Refactor source files and add berries
This commit is contained in:
1 parent
6135243641
commit
3546f3b5a7
103 files changed
+2598
-1171
No files matched your search
@@ -0,0 +1,636 @@
|
||||
import { BattleStat, getBattleStatName } from '../data/battle-stat';
|
||||
import * as Modifiers from './modifier';
|
||||
import { AttackMove, Moves, allMoves } from '../data/move';
|
||||
import { PokeballType, getPokeballName } from '../data/pokeball';
|
||||
import { PlayerPokemon, PokemonMove } from '../pokemon';
|
||||
import { EvolutionItem, pokemonEvolutions } from '../data/pokemon-evolutions';
|
||||
import { Stat, getStatName } from '../data/pokemon-stat';
|
||||
import { tmSpecies } from '../data/tms';
|
||||
import { Type } from '../data/type';
|
||||
import PartyUiHandler, { PokemonMoveSelectFilter, PokemonSelectFilter } from '../ui/party-ui-handler';
|
||||
import * as Utils from '../utils';
|
||||
import { TempBattleStat, getTempBattleStatBoosterItemName, getTempBattleStatName } from '../data/temp-battle-stat';
|
||||
import { BerryType, getBerryEffectDescription, getBerryName } from '../data/berry';
|
||||
|
||||
type Modifier = Modifiers.Modifier;
|
||||
|
||||
export enum ModifierTier {
|
||||
COMMON,
|
||||
GREAT,
|
||||
ULTRA,
|
||||
MASTER,
|
||||
LUXURY
|
||||
};
|
||||
|
||||
type NewModifierFunc = (type: ModifierType, args: any[]) => Modifier;
|
||||
|
||||
export class ModifierType {
|
||||
public name: string;
|
||||
public description: string;
|
||||
public iconImage: string;
|
||||
public group: string;
|
||||
public soundName: string;
|
||||
public tier: ModifierTier;
|
||||
private newModifierFunc: NewModifierFunc;
|
||||
|
||||
constructor(name: string, description: string, newModifierFunc: NewModifierFunc, iconImage?: string, group?: string, soundName?: string) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.iconImage = iconImage || name?.replace(/[ \-]/g, '_')?.toLowerCase();
|
||||
this.group = group || '';
|
||||
this.soundName = soundName || 'restore';
|
||||
this.newModifierFunc = newModifierFunc;
|
||||
}
|
||||
|
||||
setTier(tier: ModifierTier) {
|
||||
this.tier = tier;
|
||||
}
|
||||
|
||||
newModifier(...args: any[]) {
|
||||
return this.newModifierFunc(this, args);
|
||||
}
|
||||
}
|
||||
|
||||
class AddPokeballModifierType extends ModifierType {
|
||||
constructor(pokeballType: PokeballType, count: integer, iconImage?: string) {
|
||||
super(`${count}x ${getPokeballName(pokeballType)}`, `Receive ${getPokeballName(pokeballType)} x${count}`,
|
||||
(_type, _args) => new Modifiers.AddPokeballModifier(this, pokeballType, count), iconImage, 'pb', 'pb_bounce_1');
|
||||
}
|
||||
}
|
||||
|
||||
export class PokemonModifierType extends ModifierType {
|
||||
public selectFilter: PokemonSelectFilter;
|
||||
|
||||
constructor(name: string, description: string, newModifierFunc: NewModifierFunc, selectFilter?: PokemonSelectFilter, iconImage?: string, group?: string, soundName?: string) {
|
||||
super(name, description, newModifierFunc, iconImage, group, soundName);
|
||||
|
||||
this.selectFilter = selectFilter;
|
||||
}
|
||||
}
|
||||
|
||||
export class PokemonHeldItemModifierType extends PokemonModifierType {
|
||||
constructor(name: string, description: string, newModifierFunc: NewModifierFunc, selectFilter?: PokemonSelectFilter, iconImage?: string, group?: string, soundName?: string) {
|
||||
super(name, description, newModifierFunc, selectFilter, iconImage, group, soundName);
|
||||
}
|
||||
}
|
||||
|
||||
export class PokemonHpRestoreModifierType extends PokemonModifierType {
|
||||
protected restorePoints: integer;
|
||||
protected percent: boolean;
|
||||
|
||||
constructor(name: string, restorePoints: integer, percent?: boolean, newModifierFunc?: NewModifierFunc, selectFilter?: PokemonSelectFilter, iconImage?: string, group?: string) {
|
||||
super(name, `Restore ${restorePoints}${percent ? '%' : ''} HP for one POKéMON`,
|
||||
newModifierFunc || ((_type, args) => new Modifiers.PokemonHpRestoreModifier(this, (args[0] as PlayerPokemon).id, this.restorePoints, this.percent, false)),
|
||||
selectFilter || ((pokemon: PlayerPokemon) => {
|
||||
if (!pokemon.hp || pokemon.hp >= pokemon.getMaxHp())
|
||||
return PartyUiHandler.NoEffectMessage;
|
||||
return null;
|
||||
}), iconImage, group || 'potion');
|
||||
|
||||
this.restorePoints = restorePoints;
|
||||
this.percent = !!percent;
|
||||
}
|
||||
}
|
||||
|
||||
export class PokemonReviveModifierType extends PokemonHpRestoreModifierType {
|
||||
constructor(name: string, restorePercent: integer, iconImage?: string) {
|
||||
super(name, restorePercent, true, (_type, args) => new Modifiers.PokemonHpRestoreModifier(this, (args[0] as PlayerPokemon).id, this.restorePoints, true, true),
|
||||
((pokemon: PlayerPokemon) => {
|
||||
if (pokemon.hp)
|
||||
return PartyUiHandler.NoEffectMessage;
|
||||
return null;
|
||||
}), iconImage, 'revive');
|
||||
|
||||
this.description = `Revive one POKéMON and restore ${restorePercent}% HP`;
|
||||
this.selectFilter = (pokemon: PlayerPokemon) => {
|
||||
if (pokemon.hp)
|
||||
return PartyUiHandler.NoEffectMessage;
|
||||
return null;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class PokemonStatusHealModifierType extends PokemonModifierType {
|
||||
constructor(name: string) {
|
||||
super(name, `Heal any status ailment for one POKéMON`,
|
||||
((_type, args) => new Modifiers.PokemonStatusHealModifier(this, (args[0] as PlayerPokemon).id)),
|
||||
((pokemon: PlayerPokemon) => {
|
||||
if (!pokemon.hp || !pokemon.status)
|
||||
return PartyUiHandler.NoEffectMessage;
|
||||
return null;
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class PokemonMoveModifierType extends PokemonModifierType {
|
||||
public moveSelectFilter: PokemonMoveSelectFilter;
|
||||
|
||||
constructor(name: string, description: string, newModifierFunc: NewModifierFunc, selectFilter?: PokemonSelectFilter, moveSelectFilter?: PokemonMoveSelectFilter,
|
||||
iconImage?: string, group?: string) {
|
||||
super(name, description, newModifierFunc, selectFilter, iconImage, group);
|
||||
|
||||
this.moveSelectFilter = moveSelectFilter;
|
||||
}
|
||||
}
|
||||
|
||||
export class PokemonPpRestoreModifierType extends PokemonMoveModifierType {
|
||||
protected restorePoints: integer;
|
||||
|
||||
constructor(name: string, restorePoints: integer, iconImage?: string) {
|
||||
super(name, `Restore ${restorePoints > -1 ? restorePoints : 'all'} PP for one POKéMON move`, (_type, args) => new Modifiers.PokemonPpRestoreModifier(this, (args[0] as PlayerPokemon).id, (args[1] as integer), this.restorePoints),
|
||||
(_pokemon: PlayerPokemon) => {
|
||||
return null;
|
||||
}, (pokemonMove: PokemonMove) => {
|
||||
if (!pokemonMove.ppUsed)
|
||||
return PartyUiHandler.NoEffectMessage;
|
||||
return null;
|
||||
}, iconImage, 'ether');
|
||||
|
||||
this.restorePoints = this.restorePoints;
|
||||
}
|
||||
}
|
||||
|
||||
export class PokemonAllMovePpRestoreModifierType extends PokemonModifierType {
|
||||
protected restorePoints: integer;
|
||||
|
||||
constructor(name: string, restorePoints: integer, iconImage?: string) {
|
||||
super(name, `Restore ${restorePoints > -1 ? restorePoints : 'all'} PP for all of one POKéMON's moves`, (_type, args) => new Modifiers.PokemonAllMovePpRestoreModifier(this, (args[0] as PlayerPokemon).id, this.restorePoints),
|
||||
(pokemon: PlayerPokemon) => {
|
||||
if (!pokemon.moveset.filter(m => m.ppUsed).length)
|
||||
return PartyUiHandler.NoEffectMessage;
|
||||
return null;
|
||||
}, iconImage, 'elixir');
|
||||
|
||||
this.restorePoints = this.restorePoints;
|
||||
}
|
||||
}
|
||||
|
||||
export class TempBattleStatBoosterModifierType extends ModifierType {
|
||||
public tempBattleStat: TempBattleStat;
|
||||
|
||||
constructor(tempBattleStat: TempBattleStat) {
|
||||
super(Utils.toPokemonUpperCase(getTempBattleStatBoosterItemName(tempBattleStat)),
|
||||
`Increases the ${getTempBattleStatName(tempBattleStat)} of all party members by 1 stage for 5 battles`,
|
||||
(_type, _args) => new Modifiers.TempBattleStatBoosterModifier(this, this.tempBattleStat),
|
||||
getTempBattleStatBoosterItemName(tempBattleStat).replace(/\./g, '').replace(/[ ]/g, '_').toLowerCase());
|
||||
|
||||
this.tempBattleStat = tempBattleStat;
|
||||
}
|
||||
}
|
||||
|
||||
function getAttackTypeBoosterItemName(type: Type) {
|
||||
switch (type) {
|
||||
case Type.NORMAL:
|
||||
return 'Silk Scarf';
|
||||
case Type.FIGHTING:
|
||||
return 'Black Belt';
|
||||
case Type.FLYING:
|
||||
return 'Sharp Beak';
|
||||
case Type.POISON:
|
||||
return 'Poison Barb';
|
||||
case Type.GROUND:
|
||||
return 'Soft Sand';
|
||||
case Type.ROCK:
|
||||
return 'Hard Stone';
|
||||
case Type.BUG:
|
||||
return 'Silver Powder';
|
||||
case Type.GHOST:
|
||||
return 'Spell Tag';
|
||||
case Type.STEEL:
|
||||
return 'Metal Coat';
|
||||
case Type.FIRE:
|
||||
return 'Charcoal';
|
||||
case Type.WATER:
|
||||
return 'Mystic Water';
|
||||
case Type.GRASS:
|
||||
return 'Miracle Seed';
|
||||
case Type.ELECTRIC:
|
||||
return 'Magnet';
|
||||
case Type.PSYCHIC:
|
||||
return 'Twisted Spoon';
|
||||
case Type.ICE:
|
||||
return 'Never-Melt Ice'
|
||||
case Type.DRAGON:
|
||||
return 'Dragon Fang';
|
||||
case Type.DARK:
|
||||
return 'Black Glasses';
|
||||
case Type.FAIRY:
|
||||
return 'Clefairy Doll';
|
||||
}
|
||||
}
|
||||
|
||||
export class AttackTypeBoosterModifierType extends PokemonHeldItemModifierType {
|
||||
public moveType: Type;
|
||||
public boostPercent: integer;
|
||||
|
||||
constructor(moveType: Type, boostPercent: integer) {
|
||||
super(Utils.toPokemonUpperCase(getAttackTypeBoosterItemName(moveType)), `Inceases the power of a POKéMON's ${Type[moveType]}-type moves by 20%`,
|
||||
(_type, args) => new Modifiers.AttackTypeBoosterModifier(this, (args[0] as PlayerPokemon).id, moveType, boostPercent),
|
||||
null, `${getAttackTypeBoosterItemName(moveType).replace(/[ \-]/g, '_').toLowerCase()}`);
|
||||
|
||||
this.moveType = moveType;
|
||||
this.boostPercent = boostPercent;
|
||||
}
|
||||
}
|
||||
|
||||
export class PokemonLevelIncrementModifierType extends PokemonModifierType {
|
||||
constructor(name: string, iconImage?: string) {
|
||||
super(name, `Increase a POKéMON\'s level by 1`, (_type, args) => new Modifiers.PokemonLevelIncrementModifier(this, (args[0] as PlayerPokemon).id),
|
||||
(_pokemon: PlayerPokemon) => null, iconImage);
|
||||
}
|
||||
}
|
||||
|
||||
function getBaseStatBoosterItemName(stat: Stat) {
|
||||
switch (stat) {
|
||||
case Stat.HP:
|
||||
return 'HP-UP';
|
||||
case Stat.ATK:
|
||||
return 'PROTEIN';
|
||||
case Stat.DEF:
|
||||
return 'IRON';
|
||||
case Stat.SPATK:
|
||||
return 'CALCIUM';
|
||||
case Stat.SPDEF:
|
||||
return 'ZINC';
|
||||
case Stat.SPD:
|
||||
return 'CARBOS';
|
||||
}
|
||||
}
|
||||
|
||||
export class PokemonBaseStatBoosterModifierType extends PokemonHeldItemModifierType {
|
||||
private stat: Stat;
|
||||
|
||||
constructor(name: string, stat: Stat, _iconImage?: string) {
|
||||
super(name, `Increases the holder's base ${getStatName(stat)} by 20%` , (_type, args) => new Modifiers.PokemonBaseStatModifier(this, (args[0] as PlayerPokemon).id, this.stat));
|
||||
|
||||
this.stat = stat;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
export class ExpBoosterModifierType extends ModifierType {
|
||||
constructor(name: string, boostPercent: integer, iconImage?: string) {
|
||||
super(name, `Increases gain of EXP. Points by ${boostPercent}%`, () => new Modifiers.ExpBoosterModifier(this, boostPercent), iconImage);
|
||||
}
|
||||
}
|
||||
|
||||
export class PokemonExpBoosterModifierType extends PokemonHeldItemModifierType {
|
||||
constructor(name: string, boostPercent: integer, iconImage?: string) {
|
||||
super(name, `Increases the holder's gain of EXP. Points by ${boostPercent}%`, (_type, args) => new Modifiers.PokemonExpBoosterModifier(this, (args[0] as PlayerPokemon).id, boostPercent),
|
||||
(_pokemon: PlayerPokemon) => null, iconImage);
|
||||
}
|
||||
}
|
||||
|
||||
export class TmModifierType extends PokemonModifierType {
|
||||
public moveId: Moves;
|
||||
|
||||
constructor(moveId: Moves) {
|
||||
super(`TM${Utils.padInt(Object.keys(tmSpecies).indexOf(moveId.toString()) + 1, 3)} - ${allMoves[moveId - 1].name}`, `Teach ${allMoves[moveId - 1].name} to a POKéMON`, (_type, args) => new Modifiers.TmModifier(this, (args[0] as PlayerPokemon).id),
|
||||
(pokemon: PlayerPokemon) => {
|
||||
if (pokemon.compatibleTms.indexOf(moveId) === -1 || pokemon.moveset.filter(m => m?.moveId === moveId).length)
|
||||
return PartyUiHandler.NoEffectMessage;
|
||||
return null;
|
||||
}, `tm_${Type[allMoves[moveId - 1].type].toLowerCase()}`, 'tm');
|
||||
|
||||
this.moveId = moveId;
|
||||
}
|
||||
}
|
||||
|
||||
function getEvolutionItemName(evolutionItem: EvolutionItem) {
|
||||
switch (evolutionItem) {
|
||||
case EvolutionItem.LINKING_CORD:
|
||||
return 'Linking Cord';
|
||||
case EvolutionItem.SUN_STONE:
|
||||
return 'Sun Stone';
|
||||
case EvolutionItem.MOON_STONE:
|
||||
return 'Moon Stone';
|
||||
case EvolutionItem.LEAF_STONE:
|
||||
return 'Leaf Stone';
|
||||
case EvolutionItem.FIRE_STONE:
|
||||
return 'Fire Stone';
|
||||
case EvolutionItem.WATER_STONE:
|
||||
return 'Water Stone';
|
||||
case EvolutionItem.THUNDER_STONE:
|
||||
return 'Thunder Stone';
|
||||
case EvolutionItem.ICE_STONE:
|
||||
return 'Ice Stone';
|
||||
case EvolutionItem.DUSK_STONE:
|
||||
return 'Dusk Stone';
|
||||
case EvolutionItem.DAWN_STONE:
|
||||
return 'Dawn Stone';
|
||||
case EvolutionItem.SHINY_STONE:
|
||||
return 'Shiny Stone';
|
||||
}
|
||||
}
|
||||
|
||||
export class EvolutionItemModifierType extends PokemonModifierType {
|
||||
public evolutionItem: EvolutionItem;
|
||||
|
||||
constructor(evolutionItem: EvolutionItem) {
|
||||
super(getEvolutionItemName(evolutionItem), `Causes certain POKéMON to evolve`, (_type, args) => new Modifiers.EvolutionItemModifier(this, (args[0] as PlayerPokemon).id),
|
||||
(pokemon: PlayerPokemon) => {
|
||||
if (pokemonEvolutions.hasOwnProperty(pokemon.species.speciesId) && pokemonEvolutions[pokemon.species.speciesId].filter(e => e.item === this.evolutionItem
|
||||
&& (!e.condition || e.condition.predicate(pokemon))).length)
|
||||
return null;
|
||||
|
||||
return PartyUiHandler.NoEffectMessage;
|
||||
}, getEvolutionItemName(evolutionItem).replace(/[ \-]/g, '_').toLowerCase());
|
||||
|
||||
this.evolutionItem = evolutionItem;
|
||||
}
|
||||
}
|
||||
|
||||
class ModifierTypeGenerator extends ModifierType {
|
||||
private genTypeFunc: Function;
|
||||
|
||||
constructor(genTypeFunc: Function) {
|
||||
super(null, null, null, null);
|
||||
this.genTypeFunc = genTypeFunc;
|
||||
}
|
||||
|
||||
generateType(party: PlayerPokemon[]) {
|
||||
const ret = this.genTypeFunc(party);
|
||||
if (ret)
|
||||
ret.setTier(this.tier);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
class AttackTypeBoosterModifierTypeGenerator extends ModifierTypeGenerator {
|
||||
constructor() {
|
||||
super((party: PlayerPokemon[]) => {
|
||||
const attackMoveTypes = party.map(p => p.moveset.map(m => m.getMove()).filter(m => m instanceof AttackMove).map(m => m.type)).flat();
|
||||
const attackMoveTypeWeights = new Map<Type, integer>();
|
||||
let totalWeight = 0;
|
||||
for (let t of attackMoveTypes) {
|
||||
if (attackMoveTypeWeights.has(t)) {
|
||||
if (attackMoveTypeWeights.get(t) < 3)
|
||||
attackMoveTypeWeights.set(t, attackMoveTypeWeights.get(t) + 1);
|
||||
else
|
||||
continue;
|
||||
} else
|
||||
attackMoveTypeWeights.set(t, 1);
|
||||
totalWeight++;
|
||||
}
|
||||
|
||||
if (!totalWeight)
|
||||
return null;
|
||||
|
||||
let type: Type;
|
||||
|
||||
const randInt = Utils.randInt(totalWeight);
|
||||
let weight = 0;
|
||||
|
||||
for (let t of attackMoveTypeWeights.keys()) {
|
||||
const typeWeight = attackMoveTypeWeights.get(t);
|
||||
if (randInt <= weight + typeWeight) {
|
||||
type = t;
|
||||
break;
|
||||
}
|
||||
weight += typeWeight;
|
||||
}
|
||||
|
||||
return new AttackTypeBoosterModifierType(type, 20);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class EvolutionItemModifierTypeGenerator extends ModifierTypeGenerator {
|
||||
constructor() {
|
||||
super((party: PlayerPokemon[]) => {
|
||||
const evolutionItemPool = party.filter(p => pokemonEvolutions.hasOwnProperty(p.species.speciesId)).map(p => {
|
||||
const evolutions = pokemonEvolutions[p.species.speciesId]
|
||||
return evolutions.filter(e => e.item !== EvolutionItem.NONE && (!e.condition || e.condition.predicate(p)));
|
||||
}).flat().flatMap(e => e.item);
|
||||
|
||||
if (!evolutionItemPool.length)
|
||||
return null;
|
||||
|
||||
return new EvolutionItemModifierType(evolutionItemPool[Utils.randInt(evolutionItemPool.length)]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class WeightedModifierType {
|
||||
public modifierType: ModifierType;
|
||||
public weight: integer | Function;
|
||||
|
||||
constructor(modifierType: ModifierType, weight: integer | Function) {
|
||||
this.modifierType = modifierType;
|
||||
this.weight = weight;
|
||||
}
|
||||
|
||||
setTier(tier: ModifierTier) {
|
||||
this.modifierType.setTier(tier);
|
||||
}
|
||||
}
|
||||
|
||||
const modifierPool = {
|
||||
[ModifierTier.COMMON]: [
|
||||
new WeightedModifierType(new AddPokeballModifierType(PokeballType.POKEBALL, 5, 'pb'), 6),
|
||||
new WeightedModifierType(new PokemonLevelIncrementModifierType('RARE CANDY'), 2),
|
||||
new WeightedModifierType(new PokemonHpRestoreModifierType('POTION', 20), (party: PlayerPokemon[]) => {
|
||||
const thresholdPartyMemberCount = party.filter(p => p.getInverseHp() >= 10 || p.getHpRatio() <= 0.875).length;
|
||||
return thresholdPartyMemberCount * 3;
|
||||
}),
|
||||
new WeightedModifierType(new PokemonHpRestoreModifierType('SUPER POTION', 50), (party: PlayerPokemon[]) => {
|
||||
const thresholdPartyMemberCount = party.filter(p => p.getInverseHp() >= 25 || p.getHpRatio() <= 0.75).length;
|
||||
return thresholdPartyMemberCount;
|
||||
}),
|
||||
new WeightedModifierType(new PokemonPpRestoreModifierType('ETHER', 10), (party: PlayerPokemon[]) => {
|
||||
const thresholdPartyMemberCount = party.filter(p => p.hp && p.moveset.filter(m => (m.getMove().pp - m.ppUsed) <= 5).length).length;
|
||||
return thresholdPartyMemberCount * 3;
|
||||
}),
|
||||
new WeightedModifierType(new PokemonPpRestoreModifierType('MAX ETHER', -1), (party: PlayerPokemon[]) => {
|
||||
const thresholdPartyMemberCount = party.filter(p => p.hp && p.moveset.filter(m => (m.getMove().pp - m.ppUsed) <= 5).length).length;
|
||||
return thresholdPartyMemberCount;
|
||||
}),
|
||||
new WeightedModifierType(new ModifierTypeGenerator((party: PlayerPokemon[]) => {
|
||||
const randTempBattleStat = Utils.randInt(7) as TempBattleStat;
|
||||
return new TempBattleStatBoosterModifierType(randTempBattleStat);
|
||||
}), 4),
|
||||
new WeightedModifierType(new ModifierTypeGenerator((party: PlayerPokemon[]) => {
|
||||
const berryTypes = Utils.getEnumValues(BerryType);
|
||||
const randBerryType = berryTypes[Utils.randInt(berryTypes.length)];
|
||||
return new PokemonHeldItemModifierType(getBerryName(randBerryType), getBerryEffectDescription(randBerryType),
|
||||
(type, args) => new Modifiers.BerryModifier(type, (args[0] as PlayerPokemon).id, randBerryType),
|
||||
() => null, null, 'berry');
|
||||
}), 2)
|
||||
].map(m => { m.setTier(ModifierTier.COMMON); return m; }),
|
||||
[ModifierTier.GREAT]: [
|
||||
new WeightedModifierType(new AddPokeballModifierType(PokeballType.GREAT_BALL, 5, 'gb'), 12),
|
||||
new WeightedModifierType(new PokemonStatusHealModifierType('FULL HEAL'), (party: PlayerPokemon[]) => {
|
||||
const statusEffectPartyMemberCount = party.filter(p => p.hp && !!p.status).length;
|
||||
return statusEffectPartyMemberCount * 8;
|
||||
}),
|
||||
new WeightedModifierType(new PokemonReviveModifierType('REVIVE', 50), (party: PlayerPokemon[]) => {
|
||||
const faintedPartyMemberCount = party.filter(p => !p.hp).length;
|
||||
return faintedPartyMemberCount * 6;
|
||||
}),
|
||||
new WeightedModifierType(new PokemonReviveModifierType('MAX REVIVE', 100), (party: PlayerPokemon[]) => {
|
||||
const faintedPartyMemberCount = party.filter(p => !p.hp).length;
|
||||
return faintedPartyMemberCount * 2;
|
||||
}),
|
||||
new WeightedModifierType(new AllPokemonFullReviveModifierType('SACRED ASH'), (party: PlayerPokemon[]) => {
|
||||
return party.filter(p => !p.hp).length >= Math.ceil(party.length / 2) ? 1 : 0;
|
||||
}),
|
||||
new WeightedModifierType(new PokemonHpRestoreModifierType('HYPER POTION', 200), (party: PlayerPokemon[]) => {
|
||||
const thresholdPartyMemberCount = party.filter(p => p.getInverseHp() >= 100 || p.getHpRatio() <= 0.625).length;
|
||||
return thresholdPartyMemberCount * 2;
|
||||
}),
|
||||
new WeightedModifierType(new PokemonHpRestoreModifierType('MAX POTION', 100, true), (party: PlayerPokemon[]) => {
|
||||
const thresholdPartyMemberCount = party.filter(p => p.getInverseHp() >= 150 || p.getHpRatio() <= 0.5).length;
|
||||
return Math.ceil(thresholdPartyMemberCount / 1.5);
|
||||
}),
|
||||
new WeightedModifierType(new PokemonAllMovePpRestoreModifierType('ELIXIR', 10), (party: PlayerPokemon[]) => {
|
||||
const thresholdPartyMemberCount = party.filter(p => p.hp && p.moveset.filter(m => (m.getMove().pp - m.ppUsed) <= 5).length).length;
|
||||
return thresholdPartyMemberCount * 2;
|
||||
}),
|
||||
new WeightedModifierType(new PokemonAllMovePpRestoreModifierType('MAX ELIXIR', -1), (party: PlayerPokemon[]) => {
|
||||
const thresholdPartyMemberCount = party.filter(p => p.hp && p.moveset.filter(m => (m.getMove().pp - m.ppUsed) <= 5).length).length;
|
||||
return Math.ceil(thresholdPartyMemberCount / 1.5);
|
||||
}),
|
||||
new WeightedModifierType(new ModifierTypeGenerator((party: PlayerPokemon[]) => {
|
||||
const partyMemberCompatibleTms = party.map(p => p.compatibleTms);
|
||||
const uniqueCompatibleTms = partyMemberCompatibleTms.flat().filter((tm, i, array) => array.indexOf(tm) === i);
|
||||
if (!uniqueCompatibleTms.length)
|
||||
return null;
|
||||
const randTmIndex = Utils.randInt(uniqueCompatibleTms.length);
|
||||
return new TmModifierType(uniqueCompatibleTms[randTmIndex]);
|
||||
}), 4),
|
||||
new WeightedModifierType(new ModifierType('EXP. SHARE', 'All POKéMON in your party gain an additional 10% of a battle\'s EXP. Points', (type, _args) => new Modifiers.ExpShareModifier(type), 'exp_share'), 2),
|
||||
new WeightedModifierType(new ModifierTypeGenerator((party: PlayerPokemon[]) => {
|
||||
const randStat = Utils.randInt(6) as Stat;
|
||||
return new PokemonBaseStatBoosterModifierType(getBaseStatBoosterItemName(randStat), randStat);
|
||||
}), 4)
|
||||
].map(m => { m.setTier(ModifierTier.GREAT); return m; }),
|
||||
[ModifierTier.ULTRA]: [
|
||||
new WeightedModifierType(new AddPokeballModifierType(PokeballType.ULTRA_BALL, 5, 'ub'), 8),
|
||||
new WeightedModifierType(new EvolutionItemModifierTypeGenerator(), 12),
|
||||
new WeightedModifierType(new AttackTypeBoosterModifierTypeGenerator(), 5),
|
||||
new ModifierType('OVAL CHARM', 'For every X (no. of party members) items in a POKéMON\'s held item stack, give one to each other party member',
|
||||
(type, _args) => new Modifiers.PartyShareModifier(type), 'oval_charm'),
|
||||
new ModifierType('HEALING CHARM', 'Doubles the effectiveness of HP restoring moves and items (excludes revives)', (type, _args) => new Modifiers.HealingBoosterModifier(type, 2), 'healing_charm'),
|
||||
new WeightedModifierType(new PokemonHeldItemModifierType('SHELL BELL', 'Heals 1/8 of a POKéMON\'s dealt damage', (type, args) => new Modifiers.HitHealModifier(type, (args[0] as PlayerPokemon).id)), 2),
|
||||
new WeightedModifierType(new ExpBoosterModifierType('EXP CHARM', 25), 4),
|
||||
new WeightedModifierType(new PokemonExpBoosterModifierType('LUCKY EGG', 50), 3),
|
||||
new WeightedModifierType(new ModifierType('BERRY POUCH', 'Adds a 25% chance that a used berry will not be consumed',
|
||||
(type, _args) => new Modifiers.PreserveBerryModifier(type)), 3),
|
||||
new WeightedModifierType(new ModifierType('EXP. BALANCE', 'All EXP. Points received from battles is split among the lower leveled party members', (type, _args) => new Modifiers.ExpBalanceModifier(type), 'exp_balance'), 1)
|
||||
].map(m => { m.setTier(ModifierTier.ULTRA); return m; }),
|
||||
[ModifierTier.MASTER]: [
|
||||
new AddPokeballModifierType(PokeballType.MASTER_BALL, 1, 'mb'),
|
||||
new WeightedModifierType(new ModifierType('SHINY CHARM', 'Dramatically increases the chance of a wild POKéMON being shiny', (type, _args) => new Modifiers.ShinyRateBoosterModifier(type)), 2)
|
||||
].map(m => { m.setTier(ModifierTier.MASTER); return m; }),
|
||||
[ModifierTier.LUXURY]: [
|
||||
new ModifierType(`GOLDEN ${getPokeballName(PokeballType.POKEBALL)}`, 'Adds 1 extra item option at the end of every battle', (type, _args) => new Modifiers.ExtraModifierModifier(type), 'pb_gold', null, 'pb_bounce_1'),
|
||||
new ExpBoosterModifierType('GOLDEN EXP CHARM', 100)
|
||||
].map(m => { m.setTier(ModifierTier.LUXURY); return m; }),
|
||||
};
|
||||
|
||||
let modifierPoolThresholds = {};
|
||||
let ignoredPoolIndexes = {};
|
||||
|
||||
export function regenerateModifierPoolThresholds(party: PlayerPokemon[]) {
|
||||
ignoredPoolIndexes = {};
|
||||
modifierPoolThresholds = Object.fromEntries(new Map(Object.keys(modifierPool).map(t => {
|
||||
ignoredPoolIndexes[t] = [];
|
||||
const thresholds = new Map();
|
||||
let i = 0;
|
||||
modifierPool[t].reduce((total: integer, modifierType: ModifierType | WeightedModifierType) => {
|
||||
if (modifierType instanceof WeightedModifierType) {
|
||||
const weightedModifierType = modifierType as WeightedModifierType;
|
||||
const weight = weightedModifierType.weight instanceof Function
|
||||
? (weightedModifierType.weight as Function)(party)
|
||||
: weightedModifierType.weight as integer;
|
||||
if (weight)
|
||||
total += weight;
|
||||
else {
|
||||
ignoredPoolIndexes[t].push(i++);
|
||||
return total;
|
||||
}
|
||||
} else
|
||||
total++;
|
||||
thresholds.set(total, i++);
|
||||
return total;
|
||||
}, 0);
|
||||
return [ t, Object.fromEntries(thresholds) ]
|
||||
})));
|
||||
}
|
||||
|
||||
export function getModifierTypeOptionsForWave(waveIndex: integer, count: integer, party: PlayerPokemon[]): ModifierTypeOption[] {
|
||||
if (waveIndex % 10 === 0)
|
||||
return modifierPool[ModifierTier.LUXURY].map(m => new ModifierTypeOption(m, false));
|
||||
const options: ModifierTypeOption[] = [];
|
||||
const retryCount = Math.min(count * 5, 50);
|
||||
new Array(count).fill(0).map(() => {
|
||||
let candidate = getNewModifierTypeOption(party);
|
||||
let r = 0;
|
||||
while (options.length && ++r < retryCount && options.filter(o => o.type.name === candidate.type.name || o.type.group === candidate.type.group).length)
|
||||
candidate = getNewModifierTypeOption(party, candidate.type.tier, candidate.upgraded);
|
||||
options.push(candidate);
|
||||
});
|
||||
return options;
|
||||
}
|
||||
|
||||
function getNewModifierTypeOption(party: PlayerPokemon[], tier?: ModifierTier, upgrade?: boolean): ModifierTypeOption {
|
||||
const tierValue = Utils.randInt(256);
|
||||
if (tier === undefined) {
|
||||
const partyShinyCount = party.filter(p => p.shiny).length;
|
||||
const upgradeOdds = Math.floor(32 / Math.max((partyShinyCount * 2), 1));
|
||||
upgrade = !Utils.randInt(upgradeOdds);
|
||||
tier = (tierValue >= 52 ? ModifierTier.COMMON : tierValue >= 8 ? ModifierTier.GREAT : tierValue >= 1 ? ModifierTier.ULTRA : ModifierTier.MASTER) + (upgrade ? 1 : 0);
|
||||
}
|
||||
const thresholds = Object.keys(modifierPoolThresholds[tier]);
|
||||
const totalWeight = parseInt(thresholds[thresholds.length - 1]);
|
||||
const value = Utils.randInt(totalWeight);
|
||||
let index: integer;
|
||||
for (let t of thresholds) {
|
||||
let threshold = parseInt(t);
|
||||
if (value < threshold) {
|
||||
index = modifierPoolThresholds[tier][threshold];
|
||||
break;
|
||||
}
|
||||
}
|
||||
console.log(index, ignoredPoolIndexes[tier].filter(i => i <= index).length, ignoredPoolIndexes[tier])
|
||||
let modifierType: ModifierType | WeightedModifierType = modifierPool[tier][index];
|
||||
if (modifierType instanceof WeightedModifierType)
|
||||
modifierType = (modifierType as WeightedModifierType).modifierType;
|
||||
if (modifierType instanceof ModifierTypeGenerator) {
|
||||
modifierType = (modifierType as ModifierTypeGenerator).generateType(party);
|
||||
if (modifierType === null) {
|
||||
console.log(ModifierTier[tier], upgrade);
|
||||
return getNewModifierTypeOption(party, tier, upgrade);
|
||||
}
|
||||
}
|
||||
console.log(modifierType);
|
||||
return new ModifierTypeOption(modifierType as ModifierType, upgrade);
|
||||
}
|
||||
|
||||
export function getDefaultModifierTypeForTier(tier: ModifierTier): ModifierType {
|
||||
let modifierType: ModifierType | WeightedModifierType = modifierPool[tier][0];
|
||||
if (modifierType instanceof WeightedModifierType)
|
||||
modifierType = (modifierType as WeightedModifierType).modifierType;
|
||||
return modifierType;
|
||||
}
|
||||
|
||||
export class ModifierTypeOption {
|
||||
public type: ModifierType;
|
||||
public upgraded: boolean;
|
||||
|
||||
constructor(type: ModifierType, upgraded: boolean) {
|
||||
this.type = type;
|
||||
this.upgraded = upgraded;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,812 @@
|
||||
import * as ModifierTypes from './modifier-type';
|
||||
import { CommonAnimPhase, LearnMovePhase, LevelUpPhase, PokemonHealPhase } from "../battle-phases";
|
||||
import BattleScene from "../battle-scene";
|
||||
import { getLevelTotalExp } from "../data/exp";
|
||||
import { PokeballType } from "../data/pokeball";
|
||||
import Pokemon, { PlayerPokemon } from "../pokemon";
|
||||
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';
|
||||
import { CommonAnim } from '../data/battle-anims';
|
||||
|
||||
type ModifierType = ModifierTypes.ModifierType;
|
||||
export type ModifierPredicate = (modifier: Modifier) => boolean;
|
||||
|
||||
export class ModifierBar extends Phaser.GameObjects.Container {
|
||||
constructor(scene: BattleScene) {
|
||||
super(scene, 1, 2);
|
||||
|
||||
this.setScale(0.5);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
icon.setPosition(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class Modifier {
|
||||
public type: ModifierType;
|
||||
|
||||
constructor(type: ModifierType) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
match(_modifier: Modifier): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
shouldApply(_args: any[]): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
abstract apply(args: any[]): boolean;
|
||||
}
|
||||
|
||||
export abstract class PersistentModifier extends Modifier {
|
||||
public stackCount: integer;
|
||||
public virtualStackCount: integer;
|
||||
|
||||
constructor(type: ModifierType) {
|
||||
super(type);
|
||||
this.stackCount = 1;
|
||||
this.virtualStackCount = 0;
|
||||
}
|
||||
|
||||
add(modifiers: PersistentModifier[], virtual: boolean): boolean {
|
||||
for (let modifier of modifiers) {
|
||||
if (this.match(modifier))
|
||||
return modifier.incrementStack(virtual);
|
||||
}
|
||||
|
||||
if (virtual) {
|
||||
this.virtualStackCount += this.stackCount;
|
||||
this.stackCount = 0;
|
||||
}
|
||||
modifiers.push(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
abstract clone(): PersistentModifier;
|
||||
|
||||
incrementStack(virtual: boolean): boolean {
|
||||
if (this.getStackCount() < this.getMaxStackCount()) {
|
||||
if (!virtual)
|
||||
this.stackCount++;
|
||||
else
|
||||
this.virtualStackCount++;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
getStackCount(): integer {
|
||||
return this.stackCount + this.virtualStackCount;
|
||||
}
|
||||
|
||||
getMaxStackCount(): integer {
|
||||
return 99;
|
||||
}
|
||||
|
||||
getIcon(scene: BattleScene): Phaser.GameObjects.Container {
|
||||
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);
|
||||
|
||||
const virtualStackText = this.getIconStackText(scene, true);
|
||||
if (virtualStackText)
|
||||
container.add(virtualStackText);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
getIconStackText(scene: BattleScene, virtual?: boolean): Phaser.GameObjects.Text {
|
||||
if (this.getMaxStackCount() === 1 || (virtual && !this.virtualStackCount))
|
||||
return null;
|
||||
|
||||
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 });
|
||||
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);
|
||||
text.setStroke('#424242', 16)
|
||||
text.setOrigin(0, 0);
|
||||
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class ConsumableModifier extends Modifier {
|
||||
constructor(type: ModifierType) {
|
||||
super(type);
|
||||
}
|
||||
|
||||
add(_modifiers: Modifier[]): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
shouldApply(args: any[]): boolean {
|
||||
return super.shouldApply(args) && args.length === 1 && args[0] instanceof BattleScene;
|
||||
}
|
||||
}
|
||||
|
||||
export class AddPokeballModifier extends ConsumableModifier {
|
||||
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 {
|
||||
const pokeballCounts = (args[0] as BattleScene).pokeballCounts;
|
||||
pokeballCounts[this.pokeballType] = Math.min(pokeballCounts[this.pokeballType] + this.count, 99);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export class TempBattleStatBoosterModifier extends PersistentModifier {
|
||||
private tempBattleStat: TempBattleStat;
|
||||
private battlesLeft: integer;
|
||||
|
||||
constructor(type: ModifierTypes.TempBattleStatBoosterModifierType, tempBattleStat: TempBattleStat) {
|
||||
super(type);
|
||||
|
||||
this.tempBattleStat = tempBattleStat;
|
||||
this.battlesLeft = 5;
|
||||
}
|
||||
|
||||
clone(): TempBattleStatBoosterModifier {
|
||||
return new TempBattleStatBoosterModifier(this.type as ModifierTypes.TempBattleStatBoosterModifierType, this.tempBattleStat);
|
||||
}
|
||||
|
||||
apply(args: any[]): boolean {
|
||||
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;
|
||||
}
|
||||
|
||||
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 abstract class PokemonHeldItemModifier extends PersistentModifier {
|
||||
public pokemonId: integer;
|
||||
|
||||
constructor(type: ModifierType, pokemonId: integer) {
|
||||
super(type);
|
||||
|
||||
this.pokemonId = pokemonId;
|
||||
}
|
||||
|
||||
shouldApply(args: any[]): boolean {
|
||||
return super.shouldApply(args) && args.length && args[0] instanceof Pokemon && (this.pokemonId === -1 || (args[0] as Pokemon).id === this.pokemonId);
|
||||
}
|
||||
|
||||
getIcon(scene: BattleScene): Phaser.GameObjects.Container {
|
||||
const container = scene.add.container(0, 0);
|
||||
|
||||
const pokemon = this.getPokemon(scene);
|
||||
const pokemonIcon = scene.add.sprite(0, 8, pokemon.species.getIconAtlasKey());
|
||||
pokemonIcon.play(pokemon.getIconKey()).stop();
|
||||
pokemonIcon.setOrigin(0, 0.5);
|
||||
|
||||
container.add(pokemonIcon);
|
||||
|
||||
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);
|
||||
|
||||
const stackText = this.getIconStackText(scene);
|
||||
if (stackText)
|
||||
container.add(stackText);
|
||||
|
||||
const virtualStackText = this.getIconStackText(scene, true);
|
||||
if (virtualStackText)
|
||||
container.add(virtualStackText);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
getPokemon(scene: BattleScene) {
|
||||
return scene.getParty().find(p => p.id === this.pokemonId);
|
||||
}
|
||||
}
|
||||
|
||||
export class PokemonBaseStatModifier extends PokemonHeldItemModifier {
|
||||
protected stat: Stat;
|
||||
|
||||
constructor(type: ModifierTypes.PokemonBaseStatBoosterModifierType, pokemonId: integer, stat: Stat) {
|
||||
super(type, pokemonId);
|
||||
this.stat = stat;
|
||||
}
|
||||
|
||||
match(modifier: Modifier): boolean {
|
||||
if (modifier instanceof PokemonBaseStatModifier) {
|
||||
const pokemonStatModifier = modifier as PokemonBaseStatModifier;
|
||||
return pokemonStatModifier.pokemonId === this.pokemonId && pokemonStatModifier.stat === this.stat;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
clone(): PersistentModifier {
|
||||
return new PokemonBaseStatModifier(this.type as ModifierTypes.PokemonBaseStatBoosterModifierType, this.pokemonId, this.stat);
|
||||
}
|
||||
|
||||
shouldApply(args: any[]): boolean {
|
||||
return super.shouldApply(args) && args.length === 2 && args[1] instanceof Array<integer>;
|
||||
}
|
||||
|
||||
apply(args: any[]): boolean {
|
||||
args[1][this.stat] = Math.min(Math.floor(args[1][this.stat] * (1 + this.getStackCount() * 0.2)), 999999);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export class AttackTypeBoosterModifier extends PokemonHeldItemModifier {
|
||||
private moveType: Type;
|
||||
private boostMultiplier: number;
|
||||
|
||||
constructor(type: ModifierType, pokemonId: integer, moveType: Type, boostPercent: integer) {
|
||||
super(type, pokemonId);
|
||||
|
||||
this.moveType = moveType;
|
||||
this.boostMultiplier = boostPercent * 0.01;
|
||||
}
|
||||
|
||||
match(modifier: Modifier) {
|
||||
return modifier instanceof AttackTypeBoosterModifier;
|
||||
}
|
||||
|
||||
clone() {
|
||||
return new AttackTypeBoosterModifier(this.type, this.pokemonId, this.moveType, 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;
|
||||
}
|
||||
}
|
||||
|
||||
export class HitHealModifier extends PokemonHeldItemModifier {
|
||||
constructor(type: ModifierType, pokemonId: integer) {
|
||||
super(type, pokemonId);
|
||||
}
|
||||
|
||||
match(modifier: Modifier) {
|
||||
return modifier instanceof HitHealModifier;
|
||||
}
|
||||
|
||||
clone() {
|
||||
return new HitHealModifier(this.type, this.pokemonId);
|
||||
}
|
||||
|
||||
apply(args: any[]): boolean {
|
||||
const pokemon = args[0] as PlayerPokemon;
|
||||
|
||||
if (pokemon.turnData.damageDealt && pokemon.getHpRatio() < 1) {
|
||||
const scene = pokemon.scene;
|
||||
|
||||
scene.unshiftPhase(new PokemonHealPhase(scene, true, Math.max(Math.floor(pokemon.turnData.damageDealt / 8) * this.stackCount, 1), getPokemonMessage(pokemon, `'s ${this.type.name}\nrestored its HP a little!`), true));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export class BerryModifier extends PokemonHeldItemModifier {
|
||||
public berryType: BerryType;
|
||||
public consumed: boolean;
|
||||
|
||||
constructor(type: ModifierType, pokemonId: integer, berryType: BerryType) {
|
||||
super(type, pokemonId);
|
||||
|
||||
this.berryType = berryType;
|
||||
this.consumed = false;
|
||||
}
|
||||
|
||||
match(modifier: Modifier) {
|
||||
return modifier instanceof BerryModifier && (modifier as BerryModifier).berryType === this.berryType;
|
||||
}
|
||||
|
||||
clone() {
|
||||
return new BerryModifier(this.type, this.pokemonId, this.berryType);
|
||||
}
|
||||
|
||||
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);
|
||||
pokemon.scene.applyModifiers(PreserveBerryModifier, preserve);
|
||||
|
||||
getBerryEffectFunc(this.berryType)(pokemon);
|
||||
if (!preserve.value)
|
||||
this.consumed = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export class PreserveBerryModifier extends PersistentModifier {
|
||||
constructor(type: ModifierType) {
|
||||
super(type);
|
||||
}
|
||||
|
||||
match(modifier: Modifier) {
|
||||
return modifier instanceof PreserveBerryModifier;
|
||||
}
|
||||
|
||||
clone() {
|
||||
return new PreserveBerryModifier(this.type);
|
||||
}
|
||||
|
||||
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(): number {
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class ConsumablePokemonModifier extends ConsumableModifier {
|
||||
public pokemonId: integer;
|
||||
|
||||
constructor(type: ModifierType, pokemonId: integer) {
|
||||
super(type);
|
||||
|
||||
this.pokemonId = pokemonId;
|
||||
}
|
||||
|
||||
shouldApply(args: any[]): boolean {
|
||||
return args.length && args[0] instanceof Pokemon && (this.pokemonId === -1 || (args[0] as Pokemon).id === this.pokemonId);
|
||||
}
|
||||
|
||||
getPokemon(scene: BattleScene) {
|
||||
return scene.getParty().find(p => p.id === this.pokemonId);
|
||||
}
|
||||
}
|
||||
|
||||
export class PokemonHpRestoreModifier extends ConsumablePokemonModifier {
|
||||
private restorePoints: integer;
|
||||
private percent: boolean;
|
||||
public fainted: boolean;
|
||||
|
||||
constructor(type: ModifierType, pokemonId: integer, restorePoints: integer, percent: boolean, fainted?: boolean) {
|
||||
super(type, pokemonId);
|
||||
|
||||
this.restorePoints = restorePoints;
|
||||
this.percent = percent;
|
||||
this.fainted = !!fainted;
|
||||
}
|
||||
|
||||
shouldApply(args: any[]): boolean {
|
||||
return super.shouldApply(args) && (this.fainted || (args.length > 1 && typeof(args[1]) === 'number'));
|
||||
}
|
||||
|
||||
apply(args: any[]): boolean {
|
||||
const pokemon = args[0] as Pokemon;
|
||||
if (!pokemon.hp === this.fainted) {
|
||||
let restorePoints = this.restorePoints;
|
||||
if (!this.fainted)
|
||||
restorePoints = Math.floor(restorePoints * (args[1] as number));
|
||||
else
|
||||
pokemon.resetStatus();
|
||||
pokemon.hp = Math.min(pokemon.hp + Math.ceil((this.percent ? (restorePoints * 0.01) * pokemon.getMaxHp() : restorePoints)), pokemon.getMaxHp());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class ConsumablePokemonMoveModifier extends ConsumablePokemonModifier {
|
||||
public moveIndex: integer;
|
||||
|
||||
constructor(type: ModifierType, pokemonId: integer, moveIndex: integer) {
|
||||
super(type, pokemonId);
|
||||
|
||||
this.moveIndex = moveIndex;
|
||||
}
|
||||
}
|
||||
|
||||
export class PokemonPpRestoreModifier extends ConsumablePokemonMoveModifier {
|
||||
private restorePoints: integer;
|
||||
|
||||
constructor(type: ModifierType, pokemonId: integer, moveIndex: integer, restorePoints: integer) {
|
||||
super(type, pokemonId, moveIndex);
|
||||
|
||||
this.restorePoints = restorePoints;
|
||||
}
|
||||
|
||||
apply(args: any[]): boolean {
|
||||
const pokemon = args[0] as Pokemon;
|
||||
const move = pokemon.moveset[this.moveIndex];
|
||||
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;
|
||||
}
|
||||
|
||||
apply(args: any[]): boolean {
|
||||
const pokemon = args[0] as Pokemon;
|
||||
for (let move of pokemon.moveset)
|
||||
move.ppUsed = this.restorePoints >= -1 ? Math.max(move.ppUsed - this.restorePoints, 0) : 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export class PokemonLevelIncrementModifier extends ConsumablePokemonModifier {
|
||||
constructor(type: ModifierType, pokemonId: integer) {
|
||||
super(type, pokemonId);
|
||||
}
|
||||
|
||||
apply(args: any[]): boolean {
|
||||
const pokemon = args[0] as PlayerPokemon;
|
||||
pokemon.level++;
|
||||
if (pokemon.level <= 100) {
|
||||
pokemon.exp = getLevelTotalExp(pokemon.level, pokemon.species.growthRate);
|
||||
pokemon.levelExp = 0;
|
||||
}
|
||||
|
||||
pokemon.scene.unshiftPhase(new LevelUpPhase(pokemon.scene, pokemon.scene.getParty().indexOf(pokemon), pokemon.level - 1, pokemon.level));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export class TmModifier extends ConsumablePokemonModifier {
|
||||
constructor(type: ModifierTypes.TmModifierType, pokemonId: integer) {
|
||||
super(type, pokemonId);
|
||||
}
|
||||
|
||||
apply(args: any[]): boolean {
|
||||
const pokemon = args[0] as PlayerPokemon;
|
||||
|
||||
pokemon.scene.unshiftPhase(new LearnMovePhase(pokemon.scene, pokemon.scene.getParty().indexOf(pokemon), (this.type as ModifierTypes.TmModifierType).moveId));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
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 PartyShareModifier extends PersistentModifier {
|
||||
constructor(type: ModifierType) {
|
||||
super(type);
|
||||
}
|
||||
|
||||
match(modifier: Modifier) {
|
||||
return modifier instanceof PartyShareModifier;
|
||||
}
|
||||
|
||||
clone(): PartyShareModifier {
|
||||
return new PartyShareModifier(this.type);
|
||||
}
|
||||
|
||||
shouldApply(args: any[]): boolean {
|
||||
return super.shouldApply(args) && args.length === 2 && args[0] instanceof BattleScene && args[1] instanceof Array<Modifier>;
|
||||
}
|
||||
|
||||
apply(args: any[]): boolean {
|
||||
const scene = args[0] as BattleScene;
|
||||
const modifiers = args[1] as Modifier[];
|
||||
const party = scene.getParty();
|
||||
for (let modifier of modifiers) {
|
||||
if (modifier instanceof PokemonHeldItemModifier) {
|
||||
const heldItemModifier = modifier as PokemonHeldItemModifier;
|
||||
const extraStacks = Math.floor(modifier.stackCount / Math.max(party.length - (this.getStackCount() - 1), 1));
|
||||
for (let s = 0; s < extraStacks; s++) {
|
||||
for (let p of party) {
|
||||
if (p.id === heldItemModifier.pokemonId)
|
||||
continue;
|
||||
const newHeldItemModifier = heldItemModifier.clone() as PokemonHeldItemModifier;
|
||||
newHeldItemModifier.pokemonId = p.id;
|
||||
scene.addModifier(newHeldItemModifier, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
getMaxStackCount(): number {
|
||||
return 6;
|
||||
}
|
||||
}
|
||||
|
||||
export class HealingBoosterModifier extends PersistentModifier {
|
||||
private multiplier: number;
|
||||
|
||||
constructor(type: ModifierType, multiplier: number) {
|
||||
super(type);
|
||||
|
||||
this.multiplier = multiplier;
|
||||
}
|
||||
|
||||
match(modifier: Modifier): boolean {
|
||||
return modifier instanceof HealingBoosterModifier;
|
||||
}
|
||||
|
||||
clone(): HealingBoosterModifier {
|
||||
return new HealingBoosterModifier(this.type, this.multiplier);
|
||||
}
|
||||
|
||||
apply(args: any[]): boolean {
|
||||
const healingMultiplier = args[0] as Utils.IntegerHolder;
|
||||
for (let s = 0; s < this.getStackCount(); s++)
|
||||
healingMultiplier.value *= this.multiplier;
|
||||
healingMultiplier.value = Math.floor(healingMultiplier.value);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export class ExpBoosterModifier extends PersistentModifier {
|
||||
private boostMultiplier: integer;
|
||||
|
||||
constructor(type: ModifierType, boostPercent: integer) {
|
||||
super(type);
|
||||
|
||||
this.boostMultiplier = boostPercent * 0.01;
|
||||
}
|
||||
|
||||
match(modifier: Modifier): boolean {
|
||||
if (modifier instanceof ExpBoosterModifier) {
|
||||
const expModifier = modifier as ExpBoosterModifier;
|
||||
return expModifier.boostMultiplier === this.boostMultiplier;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
clone(): ExpBoosterModifier {
|
||||
return new ExpBoosterModifier(this.type, this.boostMultiplier * 100);
|
||||
}
|
||||
|
||||
apply(args: any[]): boolean {
|
||||
(args[0] as Utils.NumberHolder).value = Math.floor((args[0] as Utils.NumberHolder).value * (1 + (this.getStackCount() * this.boostMultiplier)));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export class PokemonExpBoosterModifier extends PokemonHeldItemModifier {
|
||||
private boostMultiplier: integer;
|
||||
|
||||
constructor(type: ModifierTypes.PokemonExpBoosterModifierType, pokemonId: integer, boostPercent: integer) {
|
||||
super(type, pokemonId);
|
||||
this.boostMultiplier = boostPercent * 0.01;
|
||||
}
|
||||
|
||||
match(modifier: Modifier): boolean {
|
||||
if (modifier instanceof PokemonExpBoosterModifier) {
|
||||
const pokemonExpModifier = modifier as PokemonExpBoosterModifier;
|
||||
return pokemonExpModifier.pokemonId === this.pokemonId && pokemonExpModifier.boostMultiplier === this.boostMultiplier;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
clone(): PersistentModifier {
|
||||
return new PokemonExpBoosterModifier(this.type as ModifierTypes.PokemonExpBoosterModifierType, this.pokemonId, 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;
|
||||
}
|
||||
}
|
||||
|
||||
export class ExpShareModifier extends PersistentModifier {
|
||||
constructor(type: ModifierType) {
|
||||
super(type);
|
||||
}
|
||||
|
||||
match(modifier: Modifier): boolean {
|
||||
return modifier instanceof ExpShareModifier;
|
||||
}
|
||||
|
||||
apply(_args: any[]): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
clone(): ExpShareModifier {
|
||||
return new ExpShareModifier(this.type);
|
||||
}
|
||||
|
||||
getMaxStackCount(): integer {
|
||||
return 5;
|
||||
}
|
||||
}
|
||||
|
||||
export class ExpBalanceModifier extends PersistentModifier {
|
||||
constructor(type: ModifierType) {
|
||||
super(type);
|
||||
}
|
||||
|
||||
match(modifier: Modifier): boolean {
|
||||
return modifier instanceof ExpBalanceModifier;
|
||||
}
|
||||
|
||||
apply(_args: any[]): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
clone(): ExpBalanceModifier {
|
||||
return new ExpBalanceModifier(this.type);
|
||||
}
|
||||
|
||||
getMaxStackCount(): integer {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
export class ShinyRateBoosterModifier extends PersistentModifier {
|
||||
constructor(type: ModifierType) {
|
||||
super(type);
|
||||
}
|
||||
|
||||
match(modifier: Modifier): boolean {
|
||||
return modifier instanceof ShinyRateBoosterModifier;
|
||||
}
|
||||
|
||||
clone(): ShinyRateBoosterModifier {
|
||||
return new ShinyRateBoosterModifier(this.type);
|
||||
}
|
||||
|
||||
apply(args: any[]): boolean {
|
||||
(args[0] as Utils.IntegerHolder).value = Math.pow((args[0] as Utils.IntegerHolder).value * 0.5, this.getStackCount() + 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
getMaxStackCount(): integer {
|
||||
return 5;
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtraModifierModifier extends PersistentModifier {
|
||||
constructor(type: ModifierType) {
|
||||
super(type);
|
||||
}
|
||||
|
||||
match(modifier: Modifier): boolean {
|
||||
return modifier instanceof ExtraModifierModifier;
|
||||
}
|
||||
|
||||
clone(): ExtraModifierModifier {
|
||||
return new ExtraModifierModifier(this.type);
|
||||
}
|
||||
|
||||
apply(args: any[]): boolean {
|
||||
(args[0] as Utils.IntegerHolder).value += this.getStackCount();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user