Merge branch 'main' of https://github.com/TempsRay/pokerogue
11
index.html
@ -8,7 +8,9 @@
|
||||
<meta property="og:title" content="PokéRogue" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:description" content="A Pokémon fangame heavily inspired by the roguelite genre. Battle endlessly while gathering stacking items, exploring many different biomes, and reaching Pokémon stats you never thought possible." />
|
||||
<meta property="og:image" content="https://pokerogue.net/logo.png" />
|
||||
<meta property="og:image" content="https://pokerogue.net/logo512.png" />
|
||||
<link rel="apple-touch-icon" href="/logo512.png" />
|
||||
<link rel="shortcut icon" type="image/png" href="/logo512.png" />
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
||||
<style type="text/css">
|
||||
@ -23,7 +25,7 @@
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" type="text/css" href="index.css" />
|
||||
<link rel="manifest" href="manifest.json" />
|
||||
<link rel="manifest" href="/manifest.webmanifest">
|
||||
<script>
|
||||
if ("serviceWorker" in navigator) {
|
||||
window.addEventListener("load", function () {
|
||||
@ -37,6 +39,11 @@
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener('beforeinstallprompt', e => {
|
||||
// Prevent invasive install prompt (users are still able to install as an app)
|
||||
e.preventDefault();
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "pokemon-rogue-battle",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "vite",
|
||||
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
BIN
public/images/items/candy.png
Normal file
After Width: | Height: | Size: 402 B |
BIN
public/images/items/candy_overlay.png
Normal file
After Width: | Height: | Size: 263 B |
BIN
public/images/items/mystery_egg.png
Normal file
After Width: | Height: | Size: 252 B |
BIN
public/images/ui/ha_capsule.png
Normal file
After Width: | Height: | Size: 297 B |
BIN
public/images/ui/legacy/ha_capsule.png
Normal file
After Width: | Height: | Size: 297 B |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 6.1 KiB |
@ -2,20 +2,13 @@
|
||||
"name": "PokéRogue",
|
||||
"short_name": "PokéRogue",
|
||||
"description": "A Pokémon fangame heavily inspired by the roguelite genre. Battle endlessly while gathering stacking items, exploring many different biomes, and reaching Pokémon stats you never thought possible.",
|
||||
"scope": "/",
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"background_color": "#8c8c8c",
|
||||
"theme_color": "#8c8c8c",
|
||||
"icons": [
|
||||
{
|
||||
"src": "./logo.png",
|
||||
"sizes": "128x128",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "./logo512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
{ "src": "logo128.png", "sizes": "128x128", "type": "image/png" },
|
||||
{ "src": "logo512.png", "sizes": "512x512", "type": "image/png" }
|
||||
]
|
||||
}
|
1
public/starter-colors.json
Normal file
@ -2,7 +2,7 @@ import Phaser, { Time } from 'phaser';
|
||||
import UI, { Mode } from './ui/ui';
|
||||
import { NextEncounterPhase, NewBiomeEncounterPhase, SelectBiomePhase, MessagePhase, TurnInitPhase, ReturnPhase, LevelCapPhase, ShowTrainerPhase, LoginPhase, MovePhase, TitlePhase, SwitchPhase } from './phases';
|
||||
import Pokemon, { PlayerPokemon, EnemyPokemon } from './field/pokemon';
|
||||
import PokemonSpecies, { PokemonSpeciesFilter, allSpecies, getPokemonSpecies, initSpecies } from './data/pokemon-species';
|
||||
import PokemonSpecies, { PokemonSpeciesFilter, allSpecies, getPokemonSpecies, initSpecies, speciesStarters } from './data/pokemon-species';
|
||||
import * as Utils from './utils';
|
||||
import { Modifier, ModifierBar, ConsumablePokemonModifier, ConsumableModifier, PokemonHpRestoreModifier, HealingBoosterModifier, PersistentModifier, PokemonHeldItemModifier, ModifierPredicate, DoubleBattleChanceBoosterModifier, FusePokemonModifier, PokemonFormChangeItemModifier, TerastallizeModifier } from './modifier/modifier';
|
||||
import { PokeballType } from './data/pokeball';
|
||||
@ -55,8 +55,8 @@ import PokemonInfoContainer from './ui/pokemon-info-container';
|
||||
import { biomeDepths } from './data/biomes';
|
||||
import { initTouchControls } from './touch-controls';
|
||||
import { UiTheme } from './enums/ui-theme';
|
||||
import CacheBustedLoaderPlugin from './plugins/cache-busted-loader-plugin';
|
||||
import { SceneBase } from './scene-base';
|
||||
import CandyBar from './ui/candy-bar';
|
||||
|
||||
export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1";
|
||||
|
||||
@ -81,6 +81,11 @@ export const startingWave = STARTING_WAVE_OVERRIDE || 1;
|
||||
const expSpriteKeys: string[] = [];
|
||||
const repeatInputDelayMillis = 250;
|
||||
|
||||
export let starterColors: StarterColors;
|
||||
interface StarterColors {
|
||||
[key: string]: [string, string]
|
||||
}
|
||||
|
||||
export enum Button {
|
||||
UP,
|
||||
DOWN,
|
||||
@ -142,6 +147,7 @@ export default class BattleScene extends SceneBase {
|
||||
public pbTrayEnemy: PokeballTray;
|
||||
public abilityBar: AbilityBar;
|
||||
public partyExpBar: PartyExpBar;
|
||||
public candyBar: CandyBar;
|
||||
public arenaBg: Phaser.GameObjects.Sprite;
|
||||
public arenaBgTransition: Phaser.GameObjects.Sprite;
|
||||
public arenaPlayer: ArenaBase;
|
||||
@ -207,8 +213,6 @@ export default class BattleScene extends SceneBase {
|
||||
this.phaseQueuePrepend = [];
|
||||
this.phaseQueuePrependSpliceIndex = -1;
|
||||
this.nextCommandPhaseQueue = [];
|
||||
|
||||
Phaser.Plugins.PluginCache.register('Loader', CacheBustedLoaderPlugin, 'load');
|
||||
}
|
||||
|
||||
loadPokemonAtlas(key: string, atlasPath: string, experimental?: boolean) {
|
||||
@ -359,6 +363,10 @@ export default class BattleScene extends SceneBase {
|
||||
this.partyExpBar.setup();
|
||||
this.fieldUI.add(this.partyExpBar);
|
||||
|
||||
this.candyBar = new CandyBar(this);
|
||||
this.candyBar.setup();
|
||||
this.fieldUI.add(this.candyBar);
|
||||
|
||||
this.waveCountText = addTextObject(this, (this.game.canvas.width / 6) - 2, 0, startingWave.toString(), TextStyle.BATTLE_INFO);
|
||||
this.waveCountText.setOrigin(1, 0);
|
||||
this.fieldUI.add(this.waveCountText);
|
||||
@ -440,7 +448,8 @@ export default class BattleScene extends SceneBase {
|
||||
Promise.all([
|
||||
Promise.all(loadPokemonAssets),
|
||||
initCommonAnims().then(() => loadCommonAnimAssets(this, true)),
|
||||
Promise.all([ Moves.TACKLE, Moves.TAIL_WHIP, Moves.FOCUS_ENERGY, Moves.STRUGGLE ].map(m => initMoveAnim(m))).then(() => loadMoveAnimAssets(this, defaultMoves, true))
|
||||
Promise.all([ Moves.TACKLE, Moves.TAIL_WHIP, Moves.FOCUS_ENERGY, Moves.STRUGGLE ].map(m => initMoveAnim(m))).then(() => loadMoveAnimAssets(this, defaultMoves, true)),
|
||||
this.initStarterColors()
|
||||
]).then(() => {
|
||||
this.pushPhase(new LoginPhase(this));
|
||||
this.pushPhase(new TitlePhase(this));
|
||||
@ -472,12 +481,53 @@ export default class BattleScene extends SceneBase {
|
||||
this.updateScoreText();
|
||||
}
|
||||
|
||||
initExpSprites(): void {
|
||||
if (expSpriteKeys.length)
|
||||
return;
|
||||
fetch('./exp_sprites.json').then(res => res.json()).then(keys => {
|
||||
if (Array.isArray(keys))
|
||||
expSpriteKeys.push(...keys);
|
||||
initExpSprites(): Promise<void> {
|
||||
return new Promise(resolve => {
|
||||
if (expSpriteKeys.length)
|
||||
return resolve();
|
||||
fetch('./exp-sprites.json').then(res => res.json()).then(keys => {
|
||||
if (Array.isArray(keys))
|
||||
expSpriteKeys.push(...keys);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
initStarterColors(): Promise<void> {
|
||||
return new Promise(resolve => {
|
||||
if (starterColors)
|
||||
return resolve();
|
||||
|
||||
fetch('./starter-colors.json').then(res => res.json()).then(sc => {
|
||||
starterColors = {};
|
||||
Object.keys(sc).forEach(key => {
|
||||
starterColors[key] = sc[key];
|
||||
});
|
||||
|
||||
/*const loadPokemonAssets: Promise<void>[] = [];
|
||||
|
||||
for (let s of Object.keys(speciesStarters)) {
|
||||
const species = getPokemonSpecies(parseInt(s));
|
||||
loadPokemonAssets.push(species.loadAssets(this, false, 0, false));
|
||||
}
|
||||
|
||||
Promise.all(loadPokemonAssets).then(() => {
|
||||
const starterCandyColors = {};
|
||||
const rgbaToHexFunc = (r, g, b) => [r, g, b].map(x => x.toString(16).padStart(2, '0')).join('');
|
||||
|
||||
for (let s of Object.keys(speciesStarters)) {
|
||||
const species = getPokemonSpecies(parseInt(s));
|
||||
|
||||
starterCandyColors[species.speciesId] = species.generateCandyColors(this).map(c => rgbaToHexFunc(c[0], c[1], c[2]));
|
||||
}
|
||||
|
||||
console.log(JSON.stringify(starterCandyColors));
|
||||
|
||||
resolve();
|
||||
});*/
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -886,6 +936,8 @@ export default class BattleScene extends SceneBase {
|
||||
case Species.TATSUGIRI:
|
||||
case Species.PALDEA_TAUROS:
|
||||
return Utils.randSeedInt(species.forms.length);
|
||||
case Species.MINIOR:
|
||||
return Utils.randSeedInt(6);
|
||||
case Species.MEOWSTIC:
|
||||
case Species.INDEEDEE:
|
||||
case Species.BASCULEGION:
|
||||
@ -1075,6 +1127,7 @@ export default class BattleScene extends SceneBase {
|
||||
this.scoreText.setY(this.moneyText.y + 10);
|
||||
const offsetY = (this.scoreText.visible ? this.scoreText : this.moneyText).y + 15;
|
||||
this.partyExpBar.setY(offsetY);
|
||||
this.candyBar.setY(offsetY + 15);
|
||||
this.ui?.achvBar.setY(this.game.canvas.height / 6 + offsetY);
|
||||
}
|
||||
|
||||
|
@ -82,6 +82,7 @@ type AbAttrCondition = (pokemon: Pokemon) => boolean;
|
||||
|
||||
type PokemonAttackCondition = (user: Pokemon, target: Pokemon, move: Move) => boolean;
|
||||
type PokemonDefendCondition = (target: Pokemon, user: Pokemon, move: Move) => boolean;
|
||||
type PokemonStatChangeCondition = (target: Pokemon, statsChanged: BattleStat[], levels: integer) => boolean;
|
||||
|
||||
export abstract class AbAttr {
|
||||
public showAbility: boolean;
|
||||
@ -385,6 +386,12 @@ export class PostDefendAbAttr extends AbAttr {
|
||||
}
|
||||
}
|
||||
|
||||
export class PostStatChangeAbAttr extends AbAttr {
|
||||
applyPostStatChange(pokemon: Pokemon, statsChanged: BattleStat[], levelChanged: integer, selfTarget: boolean, args: any[]): boolean | Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export class MoveImmunityAbAttr extends PreDefendAbAttr {
|
||||
private immuneCondition: PreDefendAbAttrCondition;
|
||||
|
||||
@ -573,6 +580,70 @@ export class PostDefendWeatherChangeAbAttr extends PostDefendAbAttr {
|
||||
}
|
||||
}
|
||||
|
||||
export class PostDefendAbilitySwapAbAttr extends PostDefendAbAttr {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
|
||||
if (move.getMove().checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.getAbility().hasAttr(UnswappableAbilityAbAttr)) {
|
||||
const tempAbilityId = attacker.getAbility().id;
|
||||
attacker.summonData.ability = pokemon.getAbility().id;
|
||||
pokemon.summonData.ability = tempAbilityId;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string {
|
||||
return getPokemonMessage(pokemon, ` swapped\nabilities with its target!`);
|
||||
}
|
||||
}
|
||||
|
||||
export class PostDefendAbilityGiveAbAttr extends PostDefendAbAttr {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
|
||||
if (move.getMove().checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.getAbility().hasAttr(UnsuppressableAbilityAbAttr) && !attacker.getAbility().hasAttr(PostDefendAbilityGiveAbAttr)) {
|
||||
attacker.summonData.ability = pokemon.getAbility().id;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string {
|
||||
return getPokemonMessage(pokemon, ` gave its target\n${abilityName}!`);
|
||||
}
|
||||
}
|
||||
|
||||
export class PostStatChangeStatChangeAbAttr extends PostStatChangeAbAttr {
|
||||
private condition: PokemonStatChangeCondition;
|
||||
private statsToChange: BattleStat[];
|
||||
private levels: integer;
|
||||
|
||||
constructor(condition: PokemonStatChangeCondition, statsToChange: BattleStat[], levels: integer) {
|
||||
super(true);
|
||||
|
||||
this.condition = condition;
|
||||
this.statsToChange = statsToChange;
|
||||
this.levels = levels;
|
||||
}
|
||||
|
||||
applyPostStatChange(pokemon: Pokemon, statsChanged: BattleStat[], levelsChanged: integer, selfTarget: boolean, args: any[]): boolean {
|
||||
if (this.condition(pokemon, statsChanged, levelsChanged) && !selfTarget) {
|
||||
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, (pokemon).getBattlerIndex(), true, this.statsToChange, this.levels));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export class PreAttackAbAttr extends AbAttr {
|
||||
applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: PokemonMove, args: any[]): boolean | Promise<boolean> {
|
||||
return false;
|
||||
@ -772,19 +843,21 @@ export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr {
|
||||
}
|
||||
}
|
||||
|
||||
export class PostAttackContactApplyStatusEffectAbAttr extends PostAttackAbAttr {
|
||||
export class PostAttackApplyStatusEffectAbAttr extends PostAttackAbAttr {
|
||||
private contactRequired: boolean;
|
||||
private chance: integer;
|
||||
private effects: StatusEffect[];
|
||||
|
||||
constructor(chance: integer, ...effects: StatusEffect[]) {
|
||||
constructor(contactRequired: boolean, chance: integer, ...effects: StatusEffect[]) {
|
||||
super();
|
||||
|
||||
this.contactRequired = contactRequired;
|
||||
this.chance = chance;
|
||||
this.effects = effects;
|
||||
}
|
||||
|
||||
applyPostAttack(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
|
||||
if (move.getMove().checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && pokemon.randSeedInt(100) < this.chance && !pokemon.status) {
|
||||
if (pokemon != attacker && (!this.contactRequired || move.getMove().checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) && pokemon.randSeedInt(100) < this.chance && !pokemon.status) {
|
||||
const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)];
|
||||
return attacker.trySetStatus(effect, true);
|
||||
}
|
||||
@ -793,6 +866,12 @@ export class PostAttackContactApplyStatusEffectAbAttr extends PostAttackAbAttr {
|
||||
}
|
||||
}
|
||||
|
||||
export class PostAttackContactApplyStatusEffectAbAttr extends PostAttackApplyStatusEffectAbAttr {
|
||||
constructor(chance: integer, ...effects: StatusEffect[]) {
|
||||
super(true, chance, ...effects);
|
||||
}
|
||||
}
|
||||
|
||||
export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr {
|
||||
private condition: PokemonDefendCondition;
|
||||
|
||||
@ -854,7 +933,7 @@ class PostVictoryStatChangeAbAttr extends PostVictoryAbAttr {
|
||||
}
|
||||
|
||||
export class PostKnockOutAbAttr extends AbAttr {
|
||||
applyPostKnockOut(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise<boolean> {
|
||||
applyPostKnockOut(pokemon: Pokemon, passive: boolean, knockedOut: Pokemon, args: any[]): boolean | Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -870,7 +949,7 @@ export class PostKnockOutStatChangeAbAttr extends PostKnockOutAbAttr {
|
||||
this.levels = levels;
|
||||
}
|
||||
|
||||
applyPostKnockOut(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise<boolean> {
|
||||
applyPostKnockOut(pokemon: Pokemon, passive: boolean, knockedOut: Pokemon, args: any[]): boolean | Promise<boolean> {
|
||||
const stat = typeof this.stat === 'function'
|
||||
? this.stat(pokemon)
|
||||
: this.stat;
|
||||
@ -880,6 +959,22 @@ export class PostKnockOutStatChangeAbAttr extends PostKnockOutAbAttr {
|
||||
}
|
||||
}
|
||||
|
||||
export class CopyFaintedAllyAbilityAbAttr extends PostKnockOutAbAttr {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
applyPostKnockOut(pokemon: Pokemon, passive: boolean, knockedOut: Pokemon, args: any[]): boolean | Promise<boolean> {
|
||||
if (pokemon.isPlayer() === knockedOut.isPlayer() && !knockedOut.getAbility().hasAttr(UncopiableAbilityAbAttr)) {
|
||||
pokemon.summonData.ability = knockedOut.getAbility().id;
|
||||
pokemon.scene.queueMessage(getPokemonMessage(knockedOut, `'s ${allAbilities[knockedOut.getAbility().id].name} was taken over!`));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export class IgnoreOpponentStatChangesAbAttr extends AbAttr {
|
||||
constructor() {
|
||||
super(false);
|
||||
@ -1043,6 +1138,27 @@ export class PostSummonFormChangeAbAttr extends PostSummonAbAttr {
|
||||
}
|
||||
}
|
||||
|
||||
export class TraceAbAttr extends PostSummonAbAttr {
|
||||
applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean {
|
||||
const targets = pokemon.getOpponents();
|
||||
let target: Pokemon;
|
||||
if (targets.length > 1)
|
||||
pokemon.scene.executeWithSeedOffset(() => target = Utils.randSeedItem(targets), pokemon.scene.currentBattle.waveIndex);
|
||||
else
|
||||
target = targets[0];
|
||||
|
||||
// Wonder Guard is normally uncopiable so has the attribute, but trace specifically can copy it
|
||||
if (target.getAbility().hasAttr(UncopiableAbilityAbAttr) && target.getAbility().id !== Abilities.WONDER_GUARD)
|
||||
return false;
|
||||
|
||||
pokemon.summonData.ability = target.getAbility().id;
|
||||
|
||||
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` traced ${target.name}'s\n${allAbilities[target.getAbility().id].name}!`));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export class PostSummonTransformAbAttr extends PostSummonAbAttr {
|
||||
constructor() {
|
||||
super(true);
|
||||
@ -1058,6 +1174,7 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr {
|
||||
|
||||
pokemon.summonData.speciesForm = target.getSpeciesForm();
|
||||
pokemon.summonData.fusionSpeciesForm = target.getFusionSpeciesForm();
|
||||
pokemon.summonData.ability = target.getAbility().id;
|
||||
pokemon.summonData.gender = target.getGender();
|
||||
pokemon.summonData.fusionGender = target.getFusionGender();
|
||||
pokemon.summonData.stats = [ pokemon.stats[Stat.HP] ].concat(target.stats.slice(1));
|
||||
@ -1205,7 +1322,7 @@ export class BlockCritAbAttr extends AbAttr {
|
||||
}
|
||||
|
||||
export class BonusCritAbAttr extends AbAttr {
|
||||
apply(pokemon: Pokemon, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
(args[0] as Utils.BooleanHolder).value = true;
|
||||
return true;
|
||||
}
|
||||
@ -1801,7 +1918,25 @@ export class MoveAbilityBypassAbAttr extends AbAttr {
|
||||
}
|
||||
}
|
||||
|
||||
export class ProtectAbilityAbAttr extends AbAttr {
|
||||
export class UncopiableAbilityAbAttr extends AbAttr {
|
||||
constructor() {
|
||||
super(false);
|
||||
}
|
||||
}
|
||||
|
||||
export class UnsuppressableAbilityAbAttr extends AbAttr {
|
||||
constructor() {
|
||||
super(false);
|
||||
}
|
||||
}
|
||||
|
||||
export class UnswappableAbilityAbAttr extends AbAttr {
|
||||
constructor() {
|
||||
super(false);
|
||||
}
|
||||
}
|
||||
|
||||
export class NoTransformAbilityAbAttr extends AbAttr {
|
||||
constructor() {
|
||||
super(false);
|
||||
}
|
||||
@ -1907,8 +2042,8 @@ export function applyPostAttackAbAttrs(attrType: { new(...args: any[]): PostAtta
|
||||
}
|
||||
|
||||
export function applyPostKnockOutAbAttrs(attrType: { new(...args: any[]): PostKnockOutAbAttr },
|
||||
pokemon: Pokemon, ...args: any[]): Promise<void> {
|
||||
return applyAbAttrsInternal<PostKnockOutAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPostKnockOut(pokemon, passive, args), args);
|
||||
pokemon: Pokemon, knockedOut: Pokemon, ...args: any[]): Promise<void> {
|
||||
return applyAbAttrsInternal<PostKnockOutAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPostKnockOut(pokemon, passive, knockedOut, args), args);
|
||||
}
|
||||
|
||||
export function applyPostVictoryAbAttrs(attrType: { new(...args: any[]): PostVictoryAbAttr },
|
||||
@ -1931,6 +2066,11 @@ export function applyPreStatChangeAbAttrs(attrType: { new(...args: any[]): PreSt
|
||||
return applyAbAttrsInternal<PreStatChangeAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPreStatChange(pokemon, passive, stat, cancelled, args), args);
|
||||
}
|
||||
|
||||
export function applyPostStatChangeAbAttrs(attrType: { new(...args: any[]): PostStatChangeAbAttr },
|
||||
pokemon: Pokemon, stats: BattleStat[], levels: integer, selfTarget: boolean, ...args: any[]): Promise<void> {
|
||||
return applyAbAttrsInternal<PostStatChangeAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPostStatChange(pokemon, stats, levels, selfTarget, args), args);
|
||||
}
|
||||
|
||||
export function applyPreSetStatusAbAttrs(attrType: { new(...args: any[]): PreSetStatusAbAttr },
|
||||
pokemon: Pokemon, effect: StatusEffect, cancelled: Utils.BooleanHolder, ...args: any[]): Promise<void> {
|
||||
const simulated = args.length > 1 && args[1];
|
||||
@ -2049,7 +2189,8 @@ export function initAbilities() {
|
||||
new Ability(Abilities.SHIELD_DUST, "Shield Dust (N)", "This Pokémon's dust blocks the additional effects of attacks taken.", 3)
|
||||
.ignorable(),
|
||||
new Ability(Abilities.OWN_TEMPO, "Own Tempo", "This Pokémon has its own tempo, and that prevents it from becoming confused.", 3)
|
||||
.attr(BattlerTagImmunityAbAttr, BattlerTagType.CONFUSED),
|
||||
.attr(BattlerTagImmunityAbAttr, BattlerTagType.CONFUSED)
|
||||
.ignorable(),
|
||||
new Ability(Abilities.SUCTION_CUPS, "Suction Cups (N)", "This Pokémon uses suction cups to stay in one spot to negate all moves and items that force switching out.", 3)
|
||||
.ignorable(),
|
||||
new Ability(Abilities.INTIMIDATE, "Intimidate", "The Pokémon intimidates opposing Pokémon upon entering battle, lowering their Attack stat.", 3)
|
||||
@ -2058,11 +2199,11 @@ export function initAbilities() {
|
||||
.attr(ArenaTrapAbAttr),
|
||||
new Ability(Abilities.ROUGH_SKIN, "Rough Skin", "This Pokémon inflicts damage with its rough skin to the attacker on contact.", 3)
|
||||
.attr(PostDefendContactDamageAbAttr, 8)
|
||||
.ignorable()
|
||||
.bypassFaint(),
|
||||
new Ability(Abilities.WONDER_GUARD, "Wonder Guard", "Its mysterious power only lets supereffective moves hit the Pokémon.", 3)
|
||||
.attr(NonSuperEffectiveImmunityAbAttr)
|
||||
.attr(ProtectAbilityAbAttr)
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr)
|
||||
.ignorable(),
|
||||
new Ability(Abilities.LEVITATE, "Levitate", "By floating in the air, the Pokémon receives full immunity to all Ground-type moves.", 3)
|
||||
.attr(TypeImmunityAbAttr, Type.GROUND, (pokemon: Pokemon) => !pokemon.getTag(BattlerTagType.IGNORE_FLYING) && !pokemon.scene.arena.getTag(ArenaTagType.GRAVITY))
|
||||
@ -2091,7 +2232,9 @@ export function initAbilities() {
|
||||
.attr(ProtectStatAbAttr, BattleStat.ACC)
|
||||
.attr(DoubleBattleChanceAbAttr)
|
||||
.ignorable(),
|
||||
new Ability(Abilities.TRACE, "Trace (N)", "When it enters a battle, the Pokémon copies an opposing Pokémon's Ability.", 3),
|
||||
new Ability(Abilities.TRACE, "Trace", "When it enters a battle, the Pokémon copies an opposing Pokémon's Ability.", 3)
|
||||
.attr(TraceAbAttr)
|
||||
.attr(UncopiableAbilityAbAttr),
|
||||
new Ability(Abilities.HUGE_POWER, "Huge Power", "Doubles the Pokémon's Attack stat.", 3)
|
||||
.attr(BattleStatMultiplierAbAttr, BattleStat.ATK, 2),
|
||||
new Ability(Abilities.POISON_POINT, "Poison Point", "Contact with the Pokémon may poison the attacker.", 3)
|
||||
@ -2145,13 +2288,14 @@ export function initAbilities() {
|
||||
.attr(PostDefendContactApplyTagChanceAbAttr, 30, BattlerTagType.INFATUATED),
|
||||
new Ability(Abilities.PLUS, "Plus (N)", "Boosts the Sp. Atk stat of the Pokémon if an ally with the Plus or Minus Ability is also in battle.", 3),
|
||||
new Ability(Abilities.MINUS, "Minus (N)", "Boosts the Sp. Atk stat of the Pokémon if an ally with the Plus or Minus Ability is also in battle.", 3),
|
||||
new Ability(Abilities.FORECAST, "Forecast (N)", "The Pokémon transforms with the weather to change its type to Water, Fire, or Ice.", 3),
|
||||
new Ability(Abilities.FORECAST, "Forecast (N)", "The Pokémon transforms with the weather to change its type to Water, Fire, or Ice.", 3)
|
||||
.attr(UncopiableAbilityAbAttr),
|
||||
new Ability(Abilities.STICKY_HOLD, "Sticky Hold", "Items held by the Pokémon are stuck fast and cannot be removed by other Pokémon.", 3)
|
||||
.attr(BlockItemTheftAbAttr)
|
||||
.bypassFaint()
|
||||
.ignorable(),
|
||||
new Ability(Abilities.SHED_SKIN, "Shed Skin", "The Pokémon may heal its own status conditions by shedding its skin.", 3)
|
||||
.conditionalAttr(pokemon => !Utils.randSeedInt(3), PostTurnResetStatusAbAttr),
|
||||
.conditionalAttr(pokemon => !Utils.randSeedInt(3), PostTurnResetStatusAbAttr),
|
||||
new Ability(Abilities.GUTS, "Guts", "It's so gutsy that having a status condition boosts the Pokémon's Attack stat.", 3)
|
||||
.attr(BypassBurnDamageReductionAbAttr)
|
||||
.conditionalAttr(pokemon => !!pokemon.status, BattleStatMultiplierAbAttr, BattleStat.ATK, 1.5),
|
||||
@ -2293,10 +2437,13 @@ export function initAbilities() {
|
||||
new Ability(Abilities.RECKLESS, "Reckless", "Powers up moves that have recoil damage.", 4)
|
||||
.attr(MovePowerBoostAbAttr, (user, target, move) => move.getAttrs(RecoilAttr).length && move.id !== Moves.STRUGGLE, 1.2),
|
||||
new Ability(Abilities.MULTITYPE, "Multitype (N)", "Changes the Pokémon's type to match the Plate or Z-Crystal it holds.", 4)
|
||||
.attr(ProtectAbilityAbAttr),
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr)
|
||||
.attr(UnsuppressableAbilityAbAttr),
|
||||
new Ability(Abilities.FLOWER_GIFT, "Flower Gift (P)", "Boosts the Attack and Sp. Def stats of itself and allies in harsh sunlight.", 4)
|
||||
.conditionalAttr(getWeatherCondition(WeatherType.SUNNY || WeatherType.HARSH_SUN), BattleStatMultiplierAbAttr, BattleStat.ATK, 1.5)
|
||||
.conditionalAttr(getWeatherCondition(WeatherType.SUNNY || WeatherType.HARSH_SUN), BattleStatMultiplierAbAttr, BattleStat.SPDEF, 1.5)
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.ignorable(),
|
||||
new Ability(Abilities.BAD_DREAMS, "Bad Dreams (N)", "Reduces the HP of sleeping opposing Pokémon.", 4),
|
||||
new Ability(Abilities.PICKPOCKET, "Pickpocket", "Steals an item from an attacker that made direct contact.", 5)
|
||||
@ -2307,7 +2454,8 @@ export function initAbilities() {
|
||||
.ignorable(),
|
||||
new Ability(Abilities.UNNERVE, "Unnerve", "Unnerves opposing Pokémon and makes them unable to eat Berries.", 5)
|
||||
.attr(PreventBerryUseAbAttr),
|
||||
new Ability(Abilities.DEFIANT, "Defiant (N)", "Boosts the Pokémon's Attack stat sharply when its stats are lowered.", 5),
|
||||
new Ability(Abilities.DEFIANT, "Defiant", "Boosts the Pokémon's Attack stat sharply when its stats are lowered.", 5)
|
||||
.attr(PostStatChangeStatChangeAbAttr, (target, statsChanged, levels) => levels < 0, [BattleStat.ATK], 2),
|
||||
new Ability(Abilities.DEFEATIST, "Defeatist", "Halves the Pokémon's Attack and Sp. Atk stats when its HP becomes half or less.", 5)
|
||||
.attr(BattleStatMultiplierAbAttr, BattleStat.ATK, 0.5)
|
||||
.attr(BattleStatMultiplierAbAttr, BattleStat.SPATK, 0.5)
|
||||
@ -2357,11 +2505,15 @@ export function initAbilities() {
|
||||
.ignorable(),
|
||||
new Ability(Abilities.ANALYTIC, "Analytic (N)", "Boosts move power when the Pokémon moves last.", 5),
|
||||
new Ability(Abilities.ILLUSION, "Illusion (N)", "Comes out disguised as the Pokémon in the party's last spot.", 5)
|
||||
.attr(ProtectAbilityAbAttr),
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr),
|
||||
new Ability(Abilities.IMPOSTER, "Imposter", "The Pokémon transforms itself into the Pokémon it's facing.", 5)
|
||||
.attr(PostSummonTransformAbAttr),
|
||||
.attr(PostSummonTransformAbAttr)
|
||||
.attr(UncopiableAbilityAbAttr),
|
||||
new Ability(Abilities.INFILTRATOR, "Infiltrator (N)", "Passes through the opposing Pokémon's barrier, substitute, and the like and strikes.", 5),
|
||||
new Ability(Abilities.MUMMY, "Mummy (N)", "Contact with the Pokémon changes the attacker's Ability to Mummy.", 5),
|
||||
new Ability(Abilities.MUMMY, "Mummy", "Contact with the Pokémon changes the attacker's Ability to Mummy.", 5)
|
||||
.attr(PostDefendAbilityGiveAbAttr)
|
||||
.bypassFaint(),
|
||||
new Ability(Abilities.MOXIE, "Moxie", "The Pokémon shows moxie, and that boosts the Attack stat after knocking out any Pokémon.", 5)
|
||||
.attr(PostVictoryStatChangeAbAttr, BattleStat.ATK, 1),
|
||||
new Ability(Abilities.JUSTIFIED, "Justified", "Being hit by a Dark-type move boosts the Attack stat of the Pokémon, for justice.", 5)
|
||||
@ -2384,9 +2536,12 @@ export function initAbilities() {
|
||||
.attr(PostDefendContactDamageAbAttr, 8)
|
||||
.bypassFaint(),
|
||||
new Ability(Abilities.ZEN_MODE, "Zen Mode", "Changes the Pokémon's shape when HP is half or less.", 5)
|
||||
.attr(PostBattleInitFormChangeAbAttr, p => p.getHpRatio() >= 0.5 ? 0 : 1)
|
||||
.attr(PostSummonFormChangeAbAttr, p => p.getHpRatio() >= 0.5 ? 0 : 1)
|
||||
.attr(PostTurnFormChangeAbAttr, p => p.getHpRatio() >= 0.5 ? 0 : 1),
|
||||
.attr(PostBattleInitFormChangeAbAttr, p => p.getHpRatio() <= 0.5 ? 1 : 0)
|
||||
.attr(PostSummonFormChangeAbAttr, p => p.getHpRatio() <= 0.5 ? 1 : 0)
|
||||
.attr(PostTurnFormChangeAbAttr, p => p.getHpRatio() <= 0.5 ? 1 : 0)
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr)
|
||||
.attr(UnsuppressableAbilityAbAttr),
|
||||
new Ability(Abilities.VICTORY_STAR, "Victory Star (N)", "Boosts the accuracy of its allies and itself.", 5),
|
||||
new Ability(Abilities.TURBOBLAZE, "Turboblaze", "Moves can be used on the target regardless of its Abilities.", 5)
|
||||
.attr(PostSummonMessageAbAttr, (pokemon: Pokemon) => getPokemonMessage(pokemon, ' is radiating a blazing aura!'))
|
||||
@ -2408,7 +2563,8 @@ export function initAbilities() {
|
||||
new Ability(Abilities.BULLETPROOF, "Bulletproof", "Protects the Pokémon from some ball and bomb moves.", 6)
|
||||
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.getMove().hasFlag(MoveFlags.BALLBOMB_MOVE))
|
||||
.ignorable(),
|
||||
new Ability(Abilities.COMPETITIVE, "Competitive (N)", "Boosts the Sp. Atk stat sharply when a stat is lowered.", 6),
|
||||
new Ability(Abilities.COMPETITIVE, "Competitive", "Boosts the Sp. Atk stat sharply when a stat is lowered.", 6)
|
||||
.attr(PostStatChangeStatChangeAbAttr, (target, statsChanged, levels) => levels < 0, [BattleStat.SPATK], 2),
|
||||
new Ability(Abilities.STRONG_JAW, "Strong Jaw", "The Pokémon's strong jaw boosts the power of its biting moves.", 6)
|
||||
.attr(MovePowerBoostAbAttr, (user, target, move) => move.hasFlag(MoveFlags.BITING_MOVE), 1.5),
|
||||
new Ability(Abilities.REFRIGERATE, "Refrigerate", "Normal-type moves become Ice-type moves. The power of those moves is boosted a little.", 6)
|
||||
@ -2416,13 +2572,16 @@ export function initAbilities() {
|
||||
new Ability(Abilities.SWEET_VEIL, "Sweet Veil (N)", "Prevents itself and ally Pokémon from falling asleep.", 6)
|
||||
.ignorable(),
|
||||
new Ability(Abilities.STANCE_CHANGE, "Stance Change", "The Pokémon changes its form to Blade Forme when it uses an attack move and changes to Shield Forme when it uses King's Shield.", 6)
|
||||
.attr(ProtectAbilityAbAttr),
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr)
|
||||
.attr(UnsuppressableAbilityAbAttr),
|
||||
new Ability(Abilities.GALE_WINGS, "Gale Wings", "Gives priority to Flying-type moves when the Pokémon's HP is full.", 6)
|
||||
.attr(IncrementMovePriorityAbAttr, (pokemon, move) => pokemon.getHpRatio() === 1 && move.type === Type.FLYING),
|
||||
new Ability(Abilities.MEGA_LAUNCHER, "Mega Launcher", "Powers up aura and pulse moves.", 6)
|
||||
.attr(MovePowerBoostAbAttr, (user, target, move) => move.hasFlag(MoveFlags.PULSE_MOVE), 1.5),
|
||||
new Ability(Abilities.GRASS_PELT, "Grass Pelt", "Boosts the Pokémon's Defense stat on Grassy Terrain.", 6)
|
||||
.conditionalAttr(getTerrainCondition(TerrainType.GRASSY), BattleStatMultiplierAbAttr, BattleStat.DEF, 1.5),
|
||||
.conditionalAttr(getTerrainCondition(TerrainType.GRASSY), BattleStatMultiplierAbAttr, BattleStat.DEF, 1.5)
|
||||
.ignorable(),
|
||||
new Ability(Abilities.SYMBIOSIS, "Symbiosis (N)", "The Pokémon passes its item to an ally that has used up an item.", 6),
|
||||
new Ability(Abilities.TOUGH_CLAWS, "Tough Claws", "Powers up moves that make direct contact.", 6)
|
||||
.attr(MovePowerBoostAbAttr, (user, target, move) => move.hasFlag(MoveFlags.MAKES_CONTACT), 1.3),
|
||||
@ -2457,8 +2616,13 @@ export function initAbilities() {
|
||||
new Ability(Abilities.WATER_COMPACTION, "Water Compaction", "Boosts the Pokémon's Defense stat sharply when hit by a Water-type move.", 7)
|
||||
.attr(PostDefendStatChangeAbAttr, (target, user, move) => move.type === Type.WATER, BattleStat.DEF, 2),
|
||||
new Ability(Abilities.MERCILESS, "Merciless (N)", "The Pokémon's attacks become critical hits if the target is poisoned.", 7),
|
||||
new Ability(Abilities.SHIELDS_DOWN, "Shields Down (N)", "When its HP becomes half or less, the Pokémon's shell breaks and it becomes aggressive.", 7)
|
||||
.attr(ProtectAbilityAbAttr),
|
||||
new Ability(Abilities.SHIELDS_DOWN, "Shields Down (P)", "When its HP becomes half or less, the Pokémon's shell breaks and it becomes aggressive.", 7)
|
||||
.attr(PostBattleInitFormChangeAbAttr, p => p.formIndex % 7 + (p.getHpRatio() <= 0.5 ? 7 : 0))
|
||||
.attr(PostSummonFormChangeAbAttr, p => p.formIndex % 7 + (p.getHpRatio() <= 0.5 ? 7 : 0))
|
||||
.attr(PostTurnFormChangeAbAttr, p => p.formIndex % 7 + (p.getHpRatio() <= 0.5 ? 7 : 0))
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr)
|
||||
.attr(UnsuppressableAbilityAbAttr),
|
||||
new Ability(Abilities.STAKEOUT, "Stakeout (N)", "Doubles the damage dealt to the target's replacement if the target switches out.", 7),
|
||||
new Ability(Abilities.WATER_BUBBLE, "Water Bubble", "Lowers the power of Fire-type moves done to the Pokémon and prevents the Pokémon from getting a burn.", 7)
|
||||
.attr(ReceivedTypeDamageMultiplierAbAttr, Type.FIRE, 0.5)
|
||||
@ -2485,17 +2649,28 @@ export function initAbilities() {
|
||||
.attr(PostBattleInitFormChangeAbAttr, p => p.level < 20 || p.getHpRatio() <= 0.25 ? 0 : 1)
|
||||
.attr(PostSummonFormChangeAbAttr, p => p.level < 20 || p.getHpRatio() <= 0.25 ? 0 : 1)
|
||||
.attr(PostTurnFormChangeAbAttr, p => p.level < 20 || p.getHpRatio() <= 0.25 ? 0 : 1)
|
||||
.attr(ProtectAbilityAbAttr),
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr)
|
||||
.attr(UnsuppressableAbilityAbAttr),
|
||||
new Ability(Abilities.DISGUISE, "Disguise (N)", "Once per battle, the shroud that covers the Pokémon can protect it from an attack.", 7)
|
||||
.ignorable()
|
||||
.attr(ProtectAbilityAbAttr),
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr)
|
||||
.attr(UnsuppressableAbilityAbAttr)
|
||||
.attr(NoTransformAbilityAbAttr)
|
||||
.ignorable(),
|
||||
new Ability(Abilities.BATTLE_BOND, "Battle Bond (N)", "Defeating an opposing Pokémon strengthens the Pokémon's bond with its Trainer, and it becomes Ash-Greninja. Water Shuriken gets more powerful.", 7)
|
||||
.attr(ProtectAbilityAbAttr),
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr)
|
||||
.attr(UnsuppressableAbilityAbAttr),
|
||||
new Ability(Abilities.POWER_CONSTRUCT, "Power Construct (N)", "Other Cells gather to aid when its HP becomes half or less. Then the Pokémon changes its form to Complete Forme.", 7)
|
||||
.attr(ProtectAbilityAbAttr),
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr)
|
||||
.attr(UnsuppressableAbilityAbAttr),
|
||||
new Ability(Abilities.CORROSION, "Corrosion (N)", "The Pokémon can poison the target even if it's a Steel or Poison type.", 7),
|
||||
new Ability(Abilities.COMATOSE, "Comatose (N)", "It's always drowsing and will never wake up. It can attack without waking up.", 7)
|
||||
.attr(ProtectAbilityAbAttr),
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr)
|
||||
.attr(UnsuppressableAbilityAbAttr),
|
||||
new Ability(Abilities.QUEENLY_MAJESTY, "Queenly Majesty (N)", "Its majesty pressures the opposing Pokémon, making it unable to attack using priority moves.", 7)
|
||||
.ignorable(),
|
||||
new Ability(Abilities.INNARDS_OUT, "Innards Out (N)", "Damages the attacker landing the finishing hit by the amount equal to its last HP.", 7),
|
||||
@ -2511,8 +2686,12 @@ export function initAbilities() {
|
||||
.attr(PostKnockOutStatChangeAbAttr, BattleStat.SPATK, 1),
|
||||
new Ability(Abilities.TANGLING_HAIR, "Tangling Hair", "Contact with the Pokémon lowers the attacker's Speed stat.", 7)
|
||||
.attr(PostDefendStatChangeAbAttr, (target, user, move) => move.hasFlag(MoveFlags.MAKES_CONTACT), BattleStat.SPD, -1, false),
|
||||
new Ability(Abilities.RECEIVER, "Receiver (N)", "The Pokémon copies the Ability of a defeated ally.", 7),
|
||||
new Ability(Abilities.POWER_OF_ALCHEMY, "Power of Alchemy (N)", "The Pokémon copies the Ability of a defeated ally.", 7),
|
||||
new Ability(Abilities.RECEIVER, "Receiver", "The Pokémon copies the Ability of a defeated ally.", 7)
|
||||
.attr(CopyFaintedAllyAbilityAbAttr)
|
||||
.attr(UncopiableAbilityAbAttr),
|
||||
new Ability(Abilities.POWER_OF_ALCHEMY, "Power of Alchemy", "The Pokémon copies the Ability of a defeated ally.", 7)
|
||||
.attr(CopyFaintedAllyAbilityAbAttr)
|
||||
.attr(UncopiableAbilityAbAttr),
|
||||
new Ability(Abilities.BEAST_BOOST, "Beast Boost", "The Pokémon boosts its most proficient stat each time it knocks out a Pokémon.", 7)
|
||||
.attr(PostVictoryStatChangeAbAttr, p => {
|
||||
const battleStats = Utils.getEnumValues(BattleStat).slice(0, -3).map(s => s as BattleStat);
|
||||
@ -2528,7 +2707,9 @@ export function initAbilities() {
|
||||
return highestBattleStatIndex;
|
||||
}, 1),
|
||||
new Ability(Abilities.RKS_SYSTEM, "RKS System (N)", "Changes the Pokémon's type to match the memory disc it holds.", 7)
|
||||
.attr(ProtectAbilityAbAttr),
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr)
|
||||
.attr(UnsuppressableAbilityAbAttr),
|
||||
new Ability(Abilities.ELECTRIC_SURGE, "Electric Surge", "Turns the ground into Electric Terrain when the Pokémon enters a battle.", 7)
|
||||
.attr(PostSummonTerrainChangeAbAttr, TerrainType.ELECTRIC)
|
||||
.attr(PostBiomeChangeTerrainChangeAbAttr, TerrainType.ELECTRIC),
|
||||
@ -2560,7 +2741,8 @@ export function initAbilities() {
|
||||
new Ability(Abilities.MIRROR_ARMOR, "Mirror Armor (N)", "Bounces back only the stat-lowering effects that the Pokémon receives.", 8)
|
||||
.ignorable(),
|
||||
new Ability(Abilities.GULP_MISSILE, "Gulp Missile (N)", "When the Pokémon uses Surf or Dive, it will come back with prey. When it takes damage, it will spit out the prey to attack.", 8)
|
||||
.attr(ProtectAbilityAbAttr),
|
||||
.attr(UnsuppressableAbilityAbAttr)
|
||||
.attr(NoTransformAbilityAbAttr),
|
||||
new Ability(Abilities.STALWART, "Stalwart (N)", "Ignores the effects of opposing Pokémon's Abilities and moves that draw in moves.", 8),
|
||||
new Ability(Abilities.STEAM_ENGINE, "Steam Engine", "Boosts the Pokémon's Speed stat drastically if hit by a Fire- or Water-type move.", 8)
|
||||
.attr(PostDefendStatChangeAbAttr, (target, user, move) => move.type === Type.FIRE || move.type === Type.WATER, BattleStat.SPD, 6),
|
||||
@ -2576,23 +2758,33 @@ export function initAbilities() {
|
||||
new Ability(Abilities.RIPEN, "Ripen", "Ripens Berries and doubles their effect.", 8)
|
||||
.attr(DoubleBerryEffectAbAttr),
|
||||
new Ability(Abilities.ICE_FACE, "Ice Face (N)", "The Pokémon's ice head can take a physical attack as a substitute, but the attack also changes the Pokémon's appearance. The ice will be restored when it hails.", 8)
|
||||
.attr(ProtectAbilityAbAttr)
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr)
|
||||
.attr(UnsuppressableAbilityAbAttr)
|
||||
.attr(NoTransformAbilityAbAttr)
|
||||
.ignorable(),
|
||||
new Ability(Abilities.POWER_SPOT, "Power Spot (N)", "Just being next to the Pokémon powers up moves.", 8),
|
||||
new Ability(Abilities.MIMICRY, "Mimicry (N)", "Changes the Pokémon's type depending on the terrain.", 8),
|
||||
new Ability(Abilities.SCREEN_CLEANER, "Screen Cleaner (N)", "When the Pokémon enters a battle, the effects of Light Screen, Reflect, and Aurora Veil are nullified for both opposing and ally Pokémon.", 8),
|
||||
new Ability(Abilities.STEELY_SPIRIT, "Steely Spirit (N)", "Powers up ally Pokémon's Steel-type moves.", 8),
|
||||
new Ability(Abilities.PERISH_BODY, "Perish Body (N)", "When hit by a move that makes direct contact, the Pokémon and the attacker will faint after three turns unless they switch out of battle.", 8),
|
||||
new Ability(Abilities.WANDERING_SPIRIT, "Wandering Spirit (N)", "The Pokémon exchanges Abilities with a Pokémon that hits it with a move that makes direct contact.", 8),
|
||||
new Ability(Abilities.WANDERING_SPIRIT, "Wandering Spirit (P)", "The Pokémon exchanges Abilities with a Pokémon that hits it with a move that makes direct contact.", 8)
|
||||
.attr(PostDefendAbilitySwapAbAttr)
|
||||
.bypassFaint(),
|
||||
new Ability(Abilities.GORILLA_TACTICS, "Gorilla Tactics (N)", "Boosts the Pokémon's Attack stat but only allows the use of the first selected move.", 8),
|
||||
new Ability(Abilities.NEUTRALIZING_GAS, "Neutralizing Gas (N)", "If the Pokémon with Neutralizing Gas is in the battle, the effects of all Pokémon's Abilities will be nullified or will not be triggered.", 8)
|
||||
.attr(ProtectAbilityAbAttr),
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr)
|
||||
.attr(NoTransformAbilityAbAttr),
|
||||
new Ability(Abilities.PASTEL_VEIL, "Pastel Veil", "Protects the Pokémon and its ally Pokémon from being poisoned.", 8)
|
||||
.attr(StatusEffectImmunityAbAttr, StatusEffect.POISON, StatusEffect.TOXIC)
|
||||
.ignorable(),
|
||||
new Ability(Abilities.HUNGER_SWITCH, "Hunger Switch", "The Pokémon changes its form, alternating between its Full Belly Mode and Hangry Mode after the end of each turn.", 8)
|
||||
.attr(PostTurnFormChangeAbAttr, p => p.getFormKey ? 0 : 1)
|
||||
.attr(PostTurnFormChangeAbAttr, p => p.getFormKey ? 1 : 0),
|
||||
.attr(PostTurnFormChangeAbAttr, p => p.getFormKey ? 1 : 0)
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr)
|
||||
.attr(NoTransformAbilityAbAttr),
|
||||
new Ability(Abilities.QUICK_DRAW, "Quick Draw (N)", "Enables the Pokémon to move first occasionally.", 8),
|
||||
new Ability(Abilities.UNSEEN_FIST, "Unseen Fist (N)", "If the Pokémon uses moves that make direct contact, it can attack the target even if the target protects itself.", 8),
|
||||
new Ability(Abilities.CURIOUS_MEDICINE, "Curious Medicine (N)", "When the Pokémon enters a battle, it scatters medicine from its shell, which removes all stat changes from allies.", 8),
|
||||
@ -2606,11 +2798,19 @@ export function initAbilities() {
|
||||
.attr(PostVictoryStatChangeAbAttr, BattleStat.SPATK, 1),
|
||||
new Ability(Abilities.AS_ONE_GLASTRIER, "As One", "This Ability combines the effects of both Calyrex's Unnerve Ability and Glastrier's Chilling Neigh Ability.", 8)
|
||||
.attr(PreventBerryUseAbAttr)
|
||||
.attr(PostVictoryStatChangeAbAttr, BattleStat.ATK, 1),
|
||||
.attr(PostVictoryStatChangeAbAttr, BattleStat.ATK, 1)
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr)
|
||||
.attr(UnsuppressableAbilityAbAttr),
|
||||
new Ability(Abilities.AS_ONE_SPECTRIER, "As One", "This Ability combines the effects of both Calyrex's Unnerve Ability and Spectrier's Grim Neigh Ability.", 8)
|
||||
.attr(PreventBerryUseAbAttr)
|
||||
.attr(PostVictoryStatChangeAbAttr, BattleStat.SPATK, 1),
|
||||
new Ability(Abilities.LINGERING_AROMA, "Lingering Aroma (N)", "Contact with the Pokémon changes the attacker's Ability to Lingering Aroma.", 9),
|
||||
.attr(PostVictoryStatChangeAbAttr, BattleStat.SPATK, 1)
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr)
|
||||
.attr(UnsuppressableAbilityAbAttr),
|
||||
new Ability(Abilities.LINGERING_AROMA, "Lingering Aroma", "Contact with the Pokémon changes the attacker's Ability to Lingering Aroma.", 9)
|
||||
.attr(PostDefendAbilityGiveAbAttr)
|
||||
.bypassFaint(),
|
||||
new Ability(Abilities.SEED_SOWER, "Seed Sower", "Turns the ground into Grassy Terrain when the Pokémon is hit by an attack.", 9)
|
||||
.attr(PostDefendTerrainChangeAbAttr, TerrainType.GRASSY),
|
||||
new Ability(Abilities.THERMAL_EXCHANGE, "Thermal Exchange (P)", "Boosts the Attack stat when the Pokémon is hit by a Fire-type move. The Pokémon also cannot be burned.", 9)
|
||||
@ -2632,37 +2832,45 @@ export function initAbilities() {
|
||||
.attr(MoveTypePowerBoostAbAttr, Type.ROCK),
|
||||
new Ability(Abilities.WIND_POWER, "Wind Power (N)", "The Pokémon becomes charged when it is hit by a wind move, boosting the power of the next Electric-type move the Pokémon uses.", 9),
|
||||
new Ability(Abilities.ZERO_TO_HERO, "Zero to Hero (N)", "The Pokémon transforms into its Hero Form when it switches out.", 9)
|
||||
.attr(ProtectAbilityAbAttr),
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr)
|
||||
.attr(UnsuppressableAbilityAbAttr)
|
||||
.attr(NoTransformAbilityAbAttr),
|
||||
new Ability(Abilities.COMMANDER, "Commander (N)", "When the Pokémon enters a battle, it goes inside the mouth of an ally Dondozo if one is on the field. The Pokémon then issues commands from there.", 9)
|
||||
.attr(ProtectAbilityAbAttr),
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr),
|
||||
new Ability(Abilities.ELECTROMORPHOSIS, "Electromorphosis (N)", "The Pokémon becomes charged when it takes damage, boosting the power of the next Electric-type move the Pokémon uses.", 9),
|
||||
new Ability(Abilities.PROTOSYNTHESIS, "Protosynthesis", "Boosts the Pokémon's most proficient stat in harsh sunlight or if the Pokémon is holding Booster Energy.", 9)
|
||||
.conditionalAttr(getWeatherCondition(WeatherType.SUNNY, WeatherType.HARSH_SUN), PostSummonAddBattlerTagAbAttr, BattlerTagType.PROTOSYNTHESIS, 0, true)
|
||||
.attr(PostWeatherChangeAddBattlerTagAttr, BattlerTagType.PROTOSYNTHESIS, 0, WeatherType.SUNNY, WeatherType.HARSH_SUN)
|
||||
.attr(ProtectAbilityAbAttr),
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr)
|
||||
.attr(NoTransformAbilityAbAttr),
|
||||
new Ability(Abilities.QUARK_DRIVE, "Quark Drive", "Boosts the Pokémon's most proficient stat on Electric Terrain or if the Pokémon is holding Booster Energy.", 9)
|
||||
.conditionalAttr(getTerrainCondition(TerrainType.ELECTRIC), PostSummonAddBattlerTagAbAttr, BattlerTagType.QUARK_DRIVE, 0, true)
|
||||
.attr(PostTerrainChangeAddBattlerTagAttr, BattlerTagType.QUARK_DRIVE, 0, TerrainType.ELECTRIC)
|
||||
.attr(ProtectAbilityAbAttr),
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr)
|
||||
.attr(NoTransformAbilityAbAttr),
|
||||
new Ability(Abilities.GOOD_AS_GOLD, "Good as Gold (P)", "A body of pure, solid gold gives the Pokémon full immunity to other Pokémon's status moves.", 9)
|
||||
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.getMove().category === MoveCategory.STATUS)
|
||||
.ignorable(),
|
||||
new Ability(Abilities.VESSEL_OF_RUIN, "Vessel of Ruin (N)", "The power of the Pokémon's ruinous vessel lowers the Sp. Atk stats of all Pokémon except itself.", 9)
|
||||
.ignorable(),
|
||||
new Ability(Abilities.SWORD_OF_RUIN, "Sword of Ruin (N)", "The power of the Pokémon's ruinous sword lowers the Defense stats of all Pokémon except itself.", 9),
|
||||
new Ability(Abilities.SWORD_OF_RUIN, "Sword of Ruin (N)", "The power of the Pokémon's ruinous sword lowers the Defense stats of all Pokémon except itself.", 9)
|
||||
.ignorable(),
|
||||
new Ability(Abilities.TABLETS_OF_RUIN, "Tablets of Ruin (N)", "The power of the Pokémon's ruinous wooden tablets lowers the Attack stats of all Pokémon except itself.", 9)
|
||||
.ignorable(),
|
||||
new Ability(Abilities.BEADS_OF_RUIN, "Beads of Ruin (N)", "The power of the Pokémon's ruinous beads lowers the Sp. Def stats of all Pokémon except itself.", 9),
|
||||
new Ability(Abilities.BEADS_OF_RUIN, "Beads of Ruin (N)", "The power of the Pokémon's ruinous beads lowers the Sp. Def stats of all Pokémon except itself.", 9)
|
||||
.ignorable(),
|
||||
new Ability(Abilities.ORICHALCUM_PULSE, "Orichalcum Pulse", "Turns the sunlight harsh when the Pokémon enters a battle. The ancient pulse thrumming through the Pokémon also boosts its Attack stat in harsh sunlight.", 9)
|
||||
.attr(PostSummonWeatherChangeAbAttr, WeatherType.SUNNY)
|
||||
.attr(PostBiomeChangeWeatherChangeAbAttr, WeatherType.SUNNY)
|
||||
.conditionalAttr(getWeatherCondition(WeatherType.SUNNY, WeatherType.HARSH_SUN), BattleStatMultiplierAbAttr, BattleStat.ATK, 4 / 3)
|
||||
.attr(ProtectAbilityAbAttr),
|
||||
.conditionalAttr(getWeatherCondition(WeatherType.SUNNY, WeatherType.HARSH_SUN), BattleStatMultiplierAbAttr, BattleStat.ATK, 4 / 3),
|
||||
new Ability(Abilities.HADRON_ENGINE, "Hadron Engine", "Turns the ground into Electric Terrain when the Pokémon enters a battle. The futuristic engine within the Pokémon also boosts its Sp. Atk stat on Electric Terrain.", 9)
|
||||
.attr(PostSummonTerrainChangeAbAttr, TerrainType.ELECTRIC)
|
||||
.attr(PostBiomeChangeTerrainChangeAbAttr, TerrainType.ELECTRIC)
|
||||
.conditionalAttr(getTerrainCondition(TerrainType.ELECTRIC), BattleStatMultiplierAbAttr, BattleStat.SPATK, 4 / 3)
|
||||
.attr(ProtectAbilityAbAttr),
|
||||
.conditionalAttr(getTerrainCondition(TerrainType.ELECTRIC), BattleStatMultiplierAbAttr, BattleStat.SPATK, 4 / 3),
|
||||
new Ability(Abilities.OPPORTUNIST, "Opportunist (N)", "If an opponent's stat is boosted, the Pokémon seizes the opportunity to boost the same stat for itself.", 9),
|
||||
new Ability(Abilities.CUD_CHEW, "Cud Chew (N)", "When the Pokémon eats a Berry, it will regurgitate that Berry at the end of the next turn and eat it one more time.", 9),
|
||||
new Ability(Abilities.SHARPNESS, "Sharpness", "Powers up slicing moves.", 9)
|
||||
@ -2680,21 +2888,43 @@ export function initAbilities() {
|
||||
.ignorable(),
|
||||
new Ability(Abilities.SUPERSWEET_SYRUP, "Supersweet Syrup (N)", "A sickly sweet scent spreads across the field the first time the Pokémon enters a battle, lowering the evasiveness of opposing Pokémon.", 9),
|
||||
new Ability(Abilities.HOSPITALITY, "Hospitality (N)", "When the Pokémon enters a battle, it showers its ally with hospitality, restoring a small amount of the ally's HP.", 9),
|
||||
new Ability(Abilities.TOXIC_CHAIN, "Toxic Chain (N)", "The power of the Pokémon's toxic chain may badly poison any target the Pokémon hits with a move.", 9),
|
||||
new Ability(Abilities.TOXIC_CHAIN, "Toxic Chain", "The power of the Pokémon's toxic chain may badly poison any target the Pokémon hits with a move.", 9)
|
||||
.attr(PostAttackApplyStatusEffectAbAttr, false, 30, StatusEffect.TOXIC),
|
||||
new Ability(Abilities.EMBODY_ASPECT_TEAL, "Embody Aspect", "The Pokémon's heart fills with memories, causing the Teal Mask to shine and the Pokémon's Speed stat to be boosted.", 9)
|
||||
.attr(PostBattleInitStatChangeAbAttr, BattleStat.SPD, 1, true),
|
||||
.attr(PostBattleInitStatChangeAbAttr, BattleStat.SPD, 1, true)
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr)
|
||||
.attr(NoTransformAbilityAbAttr),
|
||||
new Ability(Abilities.EMBODY_ASPECT_WELLSPRING, "Embody Aspect", "The Pokémon's heart fills with memories, causing the Wellspring Mask to shine and the Pokémon's Sp. Def stat to be boosted.", 9)
|
||||
.attr(PostBattleInitStatChangeAbAttr, BattleStat.SPDEF, 1, true),
|
||||
.attr(PostBattleInitStatChangeAbAttr, BattleStat.SPDEF, 1, true)
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr)
|
||||
.attr(NoTransformAbilityAbAttr),
|
||||
new Ability(Abilities.EMBODY_ASPECT_HEARTHFLAME, "Embody Aspect", "The Pokémon's heart fills with memories, causing the Hearthflame Mask to shine and the Pokémon's Attack stat to be boosted.", 9)
|
||||
.attr(PostBattleInitStatChangeAbAttr, BattleStat.ATK, 1, true),
|
||||
.attr(PostBattleInitStatChangeAbAttr, BattleStat.ATK, 1, true)
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr)
|
||||
.attr(NoTransformAbilityAbAttr),
|
||||
new Ability(Abilities.EMBODY_ASPECT_CORNERSTONE, "Embody Aspect", "The Pokémon's heart fills with memories, causing the Cornerstone Mask to shine and the Pokémon's Defense stat to be boosted.", 9)
|
||||
.attr(PostBattleInitStatChangeAbAttr, BattleStat.DEF, 1, true),
|
||||
.attr(PostBattleInitStatChangeAbAttr, BattleStat.DEF, 1, true)
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr)
|
||||
.attr(NoTransformAbilityAbAttr),
|
||||
new Ability(Abilities.TERA_SHIFT, "Tera Shift", "When the Pokémon enters a battle, it absorbs the energy around itself and transforms into its Terastal Form.", 9)
|
||||
.attr(PostSummonFormChangeAbAttr, p => p.getFormKey() ? 0 : 1),
|
||||
.attr(PostSummonFormChangeAbAttr, p => p.getFormKey() ? 0 : 1)
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr)
|
||||
.attr(UnsuppressableAbilityAbAttr)
|
||||
.attr(NoTransformAbilityAbAttr),
|
||||
new Ability(Abilities.TERA_SHELL, "Tera Shell (N)", "The Pokémon's shell contains the powers of each type. All damage-dealing moves that hit the Pokémon when its HP is full will not be very effective.", 9)
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr)
|
||||
.ignorable(),
|
||||
new Ability(Abilities.TERAFORM_ZERO, "Teraform Zero (N)", "When Terapagos changes into its Stellar Form, it uses its hidden powers to eliminate all effects of weather and terrain, reducing them to zero.", 9),
|
||||
new Ability(Abilities.TERAFORM_ZERO, "Teraform Zero (N)", "When Terapagos changes into its Stellar Form, it uses its hidden powers to eliminate all effects of weather and terrain, reducing them to zero.", 9)
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr),
|
||||
new Ability(Abilities.POISON_PUPPETEER, "Poison Puppeteer (N)", "Pokémon poisoned by Pecharunt's moves will also become confused.", 9)
|
||||
.attr(ProtectAbilityAbAttr)
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr)
|
||||
);
|
||||
}
|
||||
|
@ -799,7 +799,7 @@ export class TruantTag extends AbilityBattlerTag {
|
||||
}
|
||||
|
||||
lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
|
||||
if ((!pokemon.canApplyAbility() || pokemon.getAbility().id !== Abilities.TRUANT) && (!pokemon.canApplyAbility(true) || pokemon.getPassiveAbility().id !== Abilities.TRUANT))
|
||||
if (!pokemon.hasAbility(Abilities.TRUANT))
|
||||
return super.lapse(pokemon, lapseType);
|
||||
const passive = pokemon.getAbility().id !== Abilities.TRUANT;
|
||||
|
||||
@ -827,7 +827,7 @@ export class SlowStartTag extends AbilityBattlerTag {
|
||||
}
|
||||
|
||||
lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
|
||||
if ((!pokemon.canApplyAbility() || pokemon.getAbility().id !== this.ability) && (!pokemon.canApplyAbility(true) || pokemon.getPassiveAbility().id !== this.ability))
|
||||
if (!pokemon.hasAbility(this.ability))
|
||||
this.turnCount = 1;
|
||||
|
||||
return super.lapse(pokemon, lapseType);
|
||||
|
@ -64,6 +64,8 @@ function getDailyRunStarter(scene: BattleScene, starterSpeciesForm: PokemonSpeci
|
||||
const starter: Starter = {
|
||||
species: starterSpecies,
|
||||
dexAttr: pokemon.getDexAttr(),
|
||||
passive: false,
|
||||
variant: 0,
|
||||
nature: pokemon.getNature(),
|
||||
pokerus: pokemon.pokerus
|
||||
};
|
||||
|
393
src/data/move.ts
@ -12,8 +12,9 @@ import * as Utils from "../utils";
|
||||
import { WeatherType } from "./weather";
|
||||
import { ArenaTagSide, ArenaTrapTag } from "./arena-tag";
|
||||
import { ArenaTagType } from "./enums/arena-tag-type";
|
||||
import { ProtectAbilityAbAttr, BlockRecoilDamageAttr, BlockOneHitKOAbAttr, IgnoreContactAbAttr, MaxMultiHitAbAttr, applyAbAttrs, BlockNonDirectDamageAbAttr, applyPreSwitchOutAbAttrs, PreSwitchOutAbAttr, applyPostDefendAbAttrs, PostDefendContactApplyStatusEffectAbAttr } from "./ability";
|
||||
import { UnswappableAbilityAbAttr, UncopiableAbilityAbAttr, UnsuppressableAbilityAbAttr, NoTransformAbilityAbAttr, BlockRecoilDamageAttr, BlockOneHitKOAbAttr, IgnoreContactAbAttr, MaxMultiHitAbAttr, applyAbAttrs, BlockNonDirectDamageAbAttr, applyPreSwitchOutAbAttrs, PreSwitchOutAbAttr, applyPostDefendAbAttrs, PostDefendContactApplyStatusEffectAbAttr } from "./ability";
|
||||
import { Abilities } from "./enums/abilities";
|
||||
import { allAbilities } from './ability';
|
||||
import { PokemonHeldItemModifier } from "../modifier/modifier";
|
||||
import { BattlerIndex } from "../battle";
|
||||
import { Stat } from "./pokemon-stat";
|
||||
@ -21,6 +22,8 @@ import { TerrainType } from "./terrain";
|
||||
import { SpeciesFormChangeActiveTrigger } from "./pokemon-forms";
|
||||
import { Species } from "./enums/species";
|
||||
import { ModifierPoolType } from "#app/modifier/modifier-type";
|
||||
import { Command } from "../ui/command-ui-handler";
|
||||
import { Biome } from "./enums/biome";
|
||||
|
||||
export enum MoveCategory {
|
||||
PHYSICAL,
|
||||
@ -272,7 +275,7 @@ export default class Move {
|
||||
checkFlag(flag: MoveFlags, user: Pokemon, target: Pokemon): boolean {
|
||||
switch (flag) {
|
||||
case MoveFlags.MAKES_CONTACT:
|
||||
if ((user.canApplyAbility() && user.getAbility().hasAttr(IgnoreContactAbAttr)) || (user.canApplyAbility(true) && user.getPassiveAbility().hasAttr(IgnoreContactAbAttr)))
|
||||
if (user.hasAbilityWithAttr(IgnoreContactAbAttr))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
@ -2014,6 +2017,45 @@ export class RagingBullTypeAttr extends VariableMoveTypeAttr {
|
||||
}
|
||||
}
|
||||
|
||||
export class IvyCudgelTypeAttr extends VariableMoveTypeAttr {
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
if ([user.species.speciesId, user.fusionSpecies?.speciesId].includes(Species.OGERPON)) {
|
||||
const form = user.species.speciesId === Species.OGERPON ? user.formIndex : user.fusionSpecies.formIndex;
|
||||
const type = (args[0] as Utils.IntegerHolder);
|
||||
|
||||
switch (form) {
|
||||
case 1: // Wellspring Mask
|
||||
type.value = Type.WATER;
|
||||
break;
|
||||
case 2: // Hearthflame Mask
|
||||
type.value = Type.FIRE;
|
||||
break;
|
||||
case 3: // Cornerstone Mask
|
||||
type.value = Type.ROCK;
|
||||
break;
|
||||
case 4: // Teal Mask Tera
|
||||
type.value = Type.GRASS;
|
||||
break;
|
||||
case 5: // Wellspring Mask Tera
|
||||
type.value = Type.WATER;
|
||||
break;
|
||||
case 6: // Hearthflame Mask Tera
|
||||
type.value = Type.FIRE;
|
||||
break;
|
||||
case 7: // Cornerstone Mask Tera
|
||||
type.value = Type.ROCK;
|
||||
break;
|
||||
default:
|
||||
type.value = Type.GRASS;
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export class WeatherBallTypeAttr extends VariableMoveTypeAttr {
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
if (!user.scene.arena.weather?.isEffectSuppressed(user.scene)) {
|
||||
@ -2121,15 +2163,30 @@ export class MissEffectAttr extends MoveAttr {
|
||||
}
|
||||
}
|
||||
|
||||
const halveHpMissEffectFunc = (user: Pokemon, move: Move) => {
|
||||
export class NoEffectAttr extends MoveAttr {
|
||||
private noEffectFunc: UserMoveConditionFunc;
|
||||
|
||||
constructor(noEffectFunc: UserMoveConditionFunc) {
|
||||
super();
|
||||
|
||||
this.noEffectFunc = noEffectFunc;
|
||||
}
|
||||
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
this.noEffectFunc(user, move);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const crashDamageFunc = (user: Pokemon, move: Move) => {
|
||||
const cancelled = new Utils.BooleanHolder(false);
|
||||
applyAbAttrs(BlockNonDirectDamageAbAttr, user, cancelled);
|
||||
if (cancelled.value)
|
||||
return false;
|
||||
|
||||
const damage = user.damage(Math.floor(user.getMaxHp() / 2));
|
||||
if (damage)
|
||||
user.scene.damageNumberHandler.add(user, damage, HitResult.OTHER);
|
||||
user.damageAndUpdate(Math.floor(user.getMaxHp() / 2), HitResult.OTHER, false, true);
|
||||
user.scene.queueMessage(getPokemonMessage(user, ' kept going\nand crashed!'));
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
@ -2513,6 +2570,40 @@ export class AddArenaTrapTagAttr extends AddArenaTagAttr {
|
||||
}
|
||||
}
|
||||
|
||||
export class RemoveScreensAttr extends MoveEffectAttr {
|
||||
|
||||
private targetBothSides: boolean;
|
||||
|
||||
constructor(targetBothSides: boolean = false) {
|
||||
super(true, MoveEffectTrigger.PRE_APPLY);
|
||||
this.targetBothSides = targetBothSides;
|
||||
}
|
||||
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
|
||||
if (!super.apply(user, target, move, args))
|
||||
return false;
|
||||
|
||||
if(this.targetBothSides){
|
||||
user.scene.arena.removeTagOnSide(ArenaTagType.REFLECT, ArenaTagSide.PLAYER);
|
||||
user.scene.arena.removeTagOnSide(ArenaTagType.LIGHT_SCREEN, ArenaTagSide.PLAYER);
|
||||
user.scene.arena.removeTagOnSide(ArenaTagType.AURORA_VEIL, ArenaTagSide.PLAYER);
|
||||
|
||||
user.scene.arena.removeTagOnSide(ArenaTagType.REFLECT, ArenaTagSide.ENEMY);
|
||||
user.scene.arena.removeTagOnSide(ArenaTagType.LIGHT_SCREEN, ArenaTagSide.ENEMY);
|
||||
user.scene.arena.removeTagOnSide(ArenaTagType.AURORA_VEIL, ArenaTagSide.ENEMY);
|
||||
}
|
||||
else{
|
||||
user.scene.arena.removeTagOnSide(ArenaTagType.REFLECT, target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY);
|
||||
user.scene.arena.removeTagOnSide(ArenaTagType.LIGHT_SCREEN, target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY);
|
||||
user.scene.arena.removeTagOnSide(ArenaTagType.AURORA_VEIL, target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export class ForceSwitchOutAttr extends MoveEffectAttr {
|
||||
private user: boolean;
|
||||
private batonPass: boolean;
|
||||
@ -2728,6 +2819,100 @@ export class RandomMoveAttr extends OverrideMoveEffectAttr {
|
||||
}
|
||||
}
|
||||
|
||||
export class NaturePowerAttr extends OverrideMoveEffectAttr {
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise<boolean> {
|
||||
return new Promise(resolve => {
|
||||
var moveId;
|
||||
switch (user.scene.arena.getTerrainType()) {
|
||||
// this allows terrains to 'override' the biome move
|
||||
case TerrainType.NONE:
|
||||
switch (user.scene.arena.biomeType) {
|
||||
case Biome.TOWN:
|
||||
moveId = Moves.TRI_ATTACK;
|
||||
break;
|
||||
case Biome.PLAINS:
|
||||
case Biome.GRASS:
|
||||
case Biome.TALL_GRASS:
|
||||
case Biome.MEADOW:
|
||||
case Biome.FOREST:
|
||||
case Biome.JUNGLE:
|
||||
moveId = Moves.ENERGY_BALL;
|
||||
break;
|
||||
case Biome.SEA:
|
||||
case Biome.SWAMP:
|
||||
case Biome.BEACH:
|
||||
case Biome.LAKE:
|
||||
case Biome.SEABED:
|
||||
case Biome.ISLAND:
|
||||
moveId = Moves.HYDRO_PUMP;
|
||||
break;
|
||||
case Biome.MOUNTAIN:
|
||||
moveId = Moves.AIR_SLASH;
|
||||
break;
|
||||
case Biome.BADLANDS:
|
||||
case Biome.DESERT:
|
||||
case Biome.WASTELAND:
|
||||
case Biome.CONSTRUCTION_SITE:
|
||||
moveId = Moves.EARTH_POWER;
|
||||
break;
|
||||
case Biome.CAVE:
|
||||
moveId = Moves.POWER_GEM;
|
||||
break;
|
||||
case Biome.ICE_CAVE:
|
||||
case Biome.SNOWY_FOREST:
|
||||
moveId = Moves.ICE_BEAM;
|
||||
break;
|
||||
case Biome.VOLCANO:
|
||||
moveId = Moves.FLAMETHROWER;
|
||||
break;
|
||||
case Biome.GRAVEYARD:
|
||||
case Biome.RUINS:
|
||||
case Biome.TEMPLE:
|
||||
moveId = Moves.SHADOW_BALL;
|
||||
break;
|
||||
case Biome.DOJO:
|
||||
moveId = Moves.AURA_SPHERE;
|
||||
break;
|
||||
case Biome.FAIRY_CAVE:
|
||||
moveId = Moves.MOONBLAST;
|
||||
break;
|
||||
case Biome.ABYSS:
|
||||
case Biome.SPACE:
|
||||
moveId = Moves.DARK_PULSE;
|
||||
break;
|
||||
case Biome.END:
|
||||
moveId = Moves.ETERNABEAM;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case TerrainType.MISTY:
|
||||
moveId = Moves.MOONBLAST;
|
||||
break;
|
||||
case TerrainType.ELECTRIC:
|
||||
moveId = Moves.THUNDERBOLT;
|
||||
break;
|
||||
case TerrainType.GRASSY:
|
||||
moveId = Moves.ENERGY_BALL;
|
||||
break;
|
||||
case TerrainType.PSYCHIC:
|
||||
moveId = Moves.PSYCHIC;
|
||||
break;
|
||||
default:
|
||||
// Just in case there's no match
|
||||
moveId = Moves.TRI_ATTACK;
|
||||
break;
|
||||
}
|
||||
|
||||
user.getMoveQueue().push({ move: moveId, targets: [target.getBattlerIndex()], ignorePP: true });
|
||||
user.scene.unshiftPhase(new MovePhase(user.scene, user, [target.getBattlerIndex()], new PokemonMove(moveId, 0, 0, true), true));
|
||||
initMoveAnim(moveId).then(() => {
|
||||
loadMoveAnimAssets(user.scene, [ moveId ], true)
|
||||
.then(() => resolve(true));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const lastMoveCopiableCondition: MoveConditionFunc = (user, target, move) => {
|
||||
const copiableMove = user.scene.currentBattle.lastMove;
|
||||
|
||||
@ -2899,6 +3084,91 @@ export class SketchAttr extends MoveEffectAttr {
|
||||
}
|
||||
}
|
||||
|
||||
export class AbilityChangeAttr extends MoveEffectAttr {
|
||||
public ability: Abilities;
|
||||
|
||||
constructor(ability: Abilities, selfTarget?: boolean) {
|
||||
super(selfTarget, MoveEffectTrigger.HIT);
|
||||
|
||||
this.ability = ability;
|
||||
}
|
||||
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
if (!super.apply(user, target, move, args))
|
||||
return false;
|
||||
|
||||
(this.selfTarget ? user : target).summonData.ability = this.ability;
|
||||
|
||||
user.scene.queueMessage('The ' + getPokemonMessage((this.selfTarget ? user : target), ` acquired\n${allAbilities[this.ability].name}!`));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
getCondition(): MoveConditionFunc {
|
||||
return (user, target, move) => !(this.selfTarget ? user : target).getAbility().hasAttr(UnsuppressableAbilityAbAttr) && (this.selfTarget ? user : target).getAbility().id !== this.ability;
|
||||
}
|
||||
}
|
||||
|
||||
export class AbilityCopyAttr extends MoveEffectAttr {
|
||||
public copyToPartner: boolean;
|
||||
|
||||
constructor(copyToPartner: boolean = false) {
|
||||
super(false, MoveEffectTrigger.HIT);
|
||||
|
||||
this.copyToPartner = copyToPartner;
|
||||
}
|
||||
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
if (!super.apply(user, target, move, args))
|
||||
return false;
|
||||
|
||||
user.summonData.ability = target.getAbility().id;
|
||||
|
||||
user.scene.queueMessage(getPokemonMessage(user, ` copied the `) + getPokemonMessage(target, `'s\n${allAbilities[target.getAbility().id].name}!`));
|
||||
|
||||
if (this.copyToPartner && user.scene.currentBattle?.double && user.getAlly().hp) {
|
||||
user.getAlly().summonData.ability = target.getAbility().id;
|
||||
user.getAlly().scene.queueMessage(getPokemonMessage(user.getAlly(), ` copied the `) + getPokemonMessage(target, `'s\n${allAbilities[target.getAbility().id].name}!`));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
getCondition(): MoveConditionFunc {
|
||||
return (user, target, move) => {
|
||||
let ret = !target.getAbility().hasAttr(UncopiableAbilityAbAttr) && !user.getAbility().hasAttr(UnsuppressableAbilityAbAttr);
|
||||
if (this.copyToPartner && user.scene.currentBattle?.double)
|
||||
ret = ret && (!user.getAlly().hp || !user.getAlly().getAbility().hasAttr(UnsuppressableAbilityAbAttr));
|
||||
else
|
||||
ret = ret && user.getAbility().id !== target.getAbility().id;
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class AbilityGiveAttr extends MoveEffectAttr {
|
||||
public copyToPartner: boolean;
|
||||
|
||||
constructor() {
|
||||
super(false, MoveEffectTrigger.HIT);
|
||||
}
|
||||
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
if (!super.apply(user, target, move, args))
|
||||
return false;
|
||||
|
||||
target.summonData.ability = user.getAbility().id;
|
||||
|
||||
user.scene.queueMessage('The' + getPokemonMessage(target, `\nacquired ${allAbilities[user.getAbility().id].name}!`));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
getCondition(): MoveConditionFunc {
|
||||
return (user, target, move) => !user.getAbility().hasAttr(UncopiableAbilityAbAttr) && !target.getAbility().hasAttr(UnsuppressableAbilityAbAttr) && user.getAbility().id !== target.getAbility().id;
|
||||
}
|
||||
}
|
||||
|
||||
export class SwitchAbilitiesAttr extends MoveEffectAttr {
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
if (!super.apply(user, target, move, args))
|
||||
@ -2908,13 +3178,13 @@ export class SwitchAbilitiesAttr extends MoveEffectAttr {
|
||||
user.summonData.ability = target.getAbility().id;
|
||||
target.summonData.ability = tempAbilityId;
|
||||
|
||||
user.scene.queueMessage(getPokemonMessage(user, ` swapped\nAbilities with its target!`));
|
||||
user.scene.queueMessage(getPokemonMessage(user, ` swapped\nabilities with its target!`));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
getCondition(): MoveConditionFunc {
|
||||
return (user, target, move) => !target.getAbility().hasAttr(ProtectAbilityAbAttr);
|
||||
return (user, target, move) => !user.getAbility().hasAttr(UnswappableAbilityAbAttr) && !target.getAbility().hasAttr(UnswappableAbilityAbAttr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2926,6 +3196,7 @@ export class TransformAttr extends MoveEffectAttr {
|
||||
|
||||
user.summonData.speciesForm = target.getSpeciesForm();
|
||||
user.summonData.fusionSpeciesForm = target.getFusionSpeciesForm();
|
||||
user.summonData.ability = target.getAbility().id;
|
||||
user.summonData.gender = target.getGender();
|
||||
user.summonData.fusionGender = target.getFusionGender();
|
||||
user.summonData.stats = [ user.stats[Stat.HP] ].concat(target.stats.slice(1));
|
||||
@ -3142,7 +3413,8 @@ export function initMoves() {
|
||||
.attr(MultiHitAttr, MultiHitType._2),
|
||||
new AttackMove(Moves.MEGA_KICK, "Mega Kick", Type.NORMAL, MoveCategory.PHYSICAL, 120, 75, 5, "The target is attacked by a kick launched with muscle-packed power.", -1, 0, 1),
|
||||
new AttackMove(Moves.JUMP_KICK, "Jump Kick", Type.FIGHTING, MoveCategory.PHYSICAL, 100, 95, 10, "The user jumps up high, then strikes with a kick. If the kick misses, the user hurts itself.", -1, 0, 1)
|
||||
.attr(MissEffectAttr, halveHpMissEffectFunc)
|
||||
.attr(MissEffectAttr, crashDamageFunc)
|
||||
.attr(NoEffectAttr, crashDamageFunc)
|
||||
.condition(failOnGravityCondition),
|
||||
new AttackMove(Moves.ROLLING_KICK, "Rolling Kick", Type.FIGHTING, MoveCategory.PHYSICAL, 60, 85, 15, "The user lashes out with a quick, spinning kick. This may also make the target flinch.", 30, 0, 1)
|
||||
.attr(FlinchAttr),
|
||||
@ -3417,7 +3689,8 @@ export function initMoves() {
|
||||
.attr(HealAttr, 0.5)
|
||||
.triageMove(),
|
||||
new AttackMove(Moves.HIGH_JUMP_KICK, "High Jump Kick", Type.FIGHTING, MoveCategory.PHYSICAL, 130, 90, 10, "The target is attacked with a knee kick from a jump. If it misses, the user is hurt instead.", -1, 0, 1)
|
||||
.attr(MissEffectAttr, halveHpMissEffectFunc)
|
||||
.attr(MissEffectAttr, crashDamageFunc)
|
||||
.attr(NoEffectAttr, crashDamageFunc)
|
||||
.condition(failOnGravityCondition),
|
||||
new StatusMove(Moves.GLARE, "Glare", Type.NORMAL, 100, 30, "The user intimidates the target with the pattern on its belly to cause paralysis.", -1, 0, 1)
|
||||
.attr(StatusEffectAttr, StatusEffect.PARALYSIS),
|
||||
@ -3762,14 +4035,17 @@ export function initMoves() {
|
||||
new AttackMove(Moves.FACADE, "Facade", Type.NORMAL, MoveCategory.PHYSICAL, 70, 100, 20, "This attack move doubles its power if the user is poisoned, burned, or paralyzed.", -1, 0, 3)
|
||||
.attr(MovePowerMultiplierAttr, (user, target, move) => user.status
|
||||
&& (user.status.effect === StatusEffect.BURN || user.status.effect === StatusEffect.POISON || user.status.effect === StatusEffect.TOXIC || user.status.effect === StatusEffect.PARALYSIS) ? 2 : 1),
|
||||
new AttackMove(Moves.FOCUS_PUNCH, "Focus Punch (P)", Type.FIGHTING, MoveCategory.PHYSICAL, 150, 100, 20, "The user focuses its mind before launching a punch. This move fails if the user is hit before it is used.", -1, -3, 3)
|
||||
new AttackMove(Moves.FOCUS_PUNCH, "Focus Punch", Type.FIGHTING, MoveCategory.PHYSICAL, 150, 100, 20, "The user focuses its mind before launching a punch. This move fails if the user is hit before it is used.", -1, -3, 3)
|
||||
.punchingMove()
|
||||
.ignoresVirtual(),
|
||||
.ignoresVirtual()
|
||||
.condition((user, target, move) => !user.turnData.attacksReceived.find(r => r.damage)),
|
||||
new AttackMove(Moves.SMELLING_SALTS, "Smelling Salts", Type.NORMAL, MoveCategory.PHYSICAL, 70, 100, 10, "This attack's power is doubled when used on a target with paralysis. This also cures the target's paralysis, however.", -1, 0, 3)
|
||||
.attr(MovePowerMultiplierAttr, (user, target, move) => target.status?.effect === StatusEffect.PARALYSIS ? 2 : 1)
|
||||
.attr(HealStatusEffectAttr, true, StatusEffect.PARALYSIS),
|
||||
new SelfStatusMove(Moves.FOLLOW_ME, "Follow Me (N)", Type.NORMAL, -1, 20, "The user draws attention to itself, making all targets take aim only at the user.", -1, 2, 3),
|
||||
new StatusMove(Moves.NATURE_POWER, "Nature Power (N)", Type.NORMAL, -1, 20, "This attack makes use of nature's power. Its effects vary depending on the user's environment.", -1, 0, 3),
|
||||
new StatusMove(Moves.NATURE_POWER, "Nature Power", Type.NORMAL, -1, 20, "This attack makes use of nature's power. Its effects vary depending on the user's environment.", -1, 0, 3)
|
||||
.attr(NaturePowerAttr)
|
||||
.ignoresVirtual(),
|
||||
new SelfStatusMove(Moves.CHARGE, "Charge (P)", Type.ELECTRIC, -1, 20, "The user boosts the power of the Electric move it uses on the next turn. This also raises the user's Sp. Def stat.", -1, 0, 3)
|
||||
.attr(StatChangeAttr, BattleStat.SPDEF, 1, true),
|
||||
new StatusMove(Moves.TAUNT, "Taunt (N)", Type.DARK, 100, 20, "The target is taunted into a rage that allows it to use only attack moves for three turns.", -1, 0, 3),
|
||||
@ -3777,7 +4053,8 @@ export function initMoves() {
|
||||
.attr(AddBattlerTagAttr, BattlerTagType.HELPING_HAND)
|
||||
.target(MoveTarget.NEAR_ALLY),
|
||||
new StatusMove(Moves.TRICK, "Trick (N)", Type.PSYCHIC, 100, 10, "The user catches the target off guard and swaps its held item with its own.", -1, 0, 3),
|
||||
new StatusMove(Moves.ROLE_PLAY, "Role Play (N)", Type.PSYCHIC, -1, 10, "The user mimics the target completely, copying the target's Ability.", -1, 0, 3),
|
||||
new StatusMove(Moves.ROLE_PLAY, "Role Play", Type.PSYCHIC, -1, 10, "The user mimics the target completely, copying the target's Ability.", -1, 0, 3)
|
||||
.attr(AbilityCopyAttr),
|
||||
new SelfStatusMove(Moves.WISH, "Wish (N)", Type.NORMAL, -1, 10, "One turn after this move is used, the user's or its replacement's HP is restored by half the user's max HP.", -1, 0, 3)
|
||||
.triageMove(),
|
||||
new SelfStatusMove(Moves.ASSIST, "Assist", Type.NORMAL, -1, 20, "The user hurriedly and randomly uses a move among those known by ally Pokémon.", -1, 0, 3)
|
||||
@ -3789,8 +4066,10 @@ export function initMoves() {
|
||||
.attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.DEF ], -1, true),
|
||||
new SelfStatusMove(Moves.MAGIC_COAT, "Magic Coat (N)", Type.PSYCHIC, -1, 15, "Moves like Leech Seed and moves that inflict status conditions are blocked by a barrier and reflected back to the user of those moves.", -1, 4, 3),
|
||||
new SelfStatusMove(Moves.RECYCLE, "Recycle (N)", Type.NORMAL, -1, 10, "The user recycles a held item that has been used in battle so it can be used again.", -1, 0, 3),
|
||||
new AttackMove(Moves.REVENGE, "Revenge (P)", Type.FIGHTING, MoveCategory.PHYSICAL, 60, 100, 10, "This attack move's power is doubled if the user has been hurt by the opponent in the same turn.", -1, -4, 3),
|
||||
new AttackMove(Moves.BRICK_BREAK, "Brick Break (P)", Type.FIGHTING, MoveCategory.PHYSICAL, 75, 100, 15, "The user attacks with a swift chop. It can also break barriers, such as Light Screen and Reflect.", -1, 0, 3),
|
||||
new AttackMove(Moves.REVENGE, "Revenge", Type.FIGHTING, MoveCategory.PHYSICAL, 60, 100, 10, "This attack move's power is doubled if the user has been hurt by the opponent in the same turn.", -1, -4, 3)
|
||||
.attr(TurnDamagedDoublePowerAttr),
|
||||
new AttackMove(Moves.BRICK_BREAK, "Brick Break", Type.FIGHTING, MoveCategory.PHYSICAL, 75, 100, 15, "The user attacks with a swift chop. It can also break barriers, such as Light Screen and Reflect.", -1, 0, 3)
|
||||
.attr(RemoveScreensAttr),
|
||||
new StatusMove(Moves.YAWN, "Yawn", Type.NORMAL, -1, 10, "The user lets loose a huge yawn that lulls the target into falling asleep on the next turn.", -1, 0, 3)
|
||||
.attr(AddBattlerTagAttr, BattlerTagType.DROWSY, false, true)
|
||||
.condition((user, target, move) => !target.status),
|
||||
@ -4059,8 +4338,10 @@ export function initMoves() {
|
||||
});
|
||||
return uniqueUsedMoveIds.size >= movesetMoveIds.length - 1;
|
||||
}),
|
||||
new StatusMove(Moves.WORRY_SEED, "Worry Seed (N)", Type.GRASS, 100, 10, "A seed that causes worry is planted on the target. It prevents sleep by making the target's Ability Insomnia.", -1, 0, 4),
|
||||
new AttackMove(Moves.SUCKER_PUNCH, "Sucker Punch (P)", Type.DARK, MoveCategory.PHYSICAL, 70, 100, 5, "This move enables the user to attack first. This move fails if the target is not readying an attack.", -1, 1, 4),
|
||||
new StatusMove(Moves.WORRY_SEED, "Worry Seed", Type.GRASS, 100, 10, "A seed that causes worry is planted on the target. It prevents sleep by making the target's Ability Insomnia.", -1, 0, 4)
|
||||
.attr(AbilityChangeAttr, Abilities.INSOMNIA),
|
||||
new AttackMove(Moves.SUCKER_PUNCH, "Sucker Punch", Type.DARK, MoveCategory.PHYSICAL, 70, 100, 5, "This move enables the user to attack first. This move fails if the target is not readying an attack.", -1, 1, 4)
|
||||
.condition((user, target, move) => user.scene.currentBattle.turnCommands[target.getBattlerIndex()].command === Command.FIGHT && !target.turnData.acted && allMoves[user.scene.currentBattle.turnCommands[target.getBattlerIndex()].move.move].category !== MoveCategory.STATUS),
|
||||
new StatusMove(Moves.TOXIC_SPIKES, "Toxic Spikes", Type.POISON, -1, 20, "The user lays a trap of poison spikes at the feet of the opposing team. The spikes will poison opposing Pokémon that switch into battle.", -1, 0, 4)
|
||||
.attr(AddArenaTrapTagAttr, ArenaTagType.TOXIC_SPIKES)
|
||||
.target(MoveTarget.ENEMY_SIDE),
|
||||
@ -4164,7 +4445,8 @@ export function initMoves() {
|
||||
new StatusMove(Moves.DEFOG, "Defog", Type.FLYING, -1, 15, "A strong wind blows away the target's barriers such as Reflect or Light Screen. This also lowers the target's evasiveness.", -1, 0, 4)
|
||||
.attr(StatChangeAttr, BattleStat.EVA, -1)
|
||||
.attr(ClearWeatherAttr, WeatherType.FOG)
|
||||
.attr(ClearTerrainAttr),
|
||||
.attr(ClearTerrainAttr)
|
||||
.attr(RemoveScreensAttr, true),
|
||||
new StatusMove(Moves.TRICK_ROOM, "Trick Room", Type.PSYCHIC, -1, 5, "The user creates a bizarre area in which slower Pokémon get to move first for five turns.", -1, -7, 4)
|
||||
.attr(AddArenaTagAttr, ArenaTagType.TRICK_ROOM, 5)
|
||||
.ignoresProtect()
|
||||
@ -4308,8 +4590,10 @@ export function initMoves() {
|
||||
.ballBombMove(),
|
||||
new AttackMove(Moves.FOUL_PLAY, "Foul Play", Type.DARK, MoveCategory.PHYSICAL, 95, 100, 15, "The user turns the target's power against it. The higher the target's Attack stat, the greater the damage it deals.", -1, 0, 5)
|
||||
.attr(TargetAtkUserAtkAttr),
|
||||
new StatusMove(Moves.SIMPLE_BEAM, "Simple Beam (N)", Type.NORMAL, 100, 15, "The user's mysterious psychic wave changes the target's Ability to Simple.", -1, 0, 5),
|
||||
new StatusMove(Moves.ENTRAINMENT, "Entrainment (N)", Type.NORMAL, 100, 15, "The user dances with an odd rhythm that compels the target to mimic it, making the target's Ability the same as the user's.", -1, 0, 5),
|
||||
new StatusMove(Moves.SIMPLE_BEAM, "Simple Beam", Type.NORMAL, 100, 15, "The user's mysterious psychic wave changes the target's Ability to Simple.", -1, 0, 5)
|
||||
.attr(AbilityChangeAttr, Abilities.SIMPLE),
|
||||
new StatusMove(Moves.ENTRAINMENT, "Entrainment", Type.NORMAL, 100, 15, "The user dances with an odd rhythm that compels the target to mimic it, making the target's Ability the same as the user's.", -1, 0, 5)
|
||||
.attr(AbilityGiveAttr),
|
||||
new StatusMove(Moves.AFTER_YOU, "After You (N)", Type.NORMAL, -1, 15, "The user helps the target and makes it use its move right after the user.", -1, 0, 5)
|
||||
.ignoresProtect(),
|
||||
new AttackMove(Moves.ROUND, "Round (P)", Type.NORMAL, MoveCategory.SPECIAL, 60, 100, 15, "The user attacks the target with a song. Others can join in the Round to increase the power of the attack.", -1, 0, 5)
|
||||
@ -4505,7 +4789,7 @@ export function initMoves() {
|
||||
new StatusMove(Moves.TOPSY_TURVY, "Topsy-Turvy", Type.DARK, -1, 20, "All stat changes affecting the target turn topsy-turvy and become the opposite of what they were.", -1, 0, 6)
|
||||
.attr(InvertStatsAttr),
|
||||
new AttackMove(Moves.DRAINING_KISS, "Draining Kiss", Type.FAIRY, MoveCategory.SPECIAL, 50, 100, 10, "The user steals the target's HP with a kiss. The user's HP is restored by over half of the damage taken by the target.", -1, 0, 6)
|
||||
.attr(HitHealAttr)
|
||||
.attr(HitHealAttr, 0.75)
|
||||
.makesContact()
|
||||
.triageMove(),
|
||||
new StatusMove(Moves.CRAFTY_SHIELD, "Crafty Shield (N)", Type.FAIRY, -1, 10, "The user protects itself and its allies from status moves with a mysterious power. This does not stop moves that do damage.", -1, 3, 6)
|
||||
@ -4567,9 +4851,9 @@ export function initMoves() {
|
||||
.attr(StatChangeAttr, [ BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD ], 2, true)
|
||||
.ignoresVirtual(),
|
||||
new StatusMove(Moves.MAGNETIC_FLUX, "Magnetic Flux", Type.ELECTRIC, -1, 20, "The user manipulates magnetic fields, which raises the Defense and Sp. Def stats of ally Pokémon with the Plus or Minus Ability.", -1, 0, 6)
|
||||
.attr(StatChangeAttr, [ BattleStat.DEF, BattleStat.SPDEF ], 1, false, (user, target, move) => !![ Abilities.PLUS, Abilities.MINUS].find(a => a === user.getAbility().id || (user.canApplyPassive() && a === user.getPassiveAbility().id)))
|
||||
.attr(StatChangeAttr, [ BattleStat.DEF, BattleStat.SPDEF ], 1, false, (user, target, move) => !![ Abilities.PLUS, Abilities.MINUS].find(a => target.hasAbility(a, false)))
|
||||
.target(MoveTarget.USER_AND_ALLIES)
|
||||
.condition((user, target, move) => !![ user, user.getAlly() ].filter(p => p?.isActive()).find(p => !![ Abilities.PLUS, Abilities.MINUS].find(a => a === p.getAbility().id || (user.canApplyPassive() && a === user.getPassiveAbility().id)))),
|
||||
.condition((user, target, move) => !![ user, user.getAlly() ].filter(p => p?.isActive()).find(p => !![ Abilities.PLUS, Abilities.MINUS].find(a => p.hasAbility(a, false)))),
|
||||
new StatusMove(Moves.HAPPY_HOUR, "Happy Hour (N)", Type.NORMAL, -1, 30, "Using Happy Hour doubles the amount of prize money received after battle.", -1, 0, 6) // No animation
|
||||
.target(MoveTarget.USER_SIDE),
|
||||
new StatusMove(Moves.ELECTRIC_TERRAIN, "Electric Terrain", Type.ELECTRIC, -1, 10, "The user electrifies the ground for five turns, powering up Electric-type moves. Pokémon on the ground no longer fall asleep.", -1, 0, 6)
|
||||
@ -4695,11 +4979,11 @@ export function initMoves() {
|
||||
.attr(StatusEffectAttr, StatusEffect.POISON)
|
||||
.attr(StatChangeAttr, BattleStat.SPD, -1),
|
||||
new SelfStatusMove(Moves.LASER_FOCUS, "Laser Focus", Type.NORMAL, -1, 30, "The user concentrates intensely. The attack on the next turn always results in a critical hit.", -1, 0, 7)
|
||||
.attr(AddBattlerTagAttr, BattlerTagType.ALWAYS_CRIT, true, false),
|
||||
.attr(AddBattlerTagAttr, BattlerTagType.ALWAYS_CRIT, true, false),
|
||||
new StatusMove(Moves.GEAR_UP, "Gear Up", Type.STEEL, -1, 20, "The user engages its gears to raise the Attack and Sp. Atk stats of ally Pokémon with the Plus or Minus Ability.", -1, 0, 7)
|
||||
.attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.SPATK ], 1, false, (user, target, move) => [ Abilities.PLUS, Abilities.MINUS ].includes(target.getAbility().id) || (target.canApplyPassive() && [ Abilities.PLUS, Abilities.MINUS ].includes(target.getPassiveAbility().id)))
|
||||
.attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.SPATK ], 1, false, (user, target, move) => !![ Abilities.PLUS, Abilities.MINUS].find(a => target.hasAbility(a, false)))
|
||||
.target(MoveTarget.USER_AND_ALLIES)
|
||||
.condition((user, target, move) => !![ user, user.getAlly() ].find(p => p && [ Abilities.PLUS, Abilities.MINUS ].includes(p.getAbility().id) || (target.canApplyPassive() && [ Abilities.PLUS, Abilities.MINUS ].includes(target.getPassiveAbility().id)))),
|
||||
.condition((user, target, move) => !![ user, user.getAlly() ].filter(p => p?.isActive()).find(p => !![ Abilities.PLUS, Abilities.MINUS].find(a => p.hasAbility(a, false)))),
|
||||
new AttackMove(Moves.THROAT_CHOP, "Throat Chop (P)", Type.DARK, MoveCategory.PHYSICAL, 80, 100, 15, "The user attacks the target's throat, and the resultant suffering prevents the target from using moves that emit sound for two turns.", 100, 0, 7),
|
||||
new AttackMove(Moves.POLLEN_PUFF, "Pollen Puff (P)", Type.BUG, MoveCategory.SPECIAL, 90, 100, 15, "The user attacks the enemy with a pollen puff that explodes. If the target is an ally, it gives the ally a pollen puff that restores its HP instead.", -1, 0, 7)
|
||||
.ballBombMove(),
|
||||
@ -4753,14 +5037,16 @@ export function initMoves() {
|
||||
new AttackMove(Moves.PULVERIZING_PANCAKE, "Pulverizing Pancake (P)", Type.NORMAL, MoveCategory.PHYSICAL, 210, -1, 1, "Z-Power brings out the true capabilities of the user, Snorlax. The Pokémon moves its enormous body energetically and attacks the target with full force.", -1, 0, 7),
|
||||
new SelfStatusMove(Moves.EXTREME_EVOBOOST, "Extreme Evoboost", Type.NORMAL, -1, 1, "After obtaining Z-Power, the user, Eevee, gets energy from its evolved friends and boosts its stats sharply.", 100, 0, 7)
|
||||
.attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD ], 2, true),
|
||||
new AttackMove(Moves.GENESIS_SUPERNOVA, "Genesis Supernova (P)", Type.PSYCHIC, MoveCategory.SPECIAL, 185, -1, 1, "After obtaining Z-Power, the user, Mew, attacks the target with full force. The terrain will be charged with psychic energy.", -1, 0, 7),
|
||||
new AttackMove(Moves.GENESIS_SUPERNOVA, "Genesis Supernova", Type.PSYCHIC, MoveCategory.SPECIAL, 185, -1, 1, "After obtaining Z-Power, the user, Mew, attacks the target with full force. The terrain will be charged with psychic energy.", -1, 0, 7)
|
||||
.attr(TerrainChangeAttr, TerrainType.PSYCHIC),
|
||||
/* End Unused */
|
||||
new AttackMove(Moves.SHELL_TRAP, "Shell Trap (P)", Type.FIRE, MoveCategory.SPECIAL, 150, 100, 5, "The user sets a shell trap. If the user is hit by a physical move, the trap will explode and inflict damage on opposing Pokémon.", -1, -3, 7)
|
||||
.target(MoveTarget.ALL_NEAR_ENEMIES),
|
||||
new AttackMove(Moves.FLEUR_CANNON, "Fleur Cannon", Type.FAIRY, MoveCategory.SPECIAL, 130, 90, 5, "The user unleashes a strong beam. The attack's recoil harshly lowers the user's Sp. Atk stat.", 100, 0, 7)
|
||||
.attr(StatChangeAttr, BattleStat.SPATK, -2, true),
|
||||
new AttackMove(Moves.PSYCHIC_FANGS, "Psychic Fangs (P)", Type.PSYCHIC, MoveCategory.PHYSICAL, 85, 100, 10, "The user bites the target with its psychic capabilities. This can also destroy Light Screen and Reflect.", -1, 0, 7)
|
||||
.bitingMove(),
|
||||
new AttackMove(Moves.PSYCHIC_FANGS, "Psychic Fangs", Type.PSYCHIC, MoveCategory.PHYSICAL, 85, 100, 10, "The user bites the target with its psychic capabilities. This can also destroy Light Screen and Reflect.", -1, 0, 7)
|
||||
.bitingMove()
|
||||
.attr(RemoveScreensAttr),
|
||||
new AttackMove(Moves.STOMPING_TANTRUM, "Stomping Tantrum (P)", Type.GROUND, MoveCategory.PHYSICAL, 75, 100, 10, "Driven by frustration, the user attacks the target. If the user's previous move has failed, the power of this move doubles.", -1, 0, 7),
|
||||
new AttackMove(Moves.SHADOW_BONE, "Shadow Bone", Type.GHOST, MoveCategory.PHYSICAL, 85, 100, 10, "The user attacks by beating the target with a bone that contains a spirit. This may also lower the target's Defense stat.", 20, 0, 7)
|
||||
.attr(StatChangeAttr, BattleStat.DEF, -1)
|
||||
@ -4801,6 +5087,7 @@ export function initMoves() {
|
||||
.attr(ClearTerrainAttr)
|
||||
.makesContact(false),
|
||||
new AttackMove(Moves.CLANGOROUS_SOULBLAZE, "Clangorous Soulblaze (P)", Type.DRAGON, MoveCategory.SPECIAL, 185, -1, 1, "After obtaining Z-Power, the user, Kommo-o, attacks the opposing Pokémon with full force. This move boosts the user's stats.", 100, 0, 7)
|
||||
.attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD ], 1, true)
|
||||
.soundBased()
|
||||
.target(MoveTarget.ALL_NEAR_ENEMIES),
|
||||
/* End Unused */
|
||||
@ -4941,8 +5228,9 @@ export function initMoves() {
|
||||
.target(MoveTarget.ALL_NEAR_ENEMIES),
|
||||
new AttackMove(Moves.APPLE_ACID, "Apple Acid", Type.GRASS, MoveCategory.SPECIAL, 80, 100, 10, "The user attacks the target with an acidic liquid created from tart apples. This also lowers the target's Sp. Def stat.", 100, 0, 8)
|
||||
.attr(StatChangeAttr, BattleStat.SPDEF, -1),
|
||||
new AttackMove(Moves.GRAV_APPLE, "Grav Apple (P)", Type.GRASS, MoveCategory.PHYSICAL, 80, 100, 10, "The user inflicts damage by dropping an apple from high above. This also lowers the target's Defense stat.", 100, 0, 8)
|
||||
new AttackMove(Moves.GRAV_APPLE, "Grav Apple", Type.GRASS, MoveCategory.PHYSICAL, 80, 100, 10, "The user inflicts damage by dropping an apple from high above. This also lowers the target's Defense stat.", 100, 0, 8)
|
||||
.attr(StatChangeAttr, BattleStat.DEF, -1)
|
||||
.attr(MovePowerMultiplierAttr, (user, target, move) => user.scene.arena.getTag(ArenaTagType.GRAVITY) ? 1.5 : 1)
|
||||
.makesContact(false),
|
||||
new AttackMove(Moves.SPIRIT_BREAK, "Spirit Break", Type.FAIRY, MoveCategory.PHYSICAL, 75, 100, 15, "The user attacks the target with so much force that it could break the target's spirit. This also lowers the target's Sp. Atk stat.", 100, 0, 8)
|
||||
.attr(StatChangeAttr, BattleStat.SPATK, -1),
|
||||
@ -5010,7 +5298,7 @@ export function initMoves() {
|
||||
.attr(HealStatusEffectAttr, true, StatusEffect.FREEZE)
|
||||
.attr(StatusEffectAttr, StatusEffect.BURN),
|
||||
new StatusMove(Moves.JUNGLE_HEALING, "Jungle Healing (P)", Type.GRASS, -1, 10, "The user becomes one with the jungle, restoring HP and healing any status conditions of itself and its ally Pokémon in battle.", -1, 0, 8)
|
||||
.attr(HealAttr, 0.25)
|
||||
.attr(HealAttr, 0.25, true, false)
|
||||
.target(MoveTarget.USER_AND_ALLIES),
|
||||
new AttackMove(Moves.WICKED_BLOW, "Wicked Blow", Type.DARK, MoveCategory.PHYSICAL, 75, 100, 5, "The user, having mastered the Dark style, strikes the target with a fierce blow. This attack always results in a critical hit.", -1, 0, 8)
|
||||
.attr(CritOnlyAttr)
|
||||
@ -5183,7 +5471,8 @@ export function initMoves() {
|
||||
new SelfStatusMove(Moves.SILK_TRAP, "Silk Trap", Type.BUG, -1, 10, "The user spins a silken trap, protecting itself from damage while lowering the Speed stat of any attacker that makes direct contact.", -1, 4, 9)
|
||||
.attr(ProtectAttr, BattlerTagType.SILK_TRAP),
|
||||
new AttackMove(Moves.AXE_KICK, "Axe Kick", Type.FIGHTING, MoveCategory.PHYSICAL, 120, 90, 10, "The user attacks by kicking up into the air and slamming its heel down upon the target. This may also confuse the target. If it misses, the user takes damage instead.", 30, 0, 9)
|
||||
.attr(MissEffectAttr, halveHpMissEffectFunc)
|
||||
.attr(MissEffectAttr, crashDamageFunc)
|
||||
.attr(NoEffectAttr, crashDamageFunc)
|
||||
.attr(ConfuseAttr),
|
||||
new AttackMove(Moves.LAST_RESPECTS, "Last Respects", Type.GHOST, MoveCategory.PHYSICAL, 50, 100, 10, "The user attacks to avenge its allies. The more defeated allies there are in the user's party, the greater the move's power.", -1, 0, 9)
|
||||
.attr(MovePowerMultiplierAttr, (user, target, move) => {
|
||||
@ -5219,7 +5508,8 @@ export function initMoves() {
|
||||
.attr(LapseBattlerTagAttr, [ BattlerTagType.BIND, BattlerTagType.WRAP, BattlerTagType.FIRE_SPIN, BattlerTagType.WHIRLPOOL, BattlerTagType.CLAMP, BattlerTagType.SAND_TOMB, BattlerTagType.MAGMA_STORM, BattlerTagType.THUNDER_CAGE, BattlerTagType.SEEDED ], true)
|
||||
.attr(StatusEffectAttr, StatusEffect.POISON)
|
||||
.target(MoveTarget.ALL_NEAR_ENEMIES),
|
||||
new StatusMove(Moves.DOODLE, "Doodle (N)", Type.NORMAL, 100, 10, "The user captures the very essence of the target in a sketch. This changes the Abilities of the user and its ally Pokémon to that of the target.", -1, 0, 9),
|
||||
new StatusMove(Moves.DOODLE, "Doodle", Type.NORMAL, 100, 10, "The user captures the very essence of the target in a sketch. This changes the Abilities of the user and its ally Pokémon to that of the target.", -1, 0, 9)
|
||||
.attr(AbilityCopyAttr, true),
|
||||
new SelfStatusMove(Moves.FILLET_AWAY, "Fillet Away", Type.NORMAL, -1, 10, "The user sharply boosts its Attack, Sp. Atk, and Speed stats by using its own HP.", -1, 0, 9)
|
||||
.attr(CutHpStatBoostAttr, [ BattleStat.ATK, BattleStat.SPATK, BattleStat.SPD ], 2, 2),
|
||||
new AttackMove(Moves.KOWTOW_CLEAVE, "Kowtow Cleave", Type.DARK, MoveCategory.PHYSICAL, 85, -1, 10, "The user slashes at the target after kowtowing to make the target let down its guard. This attack never misses.", -1, 0, 9)
|
||||
@ -5233,8 +5523,9 @@ export function initMoves() {
|
||||
new AttackMove(Moves.AQUA_STEP, "Aqua Step", Type.WATER, MoveCategory.PHYSICAL, 80, 100, 10, "The user toys with the target and attacks it using light and fluid dance steps. This also boosts the user's Speed stat.", 100, 0, 9)
|
||||
.attr(StatChangeAttr, BattleStat.SPD, 1, true)
|
||||
.danceMove(),
|
||||
new AttackMove(Moves.RAGING_BULL, "Raging Bull (P)", Type.NORMAL, MoveCategory.PHYSICAL, 90, 100, 10, "The user performs a tackle like a raging bull. This move's type depends on the user's form. It can also break barriers, such as Light Screen and Reflect.", -1, 0, 9)
|
||||
.attr(RagingBullTypeAttr),
|
||||
new AttackMove(Moves.RAGING_BULL, "Raging Bull", Type.NORMAL, MoveCategory.PHYSICAL, 90, 100, 10, "The user performs a tackle like a raging bull. This move's type depends on the user's form. It can also break barriers, such as Light Screen and Reflect.", -1, 0, 9)
|
||||
.attr(RagingBullTypeAttr)
|
||||
.attr(RemoveScreensAttr),
|
||||
new AttackMove(Moves.MAKE_IT_RAIN, "Make It Rain", Type.STEEL, MoveCategory.SPECIAL, 120, 100, 5, "The user attacks by throwing out a mass of coins. This also lowers the user's Sp. Atk stat. Money is earned after the battle.", -1, 0, 9)
|
||||
.attr(MoneyAttr)
|
||||
.attr(StatChangeAttr, BattleStat.SPATK, -1, true, null, true, true)
|
||||
@ -5245,8 +5536,10 @@ export function initMoves() {
|
||||
new AttackMove(Moves.HYDRO_STEAM, "Hydro Steam (P)", Type.WATER, MoveCategory.SPECIAL, 80, 100, 15, "The user blasts the target with boiling-hot water. This move's power is not lowered in harsh sunlight but rather boosted by 50 percent.", -1, 0, 9),
|
||||
new AttackMove(Moves.RUINATION, "Ruination", Type.DARK, MoveCategory.SPECIAL, 1, 90, 10, "The user summons a ruinous disaster. This cuts the target's HP in half.", -1, 0, 9)
|
||||
.attr(TargetHalfHpDamageAttr),
|
||||
new AttackMove(Moves.COLLISION_COURSE, "Collision Course (P)", Type.FIGHTING, MoveCategory.PHYSICAL, 100, 100, 5, "The user transforms and crashes to the ground, causing a massive prehistoric explosion. This move's power is boosted more than usual if it's a supereffective hit.", -1, 0, 9),
|
||||
new AttackMove(Moves.ELECTRO_DRIFT, "Electro Drift (P)", Type.ELECTRIC, MoveCategory.SPECIAL, 100, 100, 5, "The user races forward at ultrafast speeds, piercing its target with futuristic electricity. This move's power is boosted more than usual if it's a supereffective hit.", -1, 0, 9)
|
||||
new AttackMove(Moves.COLLISION_COURSE, "Collision Course", Type.FIGHTING, MoveCategory.PHYSICAL, 100, 100, 5, "The user transforms and crashes to the ground, causing a massive prehistoric explosion. This move's power is boosted more than usual if it's a supereffective hit.", -1, 0, 9)
|
||||
.attr(MovePowerMultiplierAttr, (user, target, move) => target.getAttackTypeEffectiveness(move.type) >= 2 ? 5461/4096 : 1),
|
||||
new AttackMove(Moves.ELECTRO_DRIFT, "Electro Drift", Type.ELECTRIC, MoveCategory.SPECIAL, 100, 100, 5, "The user races forward at ultrafast speeds, piercing its target with futuristic electricity. This move's power is boosted more than usual if it's a supereffective hit.", -1, 0, 9)
|
||||
.attr(MovePowerMultiplierAttr, (user, target, move) => target.getAttackTypeEffectiveness(move.type) >= 2 ? 5461/4096 : 1)
|
||||
.makesContact(),
|
||||
new SelfStatusMove(Moves.SHED_TAIL, "Shed Tail (N)", Type.NORMAL, -1, 10, "The user creates a substitute for itself using its own HP before switching places with a party Pokémon in waiting.", -1, 0, 9),
|
||||
new StatusMove(Moves.CHILLY_RECEPTION, "Chilly Reception", Type.ICE, -1, 10, "The user tells a chillingly bad joke before switching places with a party Pokémon in waiting. This summons a snowstorm lasting five turns.", -1, 0, 9)
|
||||
@ -5279,7 +5572,11 @@ export function initMoves() {
|
||||
.triageMove(),
|
||||
new AttackMove(Moves.DOUBLE_SHOCK, "Double Shock (P)", Type.ELECTRIC, MoveCategory.PHYSICAL, 120, 100, 5, "The user discharges all the electricity from its body to perform a high-damage attack. After using this move, the user will no longer be Electric type.", -1, 0, 9),
|
||||
new AttackMove(Moves.GIGATON_HAMMER, "Gigaton Hammer (P)", Type.STEEL, MoveCategory.PHYSICAL, 160, 100, 5, "The user swings its whole body around to attack with its huge hammer. This move can't be used twice in a row.", -1, 0, 9)
|
||||
.makesContact(false),
|
||||
.makesContact(false)
|
||||
.condition((user, target, move) => {
|
||||
const turnMove = user.getLastXMoves(1);
|
||||
return !turnMove.length || turnMove[0].move !== move.id || turnMove[0].result !== MoveResult.SUCCESS;
|
||||
}), // TODO Add Instruct/Encore interaction
|
||||
new AttackMove(Moves.COMEUPPANCE, "Comeuppance", Type.DARK, MoveCategory.PHYSICAL, 1, 100, 10, "The user retaliates with much greater force against the opponent that last inflicted damage on it.", -1, 0, 9)
|
||||
.attr(CounterDamageAttr, (move: Move) => (move.category === MoveCategory.PHYSICAL || move.category === MoveCategory.SPECIAL), 1.5)
|
||||
.target(MoveTarget.ATTACKER),
|
||||
@ -5302,7 +5599,11 @@ export function initMoves() {
|
||||
new AttackMove(Moves.MAGICAL_TORQUE, "Magical Torque", Type.FAIRY, MoveCategory.PHYSICAL, 100, 100, 10, "The user revs their fae-like engine into the target. This may also confuse the target.", 30, 0, 9)
|
||||
.attr(ConfuseAttr)
|
||||
.makesContact(false),
|
||||
new AttackMove(Moves.BLOOD_MOON, "Blood Moon (P)", Type.NORMAL, MoveCategory.SPECIAL, 140, 100, 5, "The user unleashes the full brunt of its spirit from a full moon that shines as red as blood. This move can't be used twice in a row.", -1, 0, 9),
|
||||
new AttackMove(Moves.BLOOD_MOON, "Blood Moon (P)", Type.NORMAL, MoveCategory.SPECIAL, 140, 100, 5, "The user unleashes the full brunt of its spirit from a full moon that shines as red as blood. This move can't be used twice in a row.", -1, 0, 9)
|
||||
.condition((user, target, move) => {
|
||||
const turnMove = user.getLastXMoves(1);
|
||||
return !turnMove.length || turnMove[0].move !== move.id || turnMove[0].result !== MoveResult.SUCCESS;
|
||||
}), // TODO Add Instruct/Encore interaction
|
||||
new AttackMove(Moves.MATCHA_GOTCHA, "Matcha Gotcha", Type.GRASS, MoveCategory.SPECIAL, 80, 90, 15, "The user fires a blast of tea that it mixed. The user's HP is restored by up to half the damage taken by the target. This may also leave the target with a burn.", 20, 0, 9)
|
||||
.attr(HitHealAttr)
|
||||
.attr(HealStatusEffectAttr, true, StatusEffect.FREEZE)
|
||||
@ -5312,7 +5613,8 @@ export function initMoves() {
|
||||
new AttackMove(Moves.SYRUP_BOMB, "Syrup Bomb (P)", Type.GRASS, MoveCategory.SPECIAL, 60, 85, 10, "The user sets off an explosion of sticky candy syrup, which coats the target and causes the target's Speed stat to drop each turn for three turns.", -1, 0, 9)
|
||||
.attr(StatChangeAttr, BattleStat.SPD, -1) //Temporary
|
||||
.ballBombMove(),
|
||||
new AttackMove(Moves.IVY_CUDGEL, "Ivy Cudgel (P)", Type.GRASS, MoveCategory.PHYSICAL, 100, 100, 10, "The user strikes with an ivy-wrapped cudgel. This move's type changes depending on the mask worn by the user, and it has a heightened chance of landing a critical hit.", -1, 0, 9)
|
||||
new AttackMove(Moves.IVY_CUDGEL, "Ivy Cudgel", Type.GRASS, MoveCategory.PHYSICAL, 100, 100, 10, "The user strikes with an ivy-wrapped cudgel. This move's type changes depending on the mask worn by the user, and it has a heightened chance of landing a critical hit.", -1, 0, 9)
|
||||
.attr(IvyCudgelTypeAttr)
|
||||
.attr(HighCritAttr)
|
||||
.makesContact(false),
|
||||
new AttackMove(Moves.ELECTRO_SHOT, "Electro Shot", Type.ELECTRIC, MoveCategory.SPECIAL, 130, 100, 10, "The user gathers electricity on the first turn, boosting its Sp. Atk stat, then fires a high-voltage shot on the next turn. The shot will be fired immediately in rain.", 100, 0, 9)
|
||||
@ -5326,11 +5628,13 @@ export function initMoves() {
|
||||
.attr(DoublePowerChanceAttr),
|
||||
new StatusMove(Moves.BURNING_BULWARK, "Burning Bulwark", Type.FIRE, -1, 10, "The user's intensely hot fur protects it from attacks and also burns any attacker that makes direct contact with it.", 100, 4, 9)
|
||||
.attr(ProtectAttr, BattlerTagType.BURNING_BULWARK),
|
||||
new AttackMove(Moves.THUNDERCLAP, "Thunderclap (P)", Type.ELECTRIC, MoveCategory.SPECIAL, 70, 100, 5, "This move enables the user to attack first with a jolt of electricity. This move fails if the target is not readying an attack.", -1, 1, 9),
|
||||
new AttackMove(Moves.THUNDERCLAP, "Thunderclap", Type.ELECTRIC, MoveCategory.SPECIAL, 70, 100, 5, "This move enables the user to attack first with a jolt of electricity. This move fails if the target is not readying an attack.", -1, 1, 9)
|
||||
.condition((user, target, move) => user.scene.currentBattle.turnCommands[target.getBattlerIndex()].command === Command.FIGHT && !target.turnData.acted && allMoves[user.scene.currentBattle.turnCommands[target.getBattlerIndex()].move.move].category !== MoveCategory.STATUS),
|
||||
new AttackMove(Moves.MIGHTY_CLEAVE, "Mighty Cleave", Type.ROCK, MoveCategory.PHYSICAL, 95, 100, 5, "The user wields the light that has accumulated atop its head to cleave the target. This move hits even if the target protects itself.", -1, 0, 9)
|
||||
.ignoresProtect(),
|
||||
new AttackMove(Moves.TACHYON_CUTTER, "Tachyon Cutter", Type.STEEL, MoveCategory.SPECIAL, 50, -1, 10, "The user attacks by launching particle blades at the target twice in a row. This attack never misses.", -1, 0, 9)
|
||||
.attr(MultiHitAttr, MultiHitType._2),
|
||||
.attr(MultiHitAttr, MultiHitType._2)
|
||||
.slicingMove(),
|
||||
new AttackMove(Moves.HARD_PRESS, "Hard Press", Type.STEEL, MoveCategory.PHYSICAL, 100, 100, 5, "The target is crushed with an arm, a claw, or the like to inflict damage. The more HP the target has left, the greater the move's power.", -1, 0, 9)
|
||||
.attr(OpponentHighHpPowerAttr),
|
||||
new StatusMove(Moves.DRAGON_CHEER, "Dragon Cheer (P)", Type.DRAGON, -1, 15, "The user raises its allies' morale with a draconic cry so that their future attacks have a heightened chance of landing critical hits. This rouses Dragon types more.", 100, 0, 9)
|
||||
@ -5339,7 +5643,8 @@ export function initMoves() {
|
||||
new AttackMove(Moves.ALLURING_VOICE, "Alluring Voice (P)", Type.FAIRY, MoveCategory.SPECIAL, 80, 100, 10, "The user attacks the target using its angelic voice. This also confuses the target if its stats have been boosted during the turn.", -1, 0, 9),
|
||||
new AttackMove(Moves.TEMPER_FLARE, "Temper Flare (P)", Type.FIRE, MoveCategory.PHYSICAL, 75, 100, 10, "Spurred by desperation, the user attacks the target. This move's power is doubled if the user's previous move failed.", -1, 0, 9),
|
||||
new AttackMove(Moves.SUPERCELL_SLAM, "Supercell Slam", Type.ELECTRIC, MoveCategory.PHYSICAL, 100, 95, 15, "The user electrifies its body and drops onto the target to inflict damage. If this move misses, the user takes damage instead.", -1, 0, 9)
|
||||
.attr(MissEffectAttr, halveHpMissEffectFunc),
|
||||
.attr(MissEffectAttr, crashDamageFunc)
|
||||
.attr(NoEffectAttr, crashDamageFunc),
|
||||
new AttackMove(Moves.PSYCHIC_NOISE, "Psychic Noise (P)", Type.PSYCHIC, MoveCategory.SPECIAL, 75, 100, 10, "The user attacks the target with unpleasant sound waves. For two turns, the target is prevented from recovering HP through moves, Abilities, or held items.", -1, 0, 9)
|
||||
.soundBased(),
|
||||
new AttackMove(Moves.UPPER_HAND, "Upper Hand (P)", Type.FIGHTING, MoveCategory.PHYSICAL, 65, 100, 15, "The user reacts to the target's movement and strikes with the heel of its palm, making the target flinch. This move fails if the target is not readying a priority move.", -1, 3, 9),
|
||||
|
@ -535,8 +535,8 @@ export const pokemonFormChanges: PokemonFormChanges = {
|
||||
new SpeciesFormChange(Species.MELOETTA, 'pirouette', 'aria', new SpeciesFormChangeActiveTrigger(false), true)
|
||||
],
|
||||
[Species.AEGISLASH]: [
|
||||
new SpeciesFormChange(Species.AEGISLASH, 'blade', 'shield', new SpeciesFormChangePreMoveTrigger(Moves.KINGS_SHIELD), true, new SpeciesFormChangeCondition(p => p.getAbility().id === Abilities.STANCE_CHANGE)),
|
||||
new SpeciesFormChange(Species.AEGISLASH, 'shield', 'blade', new SpeciesFormChangePreMoveTrigger(m => allMoves[m].category !== MoveCategory.STATUS), true, new SpeciesFormChangeCondition(p => p.getAbility().id === Abilities.STANCE_CHANGE)),
|
||||
new SpeciesFormChange(Species.AEGISLASH, 'blade', 'shield', new SpeciesFormChangePreMoveTrigger(Moves.KINGS_SHIELD), true, new SpeciesFormChangeCondition(p => p.hasAbility(Abilities.STANCE_CHANGE))),
|
||||
new SpeciesFormChange(Species.AEGISLASH, 'shield', 'blade', new SpeciesFormChangePreMoveTrigger(m => allMoves[m].category !== MoveCategory.STATUS), true, new SpeciesFormChangeCondition(p => p.hasAbility(Abilities.STANCE_CHANGE))),
|
||||
new SpeciesFormChange(Species.AEGISLASH, 'blade', 'shield', new SpeciesFormChangeActiveTrigger(false), true)
|
||||
],
|
||||
[Species.DIANCIE]: [
|
||||
@ -549,6 +549,22 @@ export const pokemonFormChanges: PokemonFormChanges = {
|
||||
new SpeciesFormChange(Species.WISHIWASHI, '', 'school', new SpeciesFormChangeManualTrigger(), true),
|
||||
new SpeciesFormChange(Species.WISHIWASHI, 'school', '', new SpeciesFormChangeManualTrigger(), true)
|
||||
],
|
||||
[Species.MINIOR]: [
|
||||
new SpeciesFormChange(Species.MINIOR, 'red-meteor', 'red', new SpeciesFormChangeManualTrigger(), true),
|
||||
new SpeciesFormChange(Species.MINIOR, 'red', 'red-meteor', new SpeciesFormChangeManualTrigger(), true),
|
||||
new SpeciesFormChange(Species.MINIOR, 'orange-meteor', 'orange', new SpeciesFormChangeManualTrigger(), true),
|
||||
new SpeciesFormChange(Species.MINIOR, 'orange', 'orange-meteor', new SpeciesFormChangeManualTrigger(), true),
|
||||
new SpeciesFormChange(Species.MINIOR, 'yellow-meteor', 'yellow', new SpeciesFormChangeManualTrigger(), true),
|
||||
new SpeciesFormChange(Species.MINIOR, 'yellow', 'yellow-meteor', new SpeciesFormChangeManualTrigger(), true),
|
||||
new SpeciesFormChange(Species.MINIOR, 'green-meteor', 'green', new SpeciesFormChangeManualTrigger(), true),
|
||||
new SpeciesFormChange(Species.MINIOR, 'green', 'green-meteor', new SpeciesFormChangeManualTrigger(), true),
|
||||
new SpeciesFormChange(Species.MINIOR, 'blue-meteor', 'blue', new SpeciesFormChangeManualTrigger(), true),
|
||||
new SpeciesFormChange(Species.MINIOR, 'blue', 'blue-meteor', new SpeciesFormChangeManualTrigger(), true),
|
||||
new SpeciesFormChange(Species.MINIOR, 'indigo-meteor', 'indigo', new SpeciesFormChangeManualTrigger(), true),
|
||||
new SpeciesFormChange(Species.MINIOR, 'indigo', 'indigo-meteor', new SpeciesFormChangeManualTrigger(), true),
|
||||
new SpeciesFormChange(Species.MINIOR, 'violet-meteor', 'violet', new SpeciesFormChangeManualTrigger(), true),
|
||||
new SpeciesFormChange(Species.MINIOR, 'violet', 'violet-meteor', new SpeciesFormChangeManualTrigger(), true)
|
||||
],
|
||||
[Species.NECROZMA]: [
|
||||
new SpeciesFormChange(Species.NECROZMA, '', 'dawn-wings', new SpeciesFormChangeItemTrigger(FormChangeItem.N_LUNARIZER)),
|
||||
new SpeciesFormChange(Species.NECROZMA, '', 'dusk-mane', new SpeciesFormChangeItemTrigger(FormChangeItem.N_SOLARIZER))
|
||||
|
@ -11,6 +11,7 @@ import { StarterMoveset } from '../system/game-data';
|
||||
import { speciesEggMoves } from './egg-moves';
|
||||
import { PartyMemberStrength } from "./enums/party-member-strength";
|
||||
import { GameMode } from '../game-mode';
|
||||
import { QuantizerCelebi, argbFromRgba, rgbaFromArgb } from "@material/material-color-utilities";
|
||||
|
||||
export enum Region {
|
||||
NORMAL,
|
||||
@ -381,6 +382,55 @@ export abstract class PokemonSpeciesForm {
|
||||
cry.stop();
|
||||
return cry;
|
||||
}
|
||||
|
||||
generateCandyColors(scene: BattleScene): integer[][] {
|
||||
const sourceTexture = scene.textures.get(this.getSpriteKey(false));
|
||||
|
||||
const sourceFrame = sourceTexture.frames[sourceTexture.firstFrame];
|
||||
const sourceImage = sourceTexture.getSourceImage() as HTMLImageElement;
|
||||
|
||||
const canvas = document.createElement('canvas');
|
||||
|
||||
const spriteColors: integer[][] = [];
|
||||
|
||||
const context = canvas.getContext('2d');
|
||||
const frame = sourceFrame;
|
||||
canvas.width = frame.width;
|
||||
canvas.height = frame.height;
|
||||
context.drawImage(sourceImage, frame.cutX, frame.cutY, frame.width, frame.height, 0, 0, frame.width, frame.height);
|
||||
const imageData = context.getImageData(frame.cutX, frame.cutY, frame.width, frame.height);
|
||||
const pixelData = imageData.data;
|
||||
|
||||
for (let i = 0; i < pixelData.length; i += 4) {
|
||||
if (pixelData[i + 3]) {
|
||||
const pixel = pixelData.slice(i, i + 4);
|
||||
const [ r, g, b, a ] = pixel;
|
||||
if (!spriteColors.find(c => c[0] === r && c[1] === g && c[2] === b))
|
||||
spriteColors.push([ r, g, b, a ]);
|
||||
}
|
||||
}
|
||||
|
||||
const pixelColors = [];
|
||||
for (let i = 0; i < pixelData.length; i += 4) {
|
||||
const total = pixelData.slice(i, i + 3).reduce((total: integer, value: integer) => total + value, 0);
|
||||
if (!total)
|
||||
continue;
|
||||
pixelColors.push(argbFromRgba({ r: pixelData[i], g: pixelData[i + 1], b: pixelData[i + 2], a: pixelData[i + 3] }));
|
||||
}
|
||||
|
||||
let paletteColors: Map<number, number>;
|
||||
|
||||
const originalRandom = Math.random;
|
||||
Math.random = () => Phaser.Math.RND.realInRange(0, 1);
|
||||
|
||||
scene.executeWithSeedOffset(() => {
|
||||
paletteColors = QuantizerCelebi.quantize(pixelColors, 2);
|
||||
}, 0, 'This result should not vary');
|
||||
|
||||
Math.random = originalRandom;
|
||||
|
||||
return Array.from(paletteColors.keys()).map(c => Object.values(rgbaFromArgb(c)) as integer[]);
|
||||
}
|
||||
}
|
||||
|
||||
export default class PokemonSpecies extends PokemonSpeciesForm {
|
||||
@ -1407,7 +1457,7 @@ export function initSpecies() {
|
||||
new PokemonForm("Origin Forme", "origin", Type.WATER, Type.DRAGON, 6.3, 659, Abilities.PRESSURE, Abilities.NONE, Abilities.TELEPATHY, 680, 90, 100, 100, 150, 120, 120, 3, 0, 340),
|
||||
),
|
||||
new PokemonSpecies(Species.HEATRAN, "Heatran", 4, true, false, false, "Lava Dome Pokémon", Type.FIRE, Type.STEEL, 1.7, 430, Abilities.FLASH_FIRE, Abilities.NONE, Abilities.FLAME_BODY, 600, 91, 90, 106, 130, 106, 77, 3, 100, 300, GrowthRate.SLOW, 50, false),
|
||||
new PokemonSpecies(Species.REGIGIGAS, "Regigigas", 4, false, true, false, "Colossal Pokémon", Type.NORMAL, null, 3.7, 420, Abilities.SLOW_START, Abilities.NONE, Abilities.NONE, 670, 110, 160, 110, 80, 110, 100, 3, 0, 335, GrowthRate.SLOW, null, false),
|
||||
new PokemonSpecies(Species.REGIGIGAS, "Regigigas", 4, false, true, false, "Colossal Pokémon", Type.NORMAL, null, 3.7, 420, Abilities.SLOW_START, Abilities.NONE, Abilities.NORMALIZE, 670, 110, 160, 110, 80, 110, 100, 3, 0, 335, GrowthRate.SLOW, null, false),
|
||||
new PokemonSpecies(Species.GIRATINA, "Giratina", 4, false, true, false, "Renegade Pokémon", Type.GHOST, Type.DRAGON, 4.5, 750, Abilities.PRESSURE, Abilities.NONE, Abilities.TELEPATHY, 680, 150, 100, 120, 100, 120, 90, 3, 0, 340, GrowthRate.SLOW, null, false, true,
|
||||
new PokemonForm("Altered Forme", "altered", Type.GHOST, Type.DRAGON, 4.5, 750, Abilities.PRESSURE, Abilities.NONE, Abilities.TELEPATHY, 680, 150, 100, 120, 100, 120, 90, 3, 0, 340),
|
||||
new PokemonForm("Origin Forme", "origin", Type.GHOST, Type.DRAGON, 6.9, 650, Abilities.LEVITATE, Abilities.NONE, Abilities.NONE, 680, 150, 120, 100, 120, 100, 90, 3, 0, 340),
|
||||
@ -1567,7 +1617,7 @@ export function initSpecies() {
|
||||
new PokemonSpecies(Species.ALOMOMOLA, "Alomomola", 5, false, false, false, "Caring Pokémon", Type.WATER, null, 1.2, 31.6, Abilities.HEALER, Abilities.HYDRATION, Abilities.REGENERATOR, 470, 165, 75, 80, 40, 45, 65, 75, 70, 165, GrowthRate.FAST, 50, false),
|
||||
new PokemonSpecies(Species.JOLTIK, "Joltik", 5, false, false, false, "Attaching Pokémon", Type.BUG, Type.ELECTRIC, 0.1, 0.6, Abilities.COMPOUND_EYES, Abilities.UNNERVE, Abilities.SWARM, 319, 50, 47, 50, 57, 50, 65, 190, 50, 64, GrowthRate.MEDIUM_FAST, 50, false),
|
||||
new PokemonSpecies(Species.GALVANTULA, "Galvantula", 5, false, false, false, "EleSpider Pokémon", Type.BUG, Type.ELECTRIC, 0.8, 14.3, Abilities.COMPOUND_EYES, Abilities.UNNERVE, Abilities.SWARM, 472, 70, 77, 60, 97, 60, 108, 75, 50, 165, GrowthRate.MEDIUM_FAST, 50, false),
|
||||
new PokemonSpecies(Species.FERROSEED, "Ferroseed", 5, false, false, false, "Thorn Seed Pokémon", Type.GRASS, Type.STEEL, 0.6, 18.8, Abilities.IRON_BARBS, Abilities.NONE, Abilities.NONE, 305, 44, 50, 91, 24, 86, 10, 255, 50, 61, GrowthRate.MEDIUM_FAST, 50, false),
|
||||
new PokemonSpecies(Species.FERROSEED, "Ferroseed", 5, false, false, false, "Thorn Seed Pokémon", Type.GRASS, Type.STEEL, 0.6, 18.8, Abilities.IRON_BARBS, Abilities.NONE, Abilities.IRON_BARBS, 305, 44, 50, 91, 24, 86, 10, 255, 50, 61, GrowthRate.MEDIUM_FAST, 50, false),
|
||||
new PokemonSpecies(Species.FERROTHORN, "Ferrothorn", 5, false, false, false, "Thorn Pod Pokémon", Type.GRASS, Type.STEEL, 1, 110, Abilities.IRON_BARBS, Abilities.NONE, Abilities.ANTICIPATION, 489, 74, 94, 131, 54, 116, 20, 90, 50, 171, GrowthRate.MEDIUM_FAST, 50, false),
|
||||
new PokemonSpecies(Species.KLINK, "Klink", 5, false, false, false, "Gear Pokémon", Type.STEEL, null, 0.3, 21, Abilities.PLUS, Abilities.MINUS, Abilities.CLEAR_BODY, 300, 40, 55, 70, 45, 60, 30, 130, 50, 60, GrowthRate.MEDIUM_SLOW, null, false),
|
||||
new PokemonSpecies(Species.KLANG, "Klang", 5, false, false, false, "Gear Pokémon", Type.STEEL, null, 0.6, 51, Abilities.PLUS, Abilities.MINUS, Abilities.CLEAR_BODY, 440, 60, 80, 95, 70, 85, 50, 60, 50, 154, GrowthRate.MEDIUM_SLOW, null, false),
|
||||
@ -3058,8 +3108,8 @@ export const starterPassiveAbilities = {
|
||||
[Species.CHARMANDER]: Abilities.INTIMIDATE,
|
||||
[Species.SQUIRTLE]: Abilities.DAUNTLESS_SHIELD,
|
||||
[Species.CATERPIE]: Abilities.MAGICIAN,
|
||||
[Species.WEEDLE]: Abilities.POISON_TOUCH,
|
||||
[Species.PIDGEY]: Abilities.TECHNICIAN,
|
||||
[Species.WEEDLE]: Abilities.TECHNICIAN,
|
||||
[Species.PIDGEY]: Abilities.GALE_WINGS,
|
||||
[Species.RATTATA]: Abilities.STRONG_JAW,
|
||||
[Species.SPEAROW]: Abilities.MOXIE,
|
||||
[Species.EKANS]: Abilities.ROUGH_SKIN,
|
||||
@ -3070,14 +3120,14 @@ export const starterPassiveAbilities = {
|
||||
[Species.ZUBAT]: Abilities.WIND_RIDER,
|
||||
[Species.ODDISH]: Abilities.LINGERING_AROMA,
|
||||
[Species.PARAS]: Abilities.POISON_HEAL,
|
||||
[Species.VENONAT]: Abilities.TECHNICIAN,
|
||||
[Species.VENONAT]: Abilities.SWARM,
|
||||
[Species.DIGLETT]: Abilities.STURDY,
|
||||
[Species.MEOWTH]: Abilities.NORMALIZE,
|
||||
[Species.PSYDUCK]: Abilities.SIMPLE,
|
||||
[Species.MANKEY]: Abilities.STAMINA,
|
||||
[Species.GROWLITHE]: Abilities.BALL_FETCH,
|
||||
[Species.MANKEY]: Abilities.IRON_FIST,
|
||||
[Species.GROWLITHE]: Abilities.SPEED_BOOST,
|
||||
[Species.POLIWAG]: Abilities.WATER_BUBBLE,
|
||||
[Species.ABRA]: Abilities.TECHNICIAN,
|
||||
[Species.ABRA]: Abilities.OPPORTUNIST,
|
||||
[Species.MACHOP]: Abilities.IRON_FIST,
|
||||
[Species.BELLSPROUT]: Abilities.CORROSION,
|
||||
[Species.TENTACOOL]: Abilities.INNARDS_OUT,
|
||||
@ -3103,7 +3153,7 @@ export const starterPassiveAbilities = {
|
||||
[Species.TANGELA]: Abilities.TANGLING_HAIR,
|
||||
[Species.KANGASKHAN]: Abilities.IRON_FIST,
|
||||
[Species.HORSEA]: Abilities.DRIZZLE,
|
||||
[Species.GOLDEEN]: Abilities.VOLT_ABSORB,
|
||||
[Species.GOLDEEN]: Abilities.MULTISCALE,
|
||||
[Species.STARYU]: Abilities.REGENERATOR,
|
||||
[Species.SCYTHER]: Abilities.SPEED_BOOST,
|
||||
[Species.PINSIR]: Abilities.SAP_SIPPER,
|
||||
@ -3139,7 +3189,7 @@ export const starterPassiveAbilities = {
|
||||
[Species.HOPPIP]: Abilities.PRANKSTER,
|
||||
[Species.AIPOM]: Abilities.SCRAPPY,
|
||||
[Species.SUNKERN]: Abilities.DROUGHT,
|
||||
[Species.YANMA]: Abilities.TECHNICIAN,
|
||||
[Species.YANMA]: Abilities.INFILTRATOR,
|
||||
[Species.WOOPER]: Abilities.SIMPLE,
|
||||
[Species.MURKROW]: Abilities.DEFIANT,
|
||||
[Species.MISDREAVUS]: Abilities.DAZZLING,
|
||||
@ -3154,7 +3204,7 @@ export const starterPassiveAbilities = {
|
||||
[Species.HERACROSS]: Abilities.QUICK_FEET,
|
||||
[Species.SNEASEL]: Abilities.MOXIE,
|
||||
[Species.TEDDIURSA]: Abilities.GLUTTONY,
|
||||
[Species.SLUGMA]: Abilities.DESOLATE_LAND,
|
||||
[Species.SLUGMA]: Abilities.DROUGHT,
|
||||
[Species.SWINUB]: Abilities.SLUSH_RUSH,
|
||||
[Species.CORSOLA]: Abilities.STORM_DRAIN,
|
||||
[Species.REMORAID]: Abilities.SKILL_LINK,
|
||||
@ -3166,7 +3216,7 @@ export const starterPassiveAbilities = {
|
||||
[Species.SMEARGLE]: Abilities.TRACE,
|
||||
[Species.TYROGUE]: Abilities.STAMINA,
|
||||
[Species.SMOOCHUM]: Abilities.CUTE_CHARM,
|
||||
[Species.ELEKID]: Abilities.ADAPTABILITY,
|
||||
[Species.ELEKID]: Abilities.IRON_FIST,
|
||||
[Species.MAGBY]: Abilities.CONTRARY,
|
||||
[Species.MILTANK]: Abilities.GLUTTONY,
|
||||
[Species.RAIKOU]: Abilities.FLARE_BOOST,
|
||||
@ -3192,7 +3242,7 @@ export const starterPassiveAbilities = {
|
||||
[Species.SLAKOTH]: Abilities.GUTS,
|
||||
[Species.NINCADA]: Abilities.OVERCOAT,
|
||||
[Species.WHISMUR]: Abilities.PUNK_ROCK,
|
||||
[Species.MAKUHITA]: Abilities.CONTRARY,
|
||||
[Species.MAKUHITA]: Abilities.STAMINA,
|
||||
[Species.AZURILL]: Abilities.UNNERVE,
|
||||
[Species.NOSEPASS]: Abilities.LEVITATE,
|
||||
[Species.SKITTY]: Abilities.SCRAPPY,
|
||||
@ -3217,10 +3267,10 @@ export const starterPassiveAbilities = {
|
||||
[Species.SWABLU]: Abilities.WHITE_SMOKE,
|
||||
[Species.ZANGOOSE]: Abilities.SUPER_LUCK,
|
||||
[Species.SEVIPER]: Abilities.MOLD_BREAKER,
|
||||
[Species.LUNATONE]: Abilities.SHADOW_SHIELD,
|
||||
[Species.SOLROCK]: Abilities.FULL_METAL_BODY,
|
||||
[Species.LUNATONE]: Abilities.FAIRY_AURA,
|
||||
[Species.SOLROCK]: Abilities.DROUGHT,
|
||||
[Species.BARBOACH]: Abilities.BALL_FETCH,
|
||||
[Species.CORPHISH]: Abilities.WATER_BUBBLE,
|
||||
[Species.CORPHISH]: Abilities.TOUGH_CLAWS,
|
||||
[Species.BALTOY]: Abilities.OWN_TEMPO,
|
||||
[Species.LILEEP]: Abilities.WATER_ABSORB,
|
||||
[Species.ANORITH]: Abilities.WATER_ABSORB,
|
||||
@ -3237,7 +3287,7 @@ export const starterPassiveAbilities = {
|
||||
[Species.CLAMPERL]: Abilities.SIMPLE,
|
||||
[Species.RELICANTH]: Abilities.SOLID_ROCK,
|
||||
[Species.LUVDISC]: Abilities.PICKUP,
|
||||
[Species.BAGON]: Abilities.GALE_WINGS,
|
||||
[Species.BAGON]: Abilities.BERSERK,
|
||||
[Species.BELDUM]: Abilities.IRON_FIST,
|
||||
[Species.REGIROCK]: Abilities.REGENERATOR,
|
||||
[Species.REGICE]: Abilities.ICE_SCALES,
|
||||
@ -3251,7 +3301,7 @@ export const starterPassiveAbilities = {
|
||||
[Species.DEOXYS]: Abilities.STICKY_HOLD,
|
||||
[Species.TURTWIG]: Abilities.HARVEST,
|
||||
[Species.CHIMCHAR]: Abilities.DEFIANT,
|
||||
[Species.PIPLUP]: Abilities.BATTLE_ARMOR,
|
||||
[Species.PIPLUP]: Abilities.SLUSH_RUSH,
|
||||
[Species.STARLY]: Abilities.ROCK_HEAD,
|
||||
[Species.BIDOOF]: Abilities.NEUROFORCE,
|
||||
[Species.KRICKETOT]: Abilities.SOUNDPROOF,
|
||||
@ -3286,7 +3336,7 @@ export const starterPassiveAbilities = {
|
||||
[Species.FINNEON]: Abilities.DRIZZLE,
|
||||
[Species.MANTYKE]: Abilities.STORM_DRAIN,
|
||||
[Species.SNOVER]: Abilities.SNOW_CLOAK,
|
||||
[Species.ROTOM]: Abilities.ELECTRIC_SURGE,
|
||||
[Species.ROTOM]: Abilities.MOTOR_DRIVE,
|
||||
[Species.UXIE]: Abilities.ILLUSION,
|
||||
[Species.MESPRIT]: Abilities.MOODY,
|
||||
[Species.AZELF]: Abilities.NEUROFORCE,
|
||||
@ -3304,7 +3354,7 @@ export const starterPassiveAbilities = {
|
||||
[Species.VICTINI]: Abilities.SUPER_LUCK,
|
||||
[Species.SNIVY]: Abilities.MULTISCALE,
|
||||
[Species.TEPIG]: Abilities.SAND_RUSH,
|
||||
[Species.OSHAWOTT]: Abilities.LIGHTNING_ROD,
|
||||
[Species.OSHAWOTT]: Abilities.MOLD_BREAKER,
|
||||
[Species.PATRAT]: Abilities.STAKEOUT,
|
||||
[Species.LILLIPUP]: Abilities.BALL_FETCH,
|
||||
[Species.PURRLOIN]: Abilities.DEFIANT,
|
||||
@ -3325,8 +3375,8 @@ export const starterPassiveAbilities = {
|
||||
[Species.SEWADDLE]: Abilities.SHARPNESS,
|
||||
[Species.VENIPEDE]: Abilities.INTIMIDATE,
|
||||
[Species.COTTONEE]: Abilities.MISTY_SURGE,
|
||||
[Species.PETILIL]: Abilities.ORICHALCUM_PULSE,
|
||||
[Species.BASCULIN]: Abilities.ROCK_HEAD,
|
||||
[Species.PETILIL]: Abilities.DROUGHT,
|
||||
[Species.BASCULIN]: Abilities.OPPORTUNIST,
|
||||
[Species.SANDILE]: Abilities.STRONG_JAW,
|
||||
[Species.DARUMAKA]: Abilities.IRON_FIST,
|
||||
[Species.MARACTUS]: Abilities.IRON_BARBS,
|
||||
@ -3346,14 +3396,14 @@ export const starterPassiveAbilities = {
|
||||
[Species.DEERLING]: Abilities.JUSTIFIED,
|
||||
[Species.EMOLGA]: Abilities.WIND_POWER,
|
||||
[Species.KARRABLAST]: Abilities.NO_GUARD,
|
||||
[Species.FOONGUS]: Abilities.ADAPTABILITY,
|
||||
[Species.FOONGUS]: Abilities.MIMICRY,
|
||||
[Species.FRILLISH]: Abilities.MUMMY,
|
||||
[Species.ALOMOMOLA]: Abilities.MULTISCALE,
|
||||
[Species.JOLTIK]: Abilities.VOLT_ABSORB,
|
||||
[Species.FERROSEED]: Abilities.SKILL_LINK,
|
||||
[Species.KLINK]: Abilities.STEELWORKER,
|
||||
[Species.TYNAMO]: Abilities.SWIFT_SWIM,
|
||||
[Species.ELGYEM]: Abilities.COMMANDER,
|
||||
[Species.ELGYEM]: Abilities.SHADOW_TAG,
|
||||
[Species.LITWICK]: Abilities.SOUL_HEART,
|
||||
[Species.AXEW]: Abilities.SHEER_FORCE,
|
||||
[Species.CUBCHOO]: Abilities.INTIMIDATE,
|
||||
@ -3362,7 +3412,7 @@ export const starterPassiveAbilities = {
|
||||
[Species.STUNFISK]: Abilities.STORM_DRAIN,
|
||||
[Species.MIENFOO]: Abilities.NO_GUARD,
|
||||
[Species.DRUDDIGON]: Abilities.INTIMIDATE,
|
||||
[Species.GOLETT]: Abilities.JUSTIFIED,
|
||||
[Species.GOLETT]: Abilities.SHADOW_SHIELD,
|
||||
[Species.PAWNIARD]: Abilities.SHARPNESS,
|
||||
[Species.BOUFFALANT]: Abilities.THICK_FAT,
|
||||
[Species.RUFFLET]: Abilities.RECKLESS,
|
||||
@ -3380,7 +3430,7 @@ export const starterPassiveAbilities = {
|
||||
[Species.ZEKROM]: Abilities.HADRON_ENGINE,
|
||||
[Species.LANDORUS]: Abilities.PRANKSTER,
|
||||
[Species.KYUREM]: Abilities.SNOW_WARNING,
|
||||
[Species.KELDEO]: Abilities.HUGE_POWER,
|
||||
[Species.KELDEO]: Abilities.SHARPNESS,
|
||||
[Species.MELOETTA]: Abilities.PUNK_ROCK,
|
||||
[Species.GENESECT]: Abilities.MEGA_LAUNCHER,
|
||||
[Species.CHESPIN]: Abilities.IRON_BARBS,
|
||||
@ -3407,7 +3457,7 @@ export const starterPassiveAbilities = {
|
||||
[Species.AMAURA]: Abilities.SERENE_GRACE,
|
||||
[Species.HAWLUCHA]: Abilities.RECKLESS,
|
||||
[Species.DEDENNE]: Abilities.SIMPLE,
|
||||
[Species.CARBINK]: Abilities.OBLIVIOUS,
|
||||
[Species.CARBINK]: Abilities.SOLID_ROCK,
|
||||
[Species.GOOMY]: Abilities.POISON_HEAL,
|
||||
[Species.KLEFKI]: Abilities.TRIAGE,
|
||||
[Species.PHANTUMP]: Abilities.UNNERVE,
|
||||
@ -3425,7 +3475,7 @@ export const starterPassiveAbilities = {
|
||||
[Species.POPPLIO]: Abilities.PUNK_ROCK,
|
||||
[Species.PIKIPEK]: Abilities.ANGER_POINT,
|
||||
[Species.YUNGOOS]: Abilities.HUGE_POWER,
|
||||
[Species.GRUBBIN]: Abilities.GALVANIZE,
|
||||
[Species.GRUBBIN]: Abilities.SPEED_BOOST,
|
||||
[Species.CRABRAWLER]: Abilities.REFRIGERATE,
|
||||
[Species.ORICORIO]: Abilities.ADAPTABILITY,
|
||||
[Species.CUTIEFLY]: Abilities.FRIEND_GUARD,
|
||||
@ -3442,7 +3492,7 @@ export const starterPassiveAbilities = {
|
||||
[Species.COMFEY]: Abilities.FRIEND_GUARD,
|
||||
[Species.ORANGURU]: Abilities.HOSPITALITY,
|
||||
[Species.PASSIMIAN]: Abilities.COSTAR,
|
||||
[Species.WIMPOD]: Abilities.BATTLE_ARMOR,
|
||||
[Species.WIMPOD]: Abilities.TINTED_LENS,
|
||||
[Species.SANDYGAST]: Abilities.DAUNTLESS_SHIELD,
|
||||
[Species.PYUKUMUKU]: Abilities.IRON_BARBS,
|
||||
[Species.TYPE_NULL]: Abilities.ADAPTABILITY,
|
||||
@ -3465,7 +3515,7 @@ export const starterPassiveAbilities = {
|
||||
[Species.PHEROMOSA]: Abilities.MOXIE,
|
||||
[Species.XURKITREE]: Abilities.LIGHTNING_ROD,
|
||||
[Species.CELESTEELA]: Abilities.CHLOROPHYLL,
|
||||
[Species.KARTANA]: Abilities.INTREPID_SWORD,
|
||||
[Species.KARTANA]: Abilities.SHARPNESS,
|
||||
[Species.GUZZLORD]: Abilities.GLUTTONY,
|
||||
[Species.NECROZMA]: Abilities.BEAST_BOOST,
|
||||
[Species.MAGEARNA]: Abilities.STEELY_SPIRIT,
|
||||
@ -3479,7 +3529,7 @@ export const starterPassiveAbilities = {
|
||||
[Species.SCORBUNNY]: Abilities.RECKLESS,
|
||||
[Species.SOBBLE]: Abilities.MIMICRY,
|
||||
[Species.SKWOVET]: Abilities.HONEY_GATHER,
|
||||
[Species.ROOKIDEE]: Abilities.JUSTIFIED,
|
||||
[Species.ROOKIDEE]: Abilities.IRON_BARBS,
|
||||
[Species.BLIPBUG]: Abilities.TINTED_LENS,
|
||||
[Species.NICKIT]: Abilities.INTIMIDATE,
|
||||
[Species.GOSSIFLEUR]: Abilities.STORM_DRAIN,
|
||||
@ -3512,7 +3562,7 @@ export const starterPassiveAbilities = {
|
||||
[Species.ARCTOVISH]: Abilities.STRONG_JAW,
|
||||
[Species.DURALUDON]: Abilities.MEGA_LAUNCHER,
|
||||
[Species.DREEPY]: Abilities.PARENTAL_BOND,
|
||||
[Species.ZACIAN]: Abilities.SHARPNESS,
|
||||
[Species.ZACIAN]: Abilities.GUARD_DOG,
|
||||
[Species.ZAMAZENTA]: Abilities.GUARD_DOG,
|
||||
[Species.ETERNATUS]: Abilities.SUPREME_OVERLORD,
|
||||
[Species.KUBFU]: Abilities.IRON_FIST,
|
||||
@ -3535,7 +3585,7 @@ export const starterPassiveAbilities = {
|
||||
[Species.SMOLIV]: Abilities.RIPEN,
|
||||
[Species.SQUAWKABILLY]: Abilities.GALE_WINGS,
|
||||
[Species.NACLI]: Abilities.EARTH_EATER,
|
||||
[Species.CHARCADET]: Abilities.CONTRARY,
|
||||
[Species.CHARCADET]: Abilities.MIRROR_ARMOR,
|
||||
[Species.TADBULB]: Abilities.TRANSISTOR,
|
||||
[Species.WATTREL]: Abilities.GALE_WINGS,
|
||||
[Species.MASCHIFF]: Abilities.STRONG_JAW,
|
||||
@ -3562,15 +3612,15 @@ export const starterPassiveAbilities = {
|
||||
[Species.TATSUGIRI]: Abilities.WATER_BUBBLE,
|
||||
[Species.GREAT_TUSK]: Abilities.INTIMIDATE,
|
||||
[Species.SCREAM_TAIL]: Abilities.PIXILATE,
|
||||
[Species.BRUTE_BONNET]: Abilities.ADAPTABILITY,
|
||||
[Species.BRUTE_BONNET]: Abilities.BEAST_BOOST,
|
||||
[Species.FLUTTER_MANE]: Abilities.DAZZLING,
|
||||
[Species.SLITHER_WING]: Abilities.SCRAPPY,
|
||||
[Species.SLITHER_WING]: Abilities.MOXIE,
|
||||
[Species.SANDY_SHOCKS]: Abilities.EARTH_EATER,
|
||||
[Species.IRON_TREADS]: Abilities.STEAM_ENGINE,
|
||||
[Species.IRON_TREADS]: Abilities.BULLETPROOF,
|
||||
[Species.IRON_BUNDLE]: Abilities.SNOW_WARNING,
|
||||
[Species.IRON_HANDS]: Abilities.IRON_FIST,
|
||||
[Species.IRON_JUGULIS]: Abilities.NO_GUARD,
|
||||
[Species.IRON_MOTH]: Abilities.TINTED_LENS,
|
||||
[Species.IRON_MOTH]: Abilities.LEVITATE,
|
||||
[Species.IRON_THORNS]: Abilities.SAND_STREAM,
|
||||
[Species.FRIGIBAX]: Abilities.THICK_FAT,
|
||||
[Species.GIMMIGHOUL]: Abilities.SUPER_LUCK,
|
||||
@ -3585,7 +3635,7 @@ export const starterPassiveAbilities = {
|
||||
[Species.WALKING_WAKE]: Abilities.BEAST_BOOST,
|
||||
[Species.IRON_LEAVES]: Abilities.SHARPNESS,
|
||||
[Species.POLTCHAGEIST]: Abilities.FLAME_BODY,
|
||||
[Species.OKIDOGI]: Abilities.STICKY_HOLD,
|
||||
[Species.OKIDOGI]: Abilities.INTIMIDATE,
|
||||
[Species.MUNKIDORI]: Abilities.PRANKSTER,
|
||||
[Species.FEZANDIPITI]: Abilities.DAZZLING,
|
||||
[Species.OGERPON]: Abilities.DISGUISE,
|
||||
|
@ -79,7 +79,7 @@ export const trainerNamePools = {
|
||||
[TrainerType.BEAUTY]: ["Cassie","Julia","Olivia","Samantha","Valerie","Victoria","Bridget","Connie","Jessica","Johanna","Melissa","Sheila","Shirley","Tiffany","Namiko","Thalia","Grace","Lola","Lori","Maura","Tamia","Cyndy","Devon","Gabriella","Harley","Lindsay","Nicola","Callie","Charlotte","Kassandra","December","Fleming","Nikola","Aimee","Anais","Brigitte","Cassandra","Andrea","Brittney","Carolyn","Krystal","Alexis","Alice","Aina","Anya","Arianna","Aubrey","Beverly","Camille","Beauty","Evette","Hansol","Haruka","Jill","Jo","Lana","Lois","Lucy","Mai","Nickie","Nicole","Prita","Rose","Shelly","Suzy","Tessa","Anita","Alissa","Rita","Cudsy","Eloff","Miru","Minot","Nevah","Niven","Ogoin"],
|
||||
[TrainerType.BIKER]: ["Charles","Dwayne","Glenn","Harris","Joel","Riley","Zeke","Alex","Billy","Ernest","Gerald","Hideo","Isaac","Jared","Jaren","Jaxon","Jordy","Lao","Lukas","Malik","Nikolas","Ricardo","Ruben","Virgil","William","Aiden","Dale","Dan","Jacob","Markey","Reese","Teddy","Theron","Jeremy","Morgann","Phillip","Philip","Stanley","Dillon"],
|
||||
[TrainerType.BLACK_BELT]: [["Kenji","Lao","Lung","Nob","Wai","Yoshi","Atsushi","Daisuke","Hideki","Hitoshi","Kiyo","Koichi","Koji","Yuji","Cristian","Rhett","Takao","Theodore","Zander","Aaron","Hugh","Mike","Nicolas","Shea","Takashi","Adam","Carl","Colby","Darren","David","Davon","Derek","Eddie","Gregory","Griffin","Jarrett","Jeffery","Kendal","Kyle","Luke","Miles","Nathaniel","Philip","Rafael","Ray","Ricky","Sean","Willie","Ander","Manford","Benjamin","Corey","Edward","Grant","Jay","Kendrew","Kentaro","Ryder","Teppei","Thomas","Tyrone","Andrey","Donny","Drago","Gordon","Grigor","Jeriel","Kenneth","Martell","Mathis","Rich","Rocky","Rodrigo","Wesley","Zachery","Alonzo","Cadoc","Gunnar","Igor","Killian","Markus","Ricardo","Yanis","Banting","Clayton","Duane","Earl","Greg","Roy","Terry","Tracy","Walter","Alvaro","Curtis","Francis","Ross","Brice","Cheng","Dudley","Eric","Kano","Masahiro","Randy","Ryuji","Steve","Tadashi","Wong","Yuen","Brian","Carter","Reece","Nick","Yang"],["Cora","Cyndy","Jill","Laura","Sadie","Tessa","Vivian","Aisha","Callie","Danielle","Helene","Jocelyn","Lilith","Paula","Reyna","Helen","Kelsey","Tyler","Amy","Chandra","Hillary","Janie","Lee","Maggie","Mikiko","Miriam","Sharon","Susie","Xiao","Alize","Azra","Brenda","Chalina","Chan","Glinda","Maki","Tia","Tiffany","Wendy","Andrea","Gabrielle","Gerardine","Hailey","Hedvig","Justine","Kinsey","Sigrid","Veronique","Tess"]],
|
||||
[TrainerType.BREEDER]: [["Isaac","Myles","Salvadore","Allison","Alize","Bethany","Lily","Albert","Kahlil","Eustace","Galen","Owen","Addison","Marcus","Foster","Cory","Glenn","Jay","Wesley","William","Adrian","Bradley","Jaime"],["Lydia","Gabrielle","Jayden","Pat","Veronica","Amber","Jennifer","Kaylee","Adelaide","Brooke","Ethel","April","Irene","Magnolia","Amala","Mercy","Amanda","Ikue","Savannah","Yuka","Chloe","Debra","Denise","Elena"]],
|
||||
[TrainerType.BREEDER]: [["Isaac","Myles","Salvadore","Albert","Kahlil","Eustace","Galen","Owen","Addison","Marcus","Foster","Cory","Glenn","Jay","Wesley","William","Adrian","Bradley","Jaime"],["Allison","Alize","Bethany","Lily","Lydia","Gabrielle","Jayden","Pat","Veronica","Amber","Jennifer","Kaylee","Adelaide","Brooke","Ethel","April","Irene","Magnolia","Amala","Mercy","Amanda","Ikue","Savannah","Yuka","Chloe","Debra","Denise","Elena"]],
|
||||
[TrainerType.CLERK]: [["Chaz","Clemens","Doug","Fredric","Ivan","Isaac","Nelson","Wade","Warren","Augustin","Gilligan","Cody","Jeremy","Shane","Dugal","Royce","Ronald"],["Alberta","Ingrid","Katie","Piper","Trisha","Wren","Britney","Lana","Jessica","Kristen","Michelle","Gabrielle"]],
|
||||
[TrainerType.CYCLIST]: [["Axel","James","John","Ryan","Hector","Jeremiah"],["Kayla","Megan","Nicole","Rachel","Krissa","Adelaide"]],
|
||||
[TrainerType.DANCER]: ["Brian","Davey","Dirk","Edmond","Mickey","Raymond","Cara","Julia","Maika","Mireille","Ronda","Zoe"],
|
||||
|
@ -108,7 +108,7 @@ export class Weather {
|
||||
for (let pokemon of field) {
|
||||
let suppressWeatherEffectAbAttr = pokemon.getAbility().getAttrs(SuppressWeatherEffectAbAttr).find(() => true) as SuppressWeatherEffectAbAttr;
|
||||
if (!suppressWeatherEffectAbAttr)
|
||||
suppressWeatherEffectAbAttr = pokemon.canApplyPassive() ? pokemon.getPassiveAbility().getAttrs(SuppressWeatherEffectAbAttr).find(() => true) as SuppressWeatherEffectAbAttr : null;
|
||||
suppressWeatherEffectAbAttr = pokemon.hasPassive() ? pokemon.getPassiveAbility().getAttrs(SuppressWeatherEffectAbAttr).find(() => true) as SuppressWeatherEffectAbAttr : null;
|
||||
if (suppressWeatherEffectAbAttr && (!this.isImmutable() || suppressWeatherEffectAbAttr.affectsImmutable))
|
||||
return true;
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ export function getData() {
|
||||
const dataStr = localStorage.getItem('data');
|
||||
if (!dataStr)
|
||||
return null;
|
||||
return JSON.parse(atob(dataStr), (k, v) => k.endsWith('Attr') ? BigInt(v) : v);
|
||||
return JSON.parse(atob(dataStr), (k, v) => k.endsWith('Attr') && ![ 'natureAttr', 'passiveAttr', 'variantAttr' ].includes(k) ? BigInt(v) : v);
|
||||
}
|
||||
|
||||
export function getSession() {
|
||||
|
@ -166,6 +166,20 @@ export class Arena {
|
||||
return 2;
|
||||
}
|
||||
break;
|
||||
case Species.ROTOM:
|
||||
switch (this.biomeType) {
|
||||
case Biome.VOLCANO:
|
||||
return 1;
|
||||
case Biome.SEA:
|
||||
return 2;
|
||||
case Biome.ICE_CAVE:
|
||||
return 3;
|
||||
case Biome.MOUNTAIN:
|
||||
return 4;
|
||||
case Biome.TALL_GRASS:
|
||||
return 5;
|
||||
}
|
||||
break;
|
||||
case Species.SCATTERBUG:
|
||||
case Species.SPEWPA:
|
||||
case Species.VIVILLON:
|
||||
@ -337,7 +351,7 @@ export class Arena {
|
||||
weatherMultiplier = this.weather.getAttackTypeMultiplier(attackType);
|
||||
|
||||
let terrainMultiplier = 1;
|
||||
if (this.terrain && !grounded)
|
||||
if (this.terrain && grounded)
|
||||
terrainMultiplier = this.terrain.getAttackTypeMultiplier(attackType);
|
||||
|
||||
return weatherMultiplier * terrainMultiplier;
|
||||
@ -527,6 +541,16 @@ export class Arena {
|
||||
return !!tag;
|
||||
}
|
||||
|
||||
removeTagOnSide(tagType: ArenaTagType, side: ArenaTagSide): boolean {
|
||||
const tag = this.getTagOnSide(tagType, side);
|
||||
if (tag) {
|
||||
tag.onRemove(this);
|
||||
this.tags.splice(this.tags.indexOf(tag), 1);
|
||||
}
|
||||
return !!tag;
|
||||
}
|
||||
|
||||
|
||||
removeAllTags(): void {
|
||||
while (this.tags.length) {
|
||||
this.tags[0].onRemove(this);
|
||||
|
@ -25,7 +25,7 @@ import { TempBattleStat } from '../data/temp-battle-stat';
|
||||
import { ArenaTagSide, WeakenMoveScreenTag, WeakenMoveTypeTag } from '../data/arena-tag';
|
||||
import { ArenaTagType } from "../data/enums/arena-tag-type";
|
||||
import { Biome } from "../data/enums/biome";
|
||||
import { Ability, BattleStatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldVariableMovePowerAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, MoveTypeChangeAttr, NonSuperEffectiveImmunityAbAttr, PreApplyBattlerTagAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, VariableMoveTypeAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPostDefendAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs } from '../data/ability';
|
||||
import { Ability, AbAttr, BattleStatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldVariableMovePowerAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, MoveTypeChangeAttr, NonSuperEffectiveImmunityAbAttr, PreApplyBattlerTagAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, VariableMoveTypeAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPostDefendAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs } from '../data/ability';
|
||||
import { Abilities } from "#app/data/enums/abilities";
|
||||
import PokemonData from '../system/pokemon-data';
|
||||
import { BattlerIndex } from '../battle';
|
||||
@ -55,7 +55,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
public species: PokemonSpecies;
|
||||
public formIndex: integer;
|
||||
public abilityIndex: integer;
|
||||
public passive: boolean;
|
||||
public shiny: boolean;
|
||||
public variant: integer;
|
||||
public pokeball: PokeballType;
|
||||
protected battleInfo: BattleInfo;
|
||||
public level: integer;
|
||||
@ -127,6 +129,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
this.hp = dataSource.hp;
|
||||
this.stats = dataSource.stats;
|
||||
this.ivs = dataSource.ivs;
|
||||
this.passive = !!dataSource.passive;
|
||||
this.variant = dataSource.variant || 0;
|
||||
this.nature = dataSource.nature || 0 as Nature;
|
||||
this.natureOverride = dataSource.natureOverride !== undefined ? dataSource.natureOverride : -1;
|
||||
this.moveset = dataSource.moveset;
|
||||
@ -557,7 +561,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
let value = Math.floor(((2 * baseStat + this.ivs[s]) * this.level) * 0.01);
|
||||
if (isHp) {
|
||||
value = value + this.level + 10;
|
||||
if ((this.canApplyAbility() && this.getAbility().hasAttr(NonSuperEffectiveImmunityAbAttr)) || (this.canApplyAbility(true) && this.getPassiveAbility().hasAttr(NonSuperEffectiveImmunityAbAttr)))
|
||||
if (this.hasAbility(Abilities.WONDER_GUARD, false, true))
|
||||
value = 1;
|
||||
if (this.hp > value || this.hp === undefined)
|
||||
this.hp = value;
|
||||
@ -712,8 +716,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
if (OPP_ABILITY_OVERRIDE && !this.isPlayer())
|
||||
return allAbilities[OPP_ABILITY_OVERRIDE];
|
||||
if (this.isFusion())
|
||||
return allAbilities[this.getFusionSpeciesForm().getAbility(this.fusionAbilityIndex)];
|
||||
let abilityId = this.getSpeciesForm().getAbility(this.abilityIndex);
|
||||
return allAbilities[this.getFusionSpeciesForm(ignoreOverride).getAbility(this.fusionAbilityIndex)];
|
||||
let abilityId = this.getSpeciesForm(ignoreOverride).getAbility(this.abilityIndex);
|
||||
if (abilityId === Abilities.NONE)
|
||||
abilityId = this.species.ability1;
|
||||
return allAbilities[abilityId];
|
||||
@ -726,12 +730,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
return allAbilities[starterPassiveAbilities[starterSpeciesId]];
|
||||
}
|
||||
|
||||
canApplyPassive(): boolean {
|
||||
return this.isBoss();
|
||||
hasPassive(): boolean {
|
||||
return this.passive || this.isBoss();
|
||||
}
|
||||
|
||||
canApplyAbility(passive: boolean = false): boolean {
|
||||
if (passive && !this.canApplyPassive())
|
||||
if (passive && !this.hasPassive())
|
||||
return false;
|
||||
const ability = (!passive ? this.getAbility() : this.getPassiveAbility());
|
||||
if (ability.isIgnorable && this.scene.arena.ignoreAbilities)
|
||||
@ -739,6 +743,22 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
return (this.hp || ability.isBypassFaint) && !ability.conditions.find(condition => !condition(this));
|
||||
}
|
||||
|
||||
hasAbility(ability: Abilities, canApply: boolean = true, ignoreOverride?: boolean): boolean {
|
||||
if ((!canApply || this.canApplyAbility()) && this.getAbility(ignoreOverride).id === ability)
|
||||
return true;
|
||||
if (this.hasPassive() && (!canApply || this.canApplyAbility(true)) && this.getPassiveAbility().id === ability)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
hasAbilityWithAttr(attrType: { new(...args: any[]): AbAttr }, canApply: boolean = true, ignoreOverride?: boolean): boolean {
|
||||
if ((!canApply || this.canApplyAbility()) && this.getAbility(ignoreOverride).hasAttr(attrType))
|
||||
return true;
|
||||
if (this.hasPassive() && (!canApply || this.canApplyAbility(true)) && this.getPassiveAbility().hasAttr(attrType))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
getWeight(): number {
|
||||
const weight = new Utils.NumberHolder(this.species.weight);
|
||||
// This will trigger the ability overlay so only call this function when necessary
|
||||
@ -846,10 +866,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
return true;
|
||||
});
|
||||
} else
|
||||
levelMoves = this.getSpeciesForm().getLevelMoves();
|
||||
levelMoves = this.getSpeciesForm(true).getLevelMoves();
|
||||
if (this.fusionSpecies) {
|
||||
const evolutionLevelMoves = levelMoves.slice(0, Math.max(levelMoves.findIndex(lm => !!lm[0]), 0));
|
||||
const fusionLevelMoves = this.getFusionSpeciesForm().getLevelMoves();
|
||||
const fusionLevelMoves = this.getFusionSpeciesForm(true).getLevelMoves();
|
||||
const newLevelMoves: LevelMoves = [];
|
||||
while (levelMoves.length && levelMoves[0][0] < startingLevel)
|
||||
levelMoves.shift();
|
||||
@ -1160,11 +1180,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
else {
|
||||
if (source.findTag(t => t instanceof TypeBoostTag && (t as TypeBoostTag).boostedType === type))
|
||||
power.value *= 1.5;
|
||||
const arenaAttackTypeMultiplier = this.scene.arena.getAttackTypeMultiplier(type, this.isGrounded());
|
||||
const arenaAttackTypeMultiplier = this.scene.arena.getAttackTypeMultiplier(type, source.isGrounded());
|
||||
if (this.scene.arena.getTerrainType() === TerrainType.GRASSY && this.isGrounded() && type === Type.GROUND && move.moveTarget === MoveTarget.ALL_NEAR_OTHERS)
|
||||
power.value /= 2;
|
||||
else if (this.scene.arena.getTerrainType() === TerrainType.ELECTRIC && source.isGrounded() && type === Type.ELECTRIC)
|
||||
power.value *= 1.3;
|
||||
applyMoveAttrs(VariablePowerAttr, source, this, move, power);
|
||||
this.scene.applyModifiers(PokemonMultiHitModifier, source.isPlayer(), source, new Utils.IntegerHolder(0), power);
|
||||
if (!typeless) {
|
||||
@ -1184,7 +1202,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
applyMoveAttrs(HighCritAttr, source, this, move, critLevel);
|
||||
this.scene.applyModifiers(TempBattleStatBoosterModifier, source.isPlayer(), TempBattleStat.CRIT, critLevel);
|
||||
const bonusCrit = new Utils.BooleanHolder(false);
|
||||
if (applyAbAttrs(BonusCritAbAttr, this, null, bonusCrit)) {
|
||||
if (applyAbAttrs(BonusCritAbAttr, source, null, bonusCrit)) {
|
||||
if (bonusCrit.value)
|
||||
critLevel.value += 1;
|
||||
}
|
||||
@ -1240,8 +1258,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
if (this.scene.arena.terrain?.terrainType === TerrainType.MISTY && this.isGrounded() && type === Type.DRAGON)
|
||||
damage.value = Math.floor(damage.value / 2);
|
||||
|
||||
applyMoveAttrs(ModifiedDamageAttr, source, this, move, damage);
|
||||
|
||||
const fixedDamage = new Utils.IntegerHolder(0);
|
||||
applyMoveAttrs(FixedDamageAttr, source, this, move, fixedDamage);
|
||||
if (!isTypeImmune && fixedDamage.value) {
|
||||
@ -1250,8 +1266,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
result = HitResult.EFFECTIVE;
|
||||
}
|
||||
|
||||
console.log('damage', damage.value, move.name, power.value, sourceAtk, targetDef);
|
||||
|
||||
if (!result) {
|
||||
if (!typeMultiplier.value)
|
||||
result = HitResult.NO_EFFECT;
|
||||
@ -1278,6 +1292,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
this.scene.applyModifiers(EnemyDamageReducerModifier, false, damage);
|
||||
}
|
||||
|
||||
applyMoveAttrs(ModifiedDamageAttr, source, this, move, damage);
|
||||
|
||||
console.log('damage', damage.value, move.name, power.value, sourceAtk, targetDef);
|
||||
|
||||
if (damage.value) {
|
||||
if (this.getHpRatio() === 1)
|
||||
applyPreDefendAbAttrs(PreDefendFullHpEndureAbAttr, this, source, battlerMove, cancelled, damage);
|
||||
@ -2159,7 +2177,7 @@ export class PlayerPokemon extends Pokemon {
|
||||
}
|
||||
|
||||
tryPopulateMoveset(moveset: StarterMoveset): boolean {
|
||||
if (!this.getSpeciesForm().validateStarterMoveset(moveset, this.scene.gameData.starterEggMoveData[this.species.getRootSpeciesId()]))
|
||||
if (!this.getSpeciesForm().validateStarterMoveset(moveset, this.scene.gameData.starterData[this.species.getRootSpeciesId()].eggMoves))
|
||||
return false;
|
||||
|
||||
this.moveset = moveset.map(m => new PokemonMove(m));
|
||||
@ -2258,6 +2276,7 @@ export class PlayerPokemon extends Pokemon {
|
||||
if (newEvolution.condition.predicate(this)) {
|
||||
const newPokemon = this.scene.addPlayerPokemon(this.species, this.level, this.abilityIndex, this.formIndex, this.gender, this.shiny, this.ivs, this.nature);
|
||||
newPokemon.natureOverride = this.natureOverride;
|
||||
newPokemon.moveset = this.moveset.slice();
|
||||
newPokemon.fusionSpecies = this.fusionSpecies;
|
||||
newPokemon.fusionFormIndex = this.fusionFormIndex;
|
||||
newPokemon.fusionAbilityIndex = this.fusionAbilityIndex;
|
||||
@ -2817,6 +2836,7 @@ export class PokemonBattleSummonData {
|
||||
|
||||
export class PokemonTurnData {
|
||||
public flinched: boolean;
|
||||
public acted: boolean;
|
||||
public hitCount: integer;
|
||||
public hitsLeft: integer;
|
||||
public damageDealt: integer = 0;
|
||||
|
@ -3,6 +3,7 @@ import { Biome } from "./data/enums/biome";
|
||||
import { TrainerType } from "./data/enums/trainer-type";
|
||||
import { trainerConfigs } from "./data/trainer-config";
|
||||
import { getBiomeHasProps } from "./field/arena";
|
||||
import CacheBustedLoaderPlugin from "./plugins/cache-busted-loader-plugin";
|
||||
import { SceneBase } from "./scene-base";
|
||||
import { WindowVariant, getWindowVariantSuffix } from "./ui/ui-theme";
|
||||
import * as Utils from "./utils";
|
||||
@ -10,6 +11,8 @@ import * as Utils from "./utils";
|
||||
export class LoadingScene extends SceneBase {
|
||||
constructor() {
|
||||
super('loading');
|
||||
|
||||
Phaser.Plugins.PluginCache.register('Loader', CacheBustedLoaderPlugin, 'load');
|
||||
}
|
||||
|
||||
preload() {
|
||||
@ -58,6 +61,7 @@ export class LoadingScene extends SceneBase {
|
||||
this.loadImage('achv_bar_3', 'ui');
|
||||
this.loadImage('achv_bar_4', 'ui');
|
||||
this.loadImage('shiny_star', 'ui', 'shiny.png');
|
||||
this.loadImage('ha_capsule', 'ui', 'ha_capsule.png');
|
||||
this.loadImage('icon_spliced', 'ui');
|
||||
this.loadImage('icon_tera', 'ui');
|
||||
this.loadImage('type_tera', 'ui');
|
||||
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* The menu namespace holds most miscellaneous text that isn't directly part of the game's
|
||||
* contents or directly related to Pokemon. This includes menu navigation, settings,
|
||||
* account interactions, etc.
|
||||
* contents or directly related to Pokemon data. This includes menu navigation, settings,
|
||||
* account interactions, descriptive text, etc.
|
||||
*/
|
||||
export const menu = {
|
||||
"cancel": "Cancel",
|
||||
@ -9,5 +9,36 @@ export const menu = {
|
||||
"dailyRun": "Daily Run (Beta)",
|
||||
"loadGame": "Load Game",
|
||||
"newGame": "New Game",
|
||||
"selectGameMode": "Select a game mode."
|
||||
"selectGameMode": "Select a game mode.",
|
||||
"logInOrCreateAccount": "Log in or create an account to start. No email required!",
|
||||
"failedToLoadSaveData": "Failed to load save data. Please reload the page.\nIf this continues, please contact the administrator.",
|
||||
"sessionSuccess": "Session loaded successfully.",
|
||||
"failedToLoadSession": "Your session data could not be loaded.\nIt may be corrupted.",
|
||||
"boyOrGirl": "Are you a boy or a girl?",
|
||||
"boy": "Boy",
|
||||
"girl": "Girl",
|
||||
"bossAppeared": "{{bossName}} appeared.",
|
||||
"trainerAppeared": "{{trainerName}}\nwould like to battle!",
|
||||
"singleWildAppeared": "A wild {{pokemonName}} appeared!",
|
||||
"multiWildAppeared": "A wild {{pokemonName1}}\nand {{pokemonName2}} appeared!",
|
||||
"playerComeBack": "Come back, {{pokemonName}}!",
|
||||
"trainerComeBack": "{{trainerName}} withdrew {{pokemonName}}!",
|
||||
"playerGo": "Go! {{pokemonName}}!",
|
||||
"trainerGo": "{{trainerName}} sent out {{pokemonName}}!",
|
||||
"switchQuestion": "Will you switch\n{{pokemonName}}?",
|
||||
"pokemon": "Pokémon",
|
||||
"sendOutPokemon": "Go! {{pokemonName}}!",
|
||||
"levelCapUp": "The level cap\nhas increased to {{levelCap}}!",
|
||||
"moveNotImplemented": "{{moveName}} is not yet implemented and cannot be selected.",
|
||||
"moveDisabled": "{{moveName}} is disabled!",
|
||||
"noPokeballForce": "An unseen force\nprevents using Poké Balls.",
|
||||
"noPokeballTrainer": "You can't catch\nanother trainer's Pokémon!",
|
||||
"noPokeballMulti": "You can only throw a Poké Ball\nwhen there is one Pokémon remaining!",
|
||||
"noPokeballStrong": "The target Pokémon is too strong to be caught!\nYou need to weaken it first!",
|
||||
"noEscapeForce": "An unseen force\nprevents escape.",
|
||||
"noEscapeTrainer": "You can't run\nfrom a trainer battle!",
|
||||
"noEscapePokemon": "{{pokemonName}}'s {{moveName}}\nprevents {{escapeVerb}}!",
|
||||
"escapeVerbSwitch": "switching",
|
||||
"escapeVerbFlee": "fleeing",
|
||||
"notDisabled": "{{moveName}} is disabled\nno more!",
|
||||
} as const;
|
@ -1341,7 +1341,7 @@ export function getPlayerShopModifierTypeOptionsForWave(waveIndex: integer, base
|
||||
new ModifierTypeOption(modifierTypes.FULL_RESTORE(), 0, baseCost * 2.25)
|
||||
],
|
||||
[
|
||||
new ModifierTypeOption(modifierTypes.SACRED_ASH(), 0, baseCost * 12)
|
||||
new ModifierTypeOption(modifierTypes.SACRED_ASH(), 0, baseCost * 10)
|
||||
]
|
||||
];
|
||||
return options.slice(0, Math.ceil(Math.max(waveIndex + 10, 0) / 30)).flat();
|
||||
|
159
src/phases.ts
@ -2,7 +2,7 @@ import BattleScene, { STARTER_FORM_OVERRIDE, STARTER_SPECIES_OVERRIDE, bypassLog
|
||||
import { default as Pokemon, PlayerPokemon, EnemyPokemon, PokemonMove, MoveResult, DamageResult, FieldPosition, HitResult, TurnMove } from "./field/pokemon";
|
||||
import * as Utils from './utils';
|
||||
import { Moves } from "./data/enums/moves";
|
||||
import { allMoves, applyMoveAttrs, BypassSleepAttr, ChargeAttr, applyFilteredMoveAttrs, HitsTagAttr, MissEffectAttr, MoveAttr, MoveEffectAttr, MoveFlags, MultiHitAttr, OverrideMoveEffectAttr, VariableAccuracyAttr, MoveTarget, OneHitKOAttr, getMoveTargets, MoveTargetSet, MoveEffectTrigger, CopyMoveAttr, AttackMove, SelfStatusMove, DelayedAttackAttr, RechargeAttr, PreMoveMessageAttr, HealStatusEffectAttr, IgnoreOpponentStatChangesAttr } from "./data/move";
|
||||
import { allMoves, applyMoveAttrs, BypassSleepAttr, ChargeAttr, applyFilteredMoveAttrs, HitsTagAttr, MissEffectAttr, MoveAttr, MoveEffectAttr, MoveFlags, MultiHitAttr, OverrideMoveEffectAttr, VariableAccuracyAttr, MoveTarget, OneHitKOAttr, getMoveTargets, MoveTargetSet, MoveEffectTrigger, CopyMoveAttr, AttackMove, SelfStatusMove, DelayedAttackAttr, RechargeAttr, PreMoveMessageAttr, HealStatusEffectAttr, IgnoreOpponentStatChangesAttr, NoEffectAttr, FixedDamageAttr } from "./data/move";
|
||||
import { Mode } from './ui/ui';
|
||||
import { Command } from "./ui/command-ui-handler";
|
||||
import { Stat } from "./data/pokemon-stat";
|
||||
@ -30,7 +30,7 @@ import { Weather, WeatherType, getRandomWeatherType, getTerrainBlockMessage, get
|
||||
import { TempBattleStat } from "./data/temp-battle-stat";
|
||||
import { ArenaTagSide, ArenaTrapTag, MistTag, TrickRoomTag } from "./data/arena-tag";
|
||||
import { ArenaTagType } from "./data/enums/arena-tag-type";
|
||||
import { CheckTrappedAbAttr, MoveAbilityBypassAbAttr, IgnoreOpponentStatChangesAbAttr, PostAttackAbAttr, PostBattleAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreSwitchOutAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, RedirectMoveAbAttr, RunSuccessAbAttr, StatChangeMultiplierAbAttr, SuppressWeatherEffectAbAttr, SyncEncounterNatureAbAttr, applyAbAttrs, applyCheckTrappedAbAttrs, applyPostAttackAbAttrs, applyPostBattleAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreSwitchOutAbAttrs, applyPreWeatherEffectAbAttrs, BattleStatMultiplierAbAttr, applyBattleStatMultiplierAbAttrs, IncrementMovePriorityAbAttr, applyPostVictoryAbAttrs, PostVictoryAbAttr, applyPostBattleInitAbAttrs, PostBattleInitAbAttr, BlockNonDirectDamageAbAttr as BlockNonDirectDamageAbAttr, applyPostKnockOutAbAttrs, PostKnockOutAbAttr, PostBiomeChangeAbAttr, applyPostFaintAbAttrs, PostFaintAbAttr, IncreasePpAbAttr } from "./data/ability";
|
||||
import { CheckTrappedAbAttr, MoveAbilityBypassAbAttr, IgnoreOpponentStatChangesAbAttr, PostAttackAbAttr, PostBattleAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreSwitchOutAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, RedirectMoveAbAttr, RunSuccessAbAttr, StatChangeMultiplierAbAttr, SuppressWeatherEffectAbAttr, SyncEncounterNatureAbAttr, applyAbAttrs, applyCheckTrappedAbAttrs, applyPostAttackAbAttrs, applyPostBattleAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreSwitchOutAbAttrs, applyPreWeatherEffectAbAttrs, BattleStatMultiplierAbAttr, applyBattleStatMultiplierAbAttrs, IncrementMovePriorityAbAttr, applyPostVictoryAbAttrs, PostVictoryAbAttr, applyPostBattleInitAbAttrs, PostBattleInitAbAttr, BlockNonDirectDamageAbAttr as BlockNonDirectDamageAbAttr, applyPostKnockOutAbAttrs, PostKnockOutAbAttr, PostBiomeChangeAbAttr, applyPostFaintAbAttrs, PostFaintAbAttr, IncreasePpAbAttr, PostStatChangeAbAttr, applyPostStatChangeAbAttrs } from "./data/ability";
|
||||
import { Abilities } from "./data/enums/abilities";
|
||||
import { Unlockables, getUnlockableName } from "./system/unlockables";
|
||||
import { getBiomeKey } from "./field/arena";
|
||||
@ -44,7 +44,7 @@ import { EggHatchPhase } from "./egg-hatch-phase";
|
||||
import { Egg } from "./data/egg";
|
||||
import { vouchers } from "./system/voucher";
|
||||
import { loggedInUser, updateUserInfo } from "./account";
|
||||
import { GameDataType, PlayerGender, SessionSaveData } from "./system/game-data";
|
||||
import { DexAttr, GameDataType, PlayerGender, SessionSaveData } from "./system/game-data";
|
||||
import { addPokeballCaptureStars, addPokeballOpenParticles } from "./field/anims";
|
||||
import { SpeciesFormChangeActiveTrigger, SpeciesFormChangeManualTrigger, SpeciesFormChangeMoveLearnedTrigger, SpeciesFormChangePostMoveTrigger, SpeciesFormChangePreMoveTrigger } from "./data/pokemon-forms";
|
||||
import { battleSpecDialogue, getCharVariantFromDialogue } from "./data/dialogue";
|
||||
@ -80,7 +80,7 @@ export class LoginPhase extends Phase {
|
||||
if (!success) {
|
||||
if (!statusCode || statusCode === 400) {
|
||||
if (this.showText)
|
||||
this.scene.ui.showText('Log in or create an account to start. No email required!');
|
||||
this.scene.ui.showText(i18next.t('menu:logInOrCreateAccount'));
|
||||
|
||||
this.scene.playSound('menu_open');
|
||||
|
||||
@ -120,7 +120,7 @@ export class LoginPhase extends Phase {
|
||||
this.end();
|
||||
else {
|
||||
this.scene.ui.setMode(Mode.MESSAGE);
|
||||
this.scene.ui.showText('Failed to load save data. Please reload the page.\nIf this continues, please contact the administrator.');
|
||||
this.scene.ui.showText(i18next.t('menu:failedToLoadSaveData'));
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -175,7 +175,10 @@ export class TitlePhase extends Phase {
|
||||
if (loggedInUser.lastSessionSlot > -1) {
|
||||
options.push({
|
||||
label: i18next.t('menu:continue'),
|
||||
handler: () => this.loadSaveSlot(this.lastSessionData ? -1 : loggedInUser.lastSessionSlot)
|
||||
handler: () => {
|
||||
this.loadSaveSlot(this.lastSessionData ? -1 : loggedInUser.lastSessionSlot);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
options.push({
|
||||
@ -188,20 +191,29 @@ export class TitlePhase extends Phase {
|
||||
this.end();
|
||||
};
|
||||
if (this.scene.gameData.unlocks[Unlockables.ENDLESS_MODE]) {
|
||||
const options = [
|
||||
const options: OptionSelectItem[] = [
|
||||
{
|
||||
label: gameModes[GameModes.CLASSIC].getName(),
|
||||
handler: () => setModeAndEnd(GameModes.CLASSIC)
|
||||
handler: () => {
|
||||
setModeAndEnd(GameModes.CLASSIC);
|
||||
return true;
|
||||
}
|
||||
},
|
||||
{
|
||||
label: gameModes[GameModes.ENDLESS].getName(),
|
||||
handler: () => setModeAndEnd(GameModes.ENDLESS)
|
||||
handler: () => {
|
||||
setModeAndEnd(GameModes.ENDLESS);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
];
|
||||
if (this.scene.gameData.unlocks[Unlockables.SPLICED_ENDLESS_MODE]) {
|
||||
options.push({
|
||||
label: gameModes[GameModes.SPLICED_ENDLESS].getName(),
|
||||
handler: () => setModeAndEnd(GameModes.SPLICED_ENDLESS)
|
||||
handler: () => {
|
||||
setModeAndEnd(GameModes.SPLICED_ENDLESS);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
options.push({
|
||||
@ -210,6 +222,7 @@ export class TitlePhase extends Phase {
|
||||
this.scene.clearPhaseQueue();
|
||||
this.scene.pushPhase(new TitlePhase(this.scene));
|
||||
super.end();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
this.scene.ui.showText(i18next.t("menu:selectGameMode"), null, () => this.scene.ui.setOverlayMode(Mode.OPTION_SELECT, { options: options }));
|
||||
@ -219,21 +232,27 @@ export class TitlePhase extends Phase {
|
||||
this.scene.ui.clearText();
|
||||
this.end();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
{
|
||||
label: i18next.t('menu:loadGame'),
|
||||
handler: () => this.scene.ui.setOverlayMode(Mode.SAVE_SLOT, SaveSlotUiMode.LOAD,
|
||||
(slotId: integer) => {
|
||||
if (slotId === -1)
|
||||
return this.showOptions();
|
||||
this.loadSaveSlot(slotId);
|
||||
}
|
||||
)
|
||||
handler: () => {
|
||||
this.scene.ui.setOverlayMode(Mode.SAVE_SLOT, SaveSlotUiMode.LOAD,
|
||||
(slotId: integer) => {
|
||||
if (slotId === -1)
|
||||
return this.showOptions();
|
||||
this.loadSaveSlot(slotId);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
},
|
||||
{
|
||||
label: i18next.t('menu:dailyRun'),
|
||||
handler: () => this.initDailyRun(),
|
||||
handler: () => {
|
||||
this.initDailyRun();
|
||||
return true;
|
||||
},
|
||||
keepOpen: true
|
||||
});
|
||||
const config: OptionSelectConfig = {
|
||||
@ -250,12 +269,12 @@ export class TitlePhase extends Phase {
|
||||
this.scene.gameData.loadSession(this.scene, slotId, slotId === -1 ? this.lastSessionData : null).then((success: boolean) => {
|
||||
if (success) {
|
||||
this.loaded = true;
|
||||
this.scene.ui.showText('Session loaded successfully.', null, () => this.end());
|
||||
this.scene.ui.showText(i18next.t('menu:sessionSuccess'), null, () => this.end());
|
||||
} else
|
||||
this.end();
|
||||
}).catch(err => {
|
||||
console.error(err);
|
||||
this.scene.ui.showText('Your session data could not be loaded.\nIt may be corrupted.', null);
|
||||
this.scene.ui.showText(i18next.t('menu:failedToLoadSession'), null);
|
||||
});
|
||||
}
|
||||
|
||||
@ -360,6 +379,16 @@ export class UnavailablePhase extends Phase {
|
||||
}
|
||||
}
|
||||
|
||||
export class OutdatedPhase extends Phase {
|
||||
constructor(scene: BattleScene) {
|
||||
super(scene);
|
||||
}
|
||||
|
||||
start(): void {
|
||||
this.scene.ui.setMode(Mode.OUTDATED);
|
||||
}
|
||||
}
|
||||
|
||||
export class SelectGenderPhase extends Phase {
|
||||
constructor(scene: BattleScene) {
|
||||
super(scene);
|
||||
@ -368,23 +397,25 @@ export class SelectGenderPhase extends Phase {
|
||||
start(): void {
|
||||
super.start();
|
||||
|
||||
this.scene.ui.showText('Are you a boy or a girl?', null, () => {
|
||||
this.scene.ui.showText(i18next.t('menu:boyOrGirl'), null, () => {
|
||||
this.scene.ui.setMode(Mode.OPTION_SELECT, {
|
||||
options: [
|
||||
{
|
||||
label: 'Boy',
|
||||
label: i18next.t('menu:boy'),
|
||||
handler: () => {
|
||||
this.scene.gameData.gender = PlayerGender.MALE;
|
||||
this.scene.gameData.saveSetting(Setting.Player_Gender, 0);
|
||||
this.scene.gameData.saveSystem().then(() => this.end());
|
||||
return true;
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Girl',
|
||||
label: i18next.t('menu:girl'),
|
||||
handler: () => {
|
||||
this.scene.gameData.gender = PlayerGender.FEMALE;
|
||||
this.scene.gameData.saveSetting(Setting.Player_Gender, 1);
|
||||
this.scene.gameData.saveSystem().then(() => this.end());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
]
|
||||
@ -437,6 +468,10 @@ export class SelectStarterPhase extends Phase {
|
||||
const starterIvs = this.scene.gameData.dexData[starter.species.speciesId].ivs.slice(0);
|
||||
const starterPokemon = this.scene.addPlayerPokemon(starter.species, this.scene.gameMode.getStartingLevel(), starterProps.abilityIndex, starterFormIndex, starterGender, starterProps.shiny, starterIvs, starter.nature);
|
||||
starterPokemon.tryPopulateMoveset(starter.moveset);
|
||||
if (starter.passive)
|
||||
starterPokemon.passive = true;
|
||||
if (starter.variant && starter.dexAttr & DexAttr.SHINY)
|
||||
starterPokemon.variant = starter.variant;
|
||||
if (starter.pokerus)
|
||||
starterPokemon.pokerus = true;
|
||||
if (this.scene.gameMode.isSplicedOnly)
|
||||
@ -751,14 +786,14 @@ export class EncounterPhase extends BattlePhase {
|
||||
const enemyField = this.scene.getEnemyField();
|
||||
|
||||
if (this.scene.currentBattle.battleSpec === BattleSpec.FINAL_BOSS)
|
||||
return `${enemyField[0].name} appeared.`;
|
||||
return i18next.t('menu:bossAppeared', {bossName: enemyField[0].name});
|
||||
|
||||
if (this.scene.currentBattle.battleType === BattleType.TRAINER)
|
||||
return `${this.scene.currentBattle.trainer.getName(TrainerSlot.NONE, true)}\nwould like to battle!`;
|
||||
return i18next.t('menu:trainerAppeared', {trainerName: this.scene.currentBattle.trainer.getName(TrainerSlot.NONE, true)});
|
||||
|
||||
return enemyField.length === 1
|
||||
? `A wild ${enemyField[0].name} appeared!`
|
||||
: `A wild ${enemyField[0].name}\nand ${enemyField[1].name} appeared!`;
|
||||
? i18next.t('menu:singleWildAppeared', {pokemonName: enemyField[0].name})
|
||||
: i18next.t('menu:multiWildAppeared', {pokemonName1: enemyField[0].name, pokemonName2: enemyField[1].name})
|
||||
}
|
||||
|
||||
doEncounterCommon(showEncounterMessage: boolean = true) {
|
||||
@ -1268,7 +1303,13 @@ export class SwitchSummonPhase extends SummonPhase {
|
||||
|
||||
applyPreSwitchOutAbAttrs(PreSwitchOutAbAttr, pokemon);
|
||||
|
||||
this.scene.ui.showText(this.player ? `Come back, ${pokemon.name}!` : `${this.scene.currentBattle.trainer.getName(!(this.fieldIndex % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER)}\nwithdrew ${pokemon.name}!`);
|
||||
this.scene.ui.showText(this.player ?
|
||||
i18next.t('menu:playerComeBack', { pokemonName: pokemon.name }) :
|
||||
i18next.t('menu:trainerComeBack', {
|
||||
trainerName: this.scene.currentBattle.trainer.getName(!(this.fieldIndex % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER),
|
||||
pokemonName: pokemon.name
|
||||
})
|
||||
);
|
||||
this.scene.playSound('pb_rel');
|
||||
pokemon.hideInfo();
|
||||
pokemon.tint(getPokeballTintColor(pokemon.pokeball), 1, 250, 'Sine.easeIn');
|
||||
@ -1303,7 +1344,13 @@ export class SwitchSummonPhase extends SummonPhase {
|
||||
party[this.slotIndex] = this.lastPokemon;
|
||||
party[this.fieldIndex] = switchedPokemon;
|
||||
const showTextAndSummon = () => {
|
||||
this.scene.ui.showText(this.player ? `Go! ${switchedPokemon.name}!` : `${this.scene.currentBattle.trainer.getName(!(this.fieldIndex % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER)} sent out\n${this.getPokemon().name}!`);
|
||||
this.scene.ui.showText(this.player ?
|
||||
i18next.t('menu:playerGo', { pokemonName: switchedPokemon.name }) :
|
||||
i18next.t('menu:trainerGo', {
|
||||
trainerName: this.scene.currentBattle.trainer.getName(!(this.fieldIndex % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER),
|
||||
pokemonName: this.getPokemon().name
|
||||
})
|
||||
);
|
||||
this.summon();
|
||||
};
|
||||
if (this.player)
|
||||
@ -1444,7 +1491,7 @@ export class CheckSwitchPhase extends BattlePhase {
|
||||
return;
|
||||
}
|
||||
|
||||
this.scene.ui.showText(`Will you switch\n${this.useName ? pokemon.name : 'Pokémon'}?`, null, () => {
|
||||
this.scene.ui.showText(i18next.t('menu:switchQuestion', { pokemonName: this.useName ? pokemon.name : i18next.t('menu:pokemon') }), null, () => {
|
||||
this.scene.ui.setMode(Mode.CONFIRM, () => {
|
||||
this.scene.ui.setMode(Mode.MESSAGE);
|
||||
this.scene.tryRemovePhase(p => p instanceof PostSummonPhase && p.player && p.fieldIndex === this.fieldIndex);
|
||||
@ -1464,7 +1511,7 @@ export class SummonMissingPhase extends SummonPhase {
|
||||
}
|
||||
|
||||
preSummon(): void {
|
||||
this.scene.ui.showText(`Go! ${this.getPokemon().name}!`);
|
||||
this.scene.ui.showText(i18next.t('menu:sendOutPokemon', { pokemonName: this.getPokemon().name}));
|
||||
this.scene.time.delayedCall(250, () => this.summon());
|
||||
}
|
||||
}
|
||||
@ -1479,7 +1526,7 @@ export class LevelCapPhase extends FieldPhase {
|
||||
|
||||
this.scene.ui.setMode(Mode.MESSAGE).then(() => {
|
||||
this.scene.playSound('level_up_fanfare');
|
||||
this.scene.ui.showText(`The level cap\nhas increased to ${this.scene.getMaxExpLevel()}!`, null, () => this.end(), null, true);
|
||||
this.scene.ui.showText(i18next.t('menu:levelCapUp', { levelCap: this.scene.getMaxExpLevel() }), null, () => this.end(), null, true);
|
||||
this.executeForAll(pokemon => pokemon.updateInfo(true));
|
||||
});
|
||||
}
|
||||
@ -1573,7 +1620,7 @@ export class CommandPhase extends FieldPhase {
|
||||
const move = playerPokemon.getMoveset()[cursor];
|
||||
if (move.getName().endsWith(' (N)')) {
|
||||
this.scene.ui.setMode(Mode.MESSAGE);
|
||||
this.scene.ui.showText(`${move.getName().slice(0, -4)} is not yet implemented and cannot be selected.`, null, () => {
|
||||
this.scene.ui.showText(i18next.t('menu:moveNotImplemented', { moveName: move.getName().slice(0, -4) }), null, () => {
|
||||
this.scene.ui.clearText();
|
||||
this.scene.ui.setMode(Mode.FIGHT, this.fieldIndex);
|
||||
}, null, true);
|
||||
@ -1592,7 +1639,7 @@ export class CommandPhase extends FieldPhase {
|
||||
const move = playerPokemon.getMoveset()[cursor];
|
||||
if (playerPokemon.summonData.disabledMove === move.moveId) {
|
||||
this.scene.ui.setMode(Mode.MESSAGE);
|
||||
this.scene.ui.showText(`${move.getName()} is disabled!`, null, () => {
|
||||
this.scene.ui.showText(i18next.t('menu:moveDisabled', { moveName: move.getName() }), null, () => {
|
||||
this.scene.ui.clearText();
|
||||
this.scene.ui.setMode(Mode.FIGHT, this.fieldIndex);
|
||||
}, null, true);
|
||||
@ -1603,14 +1650,14 @@ export class CommandPhase extends FieldPhase {
|
||||
if (this.scene.arena.biomeType === Biome.END && (!this.scene.gameMode.isClassic || this.scene.gameData.getStarterCount(d => !!d.caughtAttr) < Object.keys(speciesStarters).length - 1)) {
|
||||
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
|
||||
this.scene.ui.setMode(Mode.MESSAGE);
|
||||
this.scene.ui.showText(`An unseen force\nprevents using Poké Balls.`, null, () => {
|
||||
this.scene.ui.showText(i18next.t('menu:noPokeballForce'), null, () => {
|
||||
this.scene.ui.showText(null, 0);
|
||||
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
|
||||
}, null, true);
|
||||
} else if (this.scene.currentBattle.battleType === BattleType.TRAINER) {
|
||||
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
|
||||
this.scene.ui.setMode(Mode.MESSAGE);
|
||||
this.scene.ui.showText(`You can't catch\nanother trainer's Pokémon!`, null, () => {
|
||||
this.scene.ui.showText(i18next.t('menu:noPokeballTrainer'), null, () => {
|
||||
this.scene.ui.showText(null, 0);
|
||||
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
|
||||
}, null, true);
|
||||
@ -1619,7 +1666,7 @@ export class CommandPhase extends FieldPhase {
|
||||
if (targets.length > 1) {
|
||||
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
|
||||
this.scene.ui.setMode(Mode.MESSAGE);
|
||||
this.scene.ui.showText(`You can only throw a Poké Ball\nwhen there is one Pokémon remaining!`, null, () => {
|
||||
this.scene.ui.showText(i18next.t('menu:noPokeballMulti'), null, () => {
|
||||
this.scene.ui.showText(null, 0);
|
||||
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
|
||||
}, null, true);
|
||||
@ -1628,7 +1675,7 @@ export class CommandPhase extends FieldPhase {
|
||||
if (targetPokemon.isBoss() && targetPokemon.bossSegmentIndex >= 1 && cursor < PokeballType.MASTER_BALL) {
|
||||
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
|
||||
this.scene.ui.setMode(Mode.MESSAGE);
|
||||
this.scene.ui.showText(`The target Pokémon is too strong to be caught!\nYou need to weaken it first!`, null, () => {
|
||||
this.scene.ui.showText(i18next.t('menu:noPokeballStrong'), null, () => {
|
||||
this.scene.ui.showText(null, 0);
|
||||
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
|
||||
}, null, true);
|
||||
@ -1648,14 +1695,14 @@ export class CommandPhase extends FieldPhase {
|
||||
if (!isSwitch && this.scene.arena.biomeType === Biome.END) {
|
||||
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
|
||||
this.scene.ui.setMode(Mode.MESSAGE);
|
||||
this.scene.ui.showText(`An unseen force\nprevents escape.`, null, () => {
|
||||
this.scene.ui.showText(i18next.t('menu:noEscapeForce'), null, () => {
|
||||
this.scene.ui.showText(null, 0);
|
||||
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
|
||||
}, null, true);
|
||||
} else if (!isSwitch && this.scene.currentBattle.battleType === BattleType.TRAINER) {
|
||||
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
|
||||
this.scene.ui.setMode(Mode.MESSAGE);
|
||||
this.scene.ui.showText(`You can't run\nfrom a trainer battle!`, null, () => {
|
||||
this.scene.ui.showText(i18next.t('menu:noEscapeTrainer'), null, () => {
|
||||
this.scene.ui.showText(null, 0);
|
||||
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
|
||||
}, null, true);
|
||||
@ -1677,11 +1724,18 @@ export class CommandPhase extends FieldPhase {
|
||||
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
|
||||
this.scene.ui.setMode(Mode.MESSAGE);
|
||||
}
|
||||
this.scene.ui.showText(`${this.scene.getPokemonById(trapTag.sourceId).name}'s ${trapTag.getMoveName()}\nprevents ${isSwitch ? 'switching' : 'fleeing'}!`, null, () => {
|
||||
this.scene.ui.showText(null, 0);
|
||||
if (!isSwitch)
|
||||
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
|
||||
}, null, true);
|
||||
this.scene.ui.showText(
|
||||
i18next.t('menu:noEscapePokemon', {
|
||||
pokemonName: this.scene.getPokemonById(trapTag.sourceId).name,
|
||||
moveName: trapTag.getMoveName(),
|
||||
escapeVerb: isSwitch ? i18next.t('menu:escapeVerbSwitch') : i18next.t('menu:escapeVerbFlee')
|
||||
}),
|
||||
null,
|
||||
() => {
|
||||
this.scene.ui.showText(null, 0);
|
||||
if (!isSwitch)
|
||||
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
|
||||
}, null, true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -1924,7 +1978,7 @@ export class TurnEndPhase extends FieldPhase {
|
||||
pokemon.lapseTags(BattlerTagLapseType.TURN_END);
|
||||
|
||||
if (pokemon.summonData.disabledMove && !--pokemon.summonData.disabledTurns) {
|
||||
this.scene.pushPhase(new MessagePhase(this.scene, `${allMoves[pokemon.summonData.disabledMove].name} is disabled\nno more!`));
|
||||
this.scene.pushPhase(new MessagePhase(this.scene, i18next.t('menu:notDisabled', { moveName: allMoves[pokemon.summonData.disabledMove].name })));
|
||||
pokemon.summonData.disabledMove = Moves.NONE;
|
||||
}
|
||||
|
||||
@ -2111,6 +2165,8 @@ export class MovePhase extends BattlePhase {
|
||||
});
|
||||
|
||||
const doMove = () => {
|
||||
this.pokemon.turnData.acted = true; // Record that the move was attempted, even if it fails
|
||||
|
||||
this.pokemon.lapseTags(BattlerTagLapseType.PRE_MOVE);
|
||||
|
||||
if (!this.followUp && this.canMove() && !this.cancelled) {
|
||||
@ -2144,7 +2200,7 @@ export class MovePhase extends BattlePhase {
|
||||
for (let opponent of targetedOpponents) {
|
||||
if (this.move.ppUsed === this.move.getMove().pp)
|
||||
break;
|
||||
if ((opponent.canApplyAbility() && opponent.getAbility().hasAttr(IncreasePpAbAttr)) || (opponent.canApplyAbility(true) && opponent.getPassiveAbility().hasAttr(IncreasePpAbAttr)))
|
||||
if (opponent.hasAbilityWithAttr(IncreasePpAbAttr))
|
||||
this.move.ppUsed = Math.min(this.move.ppUsed + 1, this.move.getMovePp());
|
||||
}
|
||||
}
|
||||
@ -2275,7 +2331,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
||||
const hitCount = new Utils.IntegerHolder(1);
|
||||
// Assume single target for multi hit
|
||||
applyMoveAttrs(MultiHitAttr, user, this.getTarget(), this.move.getMove(), hitCount);
|
||||
if (this.move.getMove() instanceof AttackMove)
|
||||
if (this.move.getMove() instanceof AttackMove && !this.move.getMove().getAttrs(FixedDamageAttr).length)
|
||||
this.scene.applyModifiers(PokemonMultiHitModifier, user.isPlayer(), user, hitCount, new Utils.IntegerHolder(0));
|
||||
user.turnData.hitsLeft = user.turnData.hitCount = hitCount.value;
|
||||
}
|
||||
@ -2343,7 +2399,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
||||
}
|
||||
Utils.executeIf(!isProtected && !chargeEffect, () => applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && (attr as MoveEffectAttr).trigger === MoveEffectTrigger.HIT && (!attr.firstHitOnly || firstHit),
|
||||
user, target, this.move.getMove()).then(() => {
|
||||
return Utils.executeIf(!target.isFainted(), () => applyPostDefendAbAttrs(PostDefendAbAttr, target, user, this.move, hitResult).then(() => {
|
||||
return Utils.executeIf(!target.isFainted() || target.canApplyAbility(), () => applyPostDefendAbAttrs(PostDefendAbAttr, target, user, this.move, hitResult).then(() => {
|
||||
if (!user.isPlayer() && this.move.getMove() instanceof AttackMove)
|
||||
user.scene.applyModifiers(EnemyAttackStatusEffectChanceModifier, false, target);
|
||||
})).then(() => {
|
||||
@ -2357,7 +2413,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
||||
).then(() => resolve());
|
||||
});
|
||||
} else
|
||||
resolve();
|
||||
applyMoveAttrs(NoEffectAttr, user, null, this.move.getMove()).then(() => resolve());
|
||||
});
|
||||
} else
|
||||
resolve();
|
||||
@ -2586,6 +2642,7 @@ export class StatChangePhase extends PokemonPhase {
|
||||
for (let stat of filteredStats)
|
||||
pokemon.summonData.battleStats[stat] = Math.max(Math.min(pokemon.summonData.battleStats[stat] + levels.value, 6), -6);
|
||||
|
||||
applyPostStatChangeAbAttrs(PostStatChangeAbAttr, pokemon, filteredStats, this.levels, this.selfTarget)
|
||||
this.end();
|
||||
};
|
||||
|
||||
@ -2931,7 +2988,7 @@ export class FaintPhase extends PokemonPhase {
|
||||
}
|
||||
|
||||
const alivePlayField = this.scene.getField(true);
|
||||
alivePlayField.forEach(p => applyPostKnockOutAbAttrs(PostKnockOutAbAttr, p));
|
||||
alivePlayField.forEach(p => applyPostKnockOutAbAttrs(PostKnockOutAbAttr, p, pokemon));
|
||||
if (pokemon.turnData?.attacksReceived?.length) {
|
||||
const defeatSource = this.scene.getPokemonById(pokemon.turnData.attacksReceived[0].sourceId);
|
||||
if (defeatSource?.isOnField())
|
||||
|
@ -20,6 +20,9 @@ i18next.init({
|
||||
lng: DEFAULT_LANGUAGE_OVERRIDE ? DEFAULT_LANGUAGE_OVERRIDE : 'en',
|
||||
fallbackLng: 'en',
|
||||
debug: true,
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
},
|
||||
resources: {
|
||||
en: {
|
||||
menu: enMenu,
|
||||
|
@ -27,6 +27,7 @@ import { Moves } from "../data/enums/moves";
|
||||
import { speciesEggMoves } from "../data/egg-moves";
|
||||
import { allMoves } from "../data/move";
|
||||
import { TrainerVariant } from "../field/trainer";
|
||||
import { OutdatedPhase, UnavailablePhase } from "#app/phases";
|
||||
|
||||
const saveKey = 'x0i2O7WRiANTqPmZ'; // Temporary; secure encryption is not yet necessary
|
||||
|
||||
@ -43,6 +44,11 @@ export enum PlayerGender {
|
||||
FEMALE
|
||||
}
|
||||
|
||||
export enum Passive {
|
||||
UNLOCKED = 1,
|
||||
ENABLED = 2
|
||||
}
|
||||
|
||||
export function getDataTypeKey(dataType: GameDataType, slotId: integer = 0): string {
|
||||
switch (dataType) {
|
||||
case GameDataType.SYSTEM:
|
||||
@ -64,8 +70,7 @@ interface SystemSaveData {
|
||||
secretId: integer;
|
||||
gender: PlayerGender;
|
||||
dexData: DexData;
|
||||
starterMoveData: StarterMoveData;
|
||||
starterEggMoveData: StarterEggMoveData;
|
||||
starterData: StarterData;
|
||||
gameStats: GameStats;
|
||||
unlocks: Unlocks;
|
||||
achvUnlocks: AchvUnlocks;
|
||||
@ -145,16 +150,25 @@ export interface DexAttrProps {
|
||||
|
||||
export type StarterMoveset = [ Moves ] | [ Moves, Moves ] | [ Moves, Moves, Moves ] | [ Moves, Moves, Moves, Moves ];
|
||||
|
||||
export interface StarterMoveData {
|
||||
[key: integer]: StarterMoveset | StarterFormMoveData
|
||||
}
|
||||
|
||||
export interface StarterFormMoveData {
|
||||
[key: integer]: StarterMoveset
|
||||
}
|
||||
|
||||
export interface StarterEggMoveData {
|
||||
[key: integer]: integer
|
||||
export interface StarterMoveData {
|
||||
[key: integer]: StarterMoveset | StarterFormMoveData
|
||||
}
|
||||
|
||||
export interface StarterDataEntry {
|
||||
moveset: StarterMoveset | StarterFormMoveData;
|
||||
eggMoves: integer;
|
||||
candyCount: integer;
|
||||
passiveAttr: integer;
|
||||
variantAttr: integer;
|
||||
valueReduction: integer;
|
||||
}
|
||||
|
||||
export interface StarterData {
|
||||
[key: integer]: StarterDataEntry
|
||||
}
|
||||
|
||||
export interface TutorialFlags {
|
||||
@ -167,7 +181,12 @@ const systemShortKeys = {
|
||||
natureAttr: '$na',
|
||||
seenCount: '$s' ,
|
||||
caughtCount: '$c',
|
||||
ivs: '$i'
|
||||
ivs: '$i',
|
||||
moveset: '$m',
|
||||
eggMoves: '$em',
|
||||
candyCount: '$cc',
|
||||
passive: '$p',
|
||||
valueReduction: '$vr'
|
||||
};
|
||||
|
||||
export class GameData {
|
||||
@ -181,9 +200,7 @@ export class GameData {
|
||||
public dexData: DexData;
|
||||
private defaultDexData: DexData;
|
||||
|
||||
public starterMoveData: StarterMoveData;
|
||||
|
||||
public starterEggMoveData: StarterEggMoveData;
|
||||
public starterData: StarterData;
|
||||
|
||||
public gameStats: GameStats;
|
||||
|
||||
@ -200,8 +217,7 @@ export class GameData {
|
||||
this.loadSettings();
|
||||
this.trainerId = Utils.randSeedInt(65536);
|
||||
this.secretId = Utils.randSeedInt(65536);
|
||||
this.starterMoveData = {};
|
||||
this.starterEggMoveData = {};
|
||||
this.starterData = {};
|
||||
this.gameStats = new GameStats();
|
||||
this.unlocks = {
|
||||
[Unlockables.ENDLESS_MODE]: false,
|
||||
@ -218,7 +234,7 @@ export class GameData {
|
||||
};
|
||||
this.eggs = [];
|
||||
this.initDexData();
|
||||
this.initEggMoveData();
|
||||
this.initStarterData();
|
||||
}
|
||||
|
||||
public saveSystem(): Promise<boolean> {
|
||||
@ -234,8 +250,7 @@ export class GameData {
|
||||
secretId: this.secretId,
|
||||
gender: this.gender,
|
||||
dexData: this.dexData,
|
||||
starterMoveData: this.starterMoveData,
|
||||
starterEggMoveData: this.starterEggMoveData,
|
||||
starterData: this.starterData,
|
||||
gameStats: this.gameStats,
|
||||
unlocks: this.unlocks,
|
||||
achvUnlocks: this.achvUnlocks,
|
||||
@ -255,6 +270,10 @@ export class GameData {
|
||||
.then(error => {
|
||||
this.scene.ui.savingIcon.hide();
|
||||
if (error) {
|
||||
if (error.startsWith('client version out of date')) {
|
||||
this.scene.clearPhaseQueue();
|
||||
this.scene.unshiftPhase(new OutdatedPhase(this.scene));
|
||||
}
|
||||
console.error(error);
|
||||
return resolve(false);
|
||||
}
|
||||
@ -297,17 +316,24 @@ export class GameData {
|
||||
|
||||
this.saveSetting(Setting.Player_Gender, systemData.gender === PlayerGender.FEMALE ? 1 : 0);
|
||||
|
||||
this.starterMoveData = systemData.starterMoveData || {};
|
||||
const initStarterData = !systemData.starterData;
|
||||
|
||||
this.starterEggMoveData = {};
|
||||
this.initEggMoveData();
|
||||
if (initStarterData) {
|
||||
this.initStarterData();
|
||||
|
||||
if (systemData.starterEggMoveData) {
|
||||
for (let key of Object.keys(systemData.starterEggMoveData)) {
|
||||
if (this.starterEggMoveData.hasOwnProperty(key))
|
||||
this.starterEggMoveData[key] = systemData.starterEggMoveData[key];
|
||||
if (systemData['starterMoveData']) {
|
||||
const starterMoveData = systemData['starterMoveData'];
|
||||
for (let s of Object.keys(starterMoveData))
|
||||
this.starterData[s].moveset = starterMoveData[s];
|
||||
}
|
||||
}
|
||||
|
||||
if (systemData['starterEggMoveData']) {
|
||||
const starterEggMoveData = systemData['starterEggMoveData'];
|
||||
for (let s of Object.keys(starterEggMoveData))
|
||||
this.starterData[s].eggMoves = starterEggMoveData[s];
|
||||
}
|
||||
} else
|
||||
this.starterData = systemData.starterData;
|
||||
|
||||
if (systemData.gameStats)
|
||||
this.gameStats = systemData.gameStats;
|
||||
@ -348,6 +374,16 @@ export class GameData {
|
||||
this.consolidateDexData(this.dexData);
|
||||
this.defaultDexData = null;
|
||||
|
||||
if (initStarterData) {
|
||||
const starterIds = Object.keys(this.starterData).map(s => parseInt(s) as Species);
|
||||
for (let s of starterIds) {
|
||||
this.starterData[s].candyCount += this.dexData[s].caughtCount;
|
||||
this.starterData[s].candyCount += this.dexData[s].hatchedCount * 2;
|
||||
if (this.dexData[s].caughtAttr & DexAttr.SHINY)
|
||||
this.starterData[s].candyCount += 4;
|
||||
}
|
||||
}
|
||||
|
||||
resolve(true);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
@ -388,7 +424,7 @@ export class GameData {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return k.endsWith('Attr') && k !== 'natureAttr' ? BigInt(v) : v;
|
||||
return k.endsWith('Attr') && ![ 'natureAttr', 'passiveAttr', 'variantAttr' ].includes(k) ? BigInt(v) : v;
|
||||
}) as SystemSaveData;
|
||||
}
|
||||
|
||||
@ -906,15 +942,23 @@ export class GameData {
|
||||
this.dexData = data;
|
||||
}
|
||||
|
||||
private initEggMoveData(): void {
|
||||
const data: StarterEggMoveData = {};
|
||||
private initStarterData(): void {
|
||||
const starterData: StarterData = {};
|
||||
|
||||
const starterSpeciesIds = Object.keys(speciesEggMoves).map(k => parseInt(k) as Species);
|
||||
const starterSpeciesIds = Object.keys(speciesStarters).map(k => parseInt(k) as Species);
|
||||
|
||||
for (let speciesId of starterSpeciesIds)
|
||||
data[speciesId] = 0;
|
||||
for (let speciesId of starterSpeciesIds) {
|
||||
starterData[speciesId] = {
|
||||
moveset: null,
|
||||
eggMoves: 0,
|
||||
candyCount: 0,
|
||||
passiveAttr: 0,
|
||||
variantAttr: 0,
|
||||
valueReduction: 0
|
||||
};
|
||||
}
|
||||
|
||||
this.starterEggMoveData = data;
|
||||
this.starterData = starterData;
|
||||
}
|
||||
|
||||
setPokemonSeen(pokemon: Pokemon, incrementCount: boolean = true): void {
|
||||
@ -943,6 +987,10 @@ export class GameData {
|
||||
pokemon.formIndex = formIndex;
|
||||
dexEntry.caughtAttr |= dexAttr;
|
||||
dexEntry.natureAttr |= Math.pow(2, pokemon.nature + 1);
|
||||
|
||||
const hasPrevolution = pokemonPrevolutions.hasOwnProperty(species.speciesId);
|
||||
const newCatch = !caughtAttr;
|
||||
|
||||
if (incrementCount) {
|
||||
if (!fromEgg) {
|
||||
dexEntry.caughtCount++;
|
||||
@ -963,10 +1011,10 @@ export class GameData {
|
||||
if (pokemon.isShiny())
|
||||
this.gameStats.shinyPokemonHatched++;
|
||||
}
|
||||
}
|
||||
|
||||
const hasPrevolution = pokemonPrevolutions.hasOwnProperty(species.speciesId);
|
||||
const newCatch = !caughtAttr;
|
||||
if (!hasPrevolution)
|
||||
this.addStarterCandy(species, (1 * (pokemon.isShiny() ? 5 : 1)) * (fromEgg || pokemon.isBoss() ? 2 : 1));
|
||||
}
|
||||
|
||||
const checkPrevolution = () => {
|
||||
if (hasPrevolution) {
|
||||
@ -984,6 +1032,11 @@ export class GameData {
|
||||
});
|
||||
}
|
||||
|
||||
addStarterCandy(species: PokemonSpecies, count: integer): void {
|
||||
this.scene.candyBar.showStarterSpeciesCandy(species.speciesId, count);
|
||||
this.starterData[species.speciesId].candyCount += count;
|
||||
}
|
||||
|
||||
setEggMoveUnlocked(species: PokemonSpecies, eggMoveIndex: integer): Promise<boolean> {
|
||||
return new Promise<boolean>(resolve => {
|
||||
const speciesId = species.speciesId;
|
||||
@ -992,17 +1045,17 @@ export class GameData {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.starterEggMoveData.hasOwnProperty(speciesId))
|
||||
this.starterEggMoveData[speciesId] = 0;
|
||||
if (!this.starterData[speciesId].eggMoves)
|
||||
this.starterData[speciesId].eggMoves = 0;
|
||||
|
||||
const value = Math.pow(2, eggMoveIndex);
|
||||
|
||||
if (this.starterEggMoveData[speciesId] & value) {
|
||||
if (this.starterData[speciesId].eggMoves & value) {
|
||||
resolve(false);
|
||||
return;
|
||||
}
|
||||
|
||||
this.starterEggMoveData[speciesId] |= value;
|
||||
this.starterData[speciesId].eggMoves |= value;
|
||||
|
||||
this.scene.playSound('level_up_fanfare');
|
||||
this.scene.ui.showText(`${eggMoveIndex === 3 ? 'Rare ' : ''}Egg Move unlocked: ${allMoves[speciesEggMoves[speciesId][eggMoveIndex]].name}`, null, () => resolve(true), null, true);
|
||||
@ -1094,7 +1147,6 @@ export class GameData {
|
||||
getSpeciesStarterValue(speciesId: Species): number {
|
||||
const baseValue = speciesStarters[speciesId];
|
||||
let value = baseValue;
|
||||
const caughtHatchedCount = this.dexData[speciesId].caughtCount + this.dexData[speciesId].hatchedCount;
|
||||
|
||||
const decrementValue = (value: number) => {
|
||||
if (value > 1)
|
||||
@ -1104,44 +1156,8 @@ export class GameData {
|
||||
return value;
|
||||
}
|
||||
|
||||
let thresholdA: integer;
|
||||
let thresholdB: integer;
|
||||
|
||||
switch (baseValue) {
|
||||
case 1:
|
||||
[ thresholdA, thresholdB ] = [ 25, 100 ];
|
||||
break;
|
||||
case 2:
|
||||
[ thresholdA, thresholdB ] = [ 20, 70 ];
|
||||
break;
|
||||
case 3:
|
||||
[ thresholdA, thresholdB ] = [ 15, 50 ];
|
||||
break;
|
||||
case 4:
|
||||
[ thresholdA, thresholdB ] = [ 10, 30 ];
|
||||
break;
|
||||
case 5:
|
||||
[ thresholdA, thresholdB ] = [ 8, 25 ];
|
||||
break;
|
||||
case 6:
|
||||
[ thresholdA, thresholdB ] = [ 5, 15 ];
|
||||
break;
|
||||
case 7:
|
||||
[ thresholdA, thresholdB ] = [ 4, 12 ];
|
||||
break;
|
||||
case 8:
|
||||
[ thresholdA, thresholdB ] = [ 3, 10 ];
|
||||
break;
|
||||
default:
|
||||
[ thresholdA, thresholdB ] = [ 2, 5 ];
|
||||
break;
|
||||
}
|
||||
|
||||
if (caughtHatchedCount >= thresholdA) {
|
||||
for (let v = 0; v < this.starterData[speciesId].valueReduction; v++)
|
||||
value = decrementValue(value);
|
||||
if (caughtHatchedCount >= thresholdB)
|
||||
value = decrementValue(value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
@ -17,7 +17,9 @@ export default class PokemonData {
|
||||
public species: Species;
|
||||
public formIndex: integer;
|
||||
public abilityIndex: integer;
|
||||
public passive: boolean;
|
||||
public shiny: boolean;
|
||||
public variant: integer;
|
||||
public pokeball: PokeballType;
|
||||
public level: integer;
|
||||
public exp: integer;
|
||||
@ -53,7 +55,9 @@ export default class PokemonData {
|
||||
this.species = sourcePokemon ? sourcePokemon.species.speciesId : source.species;
|
||||
this.formIndex = Math.max(Math.min(source.formIndex, getPokemonSpecies(this.species).forms.length - 1), 0);
|
||||
this.abilityIndex = source.abilityIndex;
|
||||
this.passive = source.passive;
|
||||
this.shiny = source.shiny;
|
||||
this.variant = source.variant;
|
||||
this.pokeball = source.pokeball;
|
||||
this.level = source.level;
|
||||
this.exp = source.exp;
|
||||
|
@ -4,6 +4,7 @@ import { Mode } from "./ui";
|
||||
import UiHandler from "./ui-handler";
|
||||
import { addWindow } from "./ui-theme";
|
||||
import * as Utils from "../utils";
|
||||
import { argbFromRgba } from "@material/material-color-utilities";
|
||||
|
||||
export interface OptionSelectConfig {
|
||||
xOffset?: number;
|
||||
@ -16,9 +17,11 @@ export interface OptionSelectConfig {
|
||||
|
||||
export interface OptionSelectItem {
|
||||
label: string;
|
||||
handler: Function;
|
||||
handler: () => boolean;
|
||||
keepOpen?: boolean;
|
||||
overrideSound?: boolean;
|
||||
item?: string;
|
||||
itemArgs?: any[]
|
||||
}
|
||||
|
||||
const scrollUpLabel = '↑';
|
||||
@ -28,6 +31,7 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler {
|
||||
protected optionSelectContainer: Phaser.GameObjects.Container;
|
||||
protected optionSelectBg: Phaser.GameObjects.NineSlice;
|
||||
protected optionSelectText: Phaser.GameObjects.Text;
|
||||
protected optionSelectIcons: Phaser.GameObjects.Sprite[];
|
||||
|
||||
protected config: OptionSelectConfig;
|
||||
|
||||
@ -58,6 +62,8 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler {
|
||||
this.optionSelectBg.setOrigin(1, 1);
|
||||
this.optionSelectContainer.add(this.optionSelectBg);
|
||||
|
||||
this.optionSelectIcons = [];
|
||||
|
||||
this.setCursor(0);
|
||||
}
|
||||
|
||||
@ -66,8 +72,12 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler {
|
||||
|
||||
if (this.optionSelectText)
|
||||
this.optionSelectText.destroy();
|
||||
if (this.optionSelectIcons?.length) {
|
||||
this.optionSelectIcons.map(i => i.destroy());
|
||||
this.optionSelectIcons.splice(0, this.optionSelectIcons.length);
|
||||
}
|
||||
|
||||
this.optionSelectText = addTextObject(this.scene, 0, 0, options.map(o => o.label).join('\n'), TextStyle.WINDOW, { maxLines: options.length });
|
||||
this.optionSelectText = addTextObject(this.scene, 0, 0, options.map(o => o.item ? ` ${o.label}` : o.label).join('\n'), TextStyle.WINDOW, { maxLines: options.length });
|
||||
this.optionSelectText.setLineSpacing(12);
|
||||
this.optionSelectContainer.add(this.optionSelectText);
|
||||
this.optionSelectContainer.setPosition((this.scene.game.canvas.width / 6) - 1 - (this.config?.xOffset || 0), -48 + (this.config?.yOffset || 0));
|
||||
@ -80,6 +90,31 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler {
|
||||
this.optionSelectBg.height = this.getWindowHeight();
|
||||
|
||||
this.optionSelectText.setPositionRelative(this.optionSelectBg, 16, 9);
|
||||
|
||||
options.forEach((option: OptionSelectItem, i: integer) => {
|
||||
if (option.item) {
|
||||
const itemIcon = this.scene.add.sprite(0, 0, 'items', option.item);
|
||||
itemIcon.setScale(0.5);
|
||||
this.optionSelectIcons.push(itemIcon);
|
||||
|
||||
this.optionSelectContainer.add(itemIcon);
|
||||
|
||||
itemIcon.setPositionRelative(this.optionSelectText, 6, 7 + 16 * i);
|
||||
|
||||
if (option.item === 'candy') {
|
||||
const itemOverlayIcon = this.scene.add.sprite(0, 0, 'items', 'candy_overlay');
|
||||
itemOverlayIcon.setScale(0.5);
|
||||
this.optionSelectIcons.push(itemOverlayIcon);
|
||||
|
||||
this.optionSelectContainer.add(itemOverlayIcon);
|
||||
|
||||
itemOverlayIcon.setPositionRelative(this.optionSelectText, 6, 7 + 16 * i);
|
||||
|
||||
itemIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(option.itemArgs[0])));
|
||||
itemOverlayIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(option.itemArgs[1])));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
show(args: any[]): boolean {
|
||||
@ -132,10 +167,12 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler {
|
||||
return false;
|
||||
}
|
||||
const option = this.config.options[this.cursor + (this.scrollCursor - (this.scrollCursor ? 1 : 0))];
|
||||
option.handler();
|
||||
if (!option.keepOpen)
|
||||
this.clear();
|
||||
playSound = !option.overrideSound;
|
||||
if (option.handler()) {
|
||||
if (!option.keepOpen)
|
||||
this.clear();
|
||||
playSound = !option.overrideSound;
|
||||
} else
|
||||
ui.playError();
|
||||
} else {
|
||||
switch (button) {
|
||||
case Button.UP:
|
||||
@ -182,12 +219,12 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler {
|
||||
if (optionStartIndex)
|
||||
options.unshift({
|
||||
label: scrollUpLabel,
|
||||
handler: () => { }
|
||||
handler: () => true
|
||||
});
|
||||
if (optionEndIndex < optionsScrollTotal)
|
||||
options.push({
|
||||
label: scrollDownLabel,
|
||||
handler: () => { }
|
||||
handler: () => true
|
||||
});
|
||||
}
|
||||
|
||||
|
123
src/ui/candy-bar.ts
Normal file
@ -0,0 +1,123 @@
|
||||
import BattleScene, { starterColors } from "../battle-scene";
|
||||
import { TextStyle, addTextObject } from "./text";
|
||||
import { argbFromRgba } from "@material/material-color-utilities";
|
||||
import * as Utils from "../utils";
|
||||
import { Species } from "#app/data/enums/species";
|
||||
|
||||
export default class CandyBar extends Phaser.GameObjects.Container {
|
||||
private bg: Phaser.GameObjects.NineSlice;
|
||||
private candyIcon: Phaser.GameObjects.Sprite;
|
||||
private candyOverlayIcon: Phaser.GameObjects.Sprite;
|
||||
private countText: Phaser.GameObjects.Text;
|
||||
private speciesId: Species;
|
||||
|
||||
private tween: Phaser.Tweens.Tween;
|
||||
private autoHideTimer: number;
|
||||
|
||||
public shown: boolean;
|
||||
|
||||
constructor(scene: BattleScene) {
|
||||
super(scene, (scene.game.canvas.width / 6), -((scene.game.canvas.height) / 6) + 15);
|
||||
}
|
||||
|
||||
setup(): void {
|
||||
this.bg = this.scene.add.nineslice(0, 0, 'party_exp_bar', null, 8, 18, 21, 5, 6, 4);
|
||||
this.bg.setOrigin(0, 0);
|
||||
|
||||
this.add(this.bg);
|
||||
|
||||
this.candyIcon = this.scene.add.sprite(14, 0, 'items', 'candy');
|
||||
this.candyIcon.setOrigin(0.5, 0);
|
||||
this.candyIcon.setScale(0.5);
|
||||
|
||||
this.add(this.candyIcon);
|
||||
|
||||
this.candyOverlayIcon = this.scene.add.sprite(14, 0, 'items', 'candy_overlay');
|
||||
this.candyOverlayIcon.setOrigin(0.5, 0);
|
||||
this.candyOverlayIcon.setScale(0.5);
|
||||
|
||||
this.add(this.candyOverlayIcon);
|
||||
|
||||
this.countText = addTextObject(this.scene, 22, 4, '', TextStyle.BATTLE_INFO);
|
||||
this.countText.setOrigin(0, 0);
|
||||
this.add(this.countText);
|
||||
|
||||
this.setVisible(false);
|
||||
this.shown = false;
|
||||
}
|
||||
|
||||
showStarterSpeciesCandy(starterSpeciesId: Species, count: integer): Promise<void> {
|
||||
return new Promise<void>(resolve => {
|
||||
if (this.shown) {
|
||||
if (this.speciesId === starterSpeciesId)
|
||||
return resolve();
|
||||
else
|
||||
return this.hide().then(() => this.showStarterSpeciesCandy(starterSpeciesId, count)).then(() => resolve());
|
||||
}
|
||||
|
||||
const colorScheme = starterColors[starterSpeciesId];
|
||||
|
||||
this.candyIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(colorScheme[0])));
|
||||
this.candyOverlayIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(colorScheme[1])));
|
||||
|
||||
this.countText.setText(`${(this.scene as BattleScene).gameData.starterData[starterSpeciesId].candyCount + count} (+${count.toString()})`);
|
||||
|
||||
this.bg.width = this.countText.displayWidth + 28;
|
||||
|
||||
(this.scene as BattleScene).fieldUI.bringToTop(this);
|
||||
|
||||
if (this.tween)
|
||||
this.tween.stop();
|
||||
|
||||
this.tween = this.scene.tweens.add({
|
||||
targets: this,
|
||||
x: (this.scene.game.canvas.width / 6) - (this.bg.width - 5),
|
||||
duration: 500,
|
||||
ease: 'Sine.easeOut',
|
||||
onComplete: () => {
|
||||
this.tween = null;
|
||||
this.resetAutoHideTimer();
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
|
||||
this.setVisible(true);
|
||||
this.shown = true;
|
||||
});
|
||||
}
|
||||
|
||||
hide(): Promise<void> {
|
||||
return new Promise<void>(resolve => {
|
||||
if (!this.shown)
|
||||
return resolve();
|
||||
|
||||
if (this.autoHideTimer)
|
||||
clearInterval(this.autoHideTimer);
|
||||
|
||||
if (this.tween)
|
||||
this.tween.stop();
|
||||
|
||||
this.tween = this.scene.tweens.add({
|
||||
targets: this,
|
||||
x: (this.scene.game.canvas.width / 6),
|
||||
duration: 500,
|
||||
ease: 'Sine.easeIn',
|
||||
onComplete: () => {
|
||||
this.tween = null;
|
||||
this.shown = false;
|
||||
this.setVisible(false);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
resetAutoHideTimer(): void {
|
||||
if (this.autoHideTimer)
|
||||
clearInterval(this.autoHideTimer);
|
||||
this.autoHideTimer = setTimeout(() => {
|
||||
this.hide();
|
||||
this.autoHideTimer = null;
|
||||
}, 2500);
|
||||
}
|
||||
}
|
@ -20,11 +20,17 @@ export default class ConfirmUiHandler extends AbstractOptionSelectUiHandler {
|
||||
options: [
|
||||
{
|
||||
label: 'Yes',
|
||||
handler: args[0]
|
||||
handler: () => {
|
||||
args[0]();
|
||||
return true;
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'No',
|
||||
handler: args[1]
|
||||
handler: () => {
|
||||
args[1]();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
],
|
||||
delay: args.length >= 6 && args[5] !== null ? args[5] as integer : 0
|
||||
|
@ -339,7 +339,7 @@ export default class EggGachaUiHandler extends MessageUiHandler {
|
||||
const tierValueOffset = this.gachaCursor === GachaType.LEGENDARY ? 1 : 0;
|
||||
const tiers = new Array(pullCount).fill(null).map(() => {
|
||||
const tierValue = Utils.randInt(256);
|
||||
return tierValue >= 52 + tierValueOffset ? EggTier.COMMON : tierValue + tierValueOffset >= 8 ? EggTier.GREAT : tierValue >= 1 + tierValueOffset ? EggTier.ULTRA : EggTier.MASTER;
|
||||
return tierValue >= 52 + tierValueOffset ? EggTier.COMMON : tierValue >= 8 + tierValueOffset ? EggTier.GREAT : tierValue >= 1 + tierValueOffset ? EggTier.ULTRA : EggTier.MASTER;
|
||||
});
|
||||
if (pullCount >= 25 && !tiers.filter(t => t >= EggTier.ULTRA).length)
|
||||
tiers[Utils.randInt(tiers.length)] = EggTier.ULTRA;
|
||||
|
@ -5,7 +5,7 @@ import * as Utils from "../utils";
|
||||
import { addWindow } from "./ui-theme";
|
||||
import MessageUiHandler from "./message-ui-handler";
|
||||
import { GameDataType } from "../system/game-data";
|
||||
import { OptionSelectConfig } from "./abstact-option-select-ui-handler";
|
||||
import { OptionSelectConfig, OptionSelectItem } from "./abstact-option-select-ui-handler";
|
||||
import { Tutorial, handleTutorial } from "../tutorial";
|
||||
import { updateUserInfo } from "../account";
|
||||
|
||||
@ -99,6 +99,7 @@ export default class MenuUiHandler extends MessageUiHandler {
|
||||
callback(i);
|
||||
ui.revertMode();
|
||||
ui.showText(null, 0);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}).concat([{
|
||||
@ -106,6 +107,7 @@ export default class MenuUiHandler extends MessageUiHandler {
|
||||
handler: () => {
|
||||
ui.revertMode();
|
||||
ui.showText(null, 0);
|
||||
return true;
|
||||
}
|
||||
}]),
|
||||
xOffset: 98
|
||||
@ -117,7 +119,10 @@ export default class MenuUiHandler extends MessageUiHandler {
|
||||
if (Utils.isLocal) {
|
||||
manageDataOptions.push({
|
||||
label: 'Import Session',
|
||||
handler: () => confirmSlot('Select a slot to import to.', () => true, slotId => this.scene.gameData.importData(GameDataType.SESSION, slotId)),
|
||||
handler: () => {
|
||||
confirmSlot('Select a slot to import to.', () => true, slotId => this.scene.gameData.importData(GameDataType.SESSION, slotId));
|
||||
return true;
|
||||
},
|
||||
keepOpen: true
|
||||
});
|
||||
}
|
||||
@ -137,25 +142,35 @@ export default class MenuUiHandler extends MessageUiHandler {
|
||||
i => dataSlots.indexOf(i) > -1,
|
||||
slotId => this.scene.gameData.tryExportData(GameDataType.SESSION, slotId));
|
||||
});
|
||||
return true;
|
||||
},
|
||||
keepOpen: true
|
||||
});
|
||||
if (Utils.isLocal) {
|
||||
manageDataOptions.push({
|
||||
label: 'Import Data',
|
||||
handler: () => this.scene.gameData.importData(GameDataType.SYSTEM),
|
||||
handler: () => {
|
||||
this.scene.gameData.importData(GameDataType.SYSTEM);
|
||||
return true;
|
||||
},
|
||||
keepOpen: true
|
||||
});
|
||||
}
|
||||
manageDataOptions.push(
|
||||
{
|
||||
label: 'Export Data',
|
||||
handler: () => this.scene.gameData.tryExportData(GameDataType.SYSTEM),
|
||||
handler: () => {
|
||||
this.scene.gameData.tryExportData(GameDataType.SYSTEM);
|
||||
return true;
|
||||
},
|
||||
keepOpen: true
|
||||
},
|
||||
{
|
||||
label: 'Cancel',
|
||||
handler: () => this.scene.ui.revertMode()
|
||||
handler: () => {
|
||||
this.scene.ui.revertMode();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@ -164,25 +179,37 @@ export default class MenuUiHandler extends MessageUiHandler {
|
||||
options: manageDataOptions
|
||||
};
|
||||
|
||||
const communityOptions = [
|
||||
const communityOptions: OptionSelectItem[] = [
|
||||
{
|
||||
label: 'Wiki',
|
||||
handler: () => window.open(wikiUrl, '_blank').focus(),
|
||||
handler: () => {
|
||||
window.open(wikiUrl, '_blank').focus();
|
||||
return true;
|
||||
},
|
||||
keepOpen: true
|
||||
},
|
||||
{
|
||||
label: 'Discord',
|
||||
handler: () => window.open(discordUrl, '_blank').focus(),
|
||||
handler: () => {
|
||||
window.open(discordUrl, '_blank').focus();
|
||||
return true;
|
||||
},
|
||||
keepOpen: true
|
||||
},
|
||||
{
|
||||
label: 'GitHub',
|
||||
handler: () => window.open(githubUrl, '_blank').focus(),
|
||||
handler: () => {
|
||||
window.open(githubUrl, '_blank').focus();
|
||||
return true;
|
||||
},
|
||||
keepOpen: true
|
||||
},
|
||||
{
|
||||
label: 'Cancel',
|
||||
handler: () => this.scene.ui.revertMode()
|
||||
handler: () => {
|
||||
this.scene.ui.revertMode();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
|
47
src/ui/outdated-modal-ui-handler.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import BattleScene from "../battle-scene";
|
||||
import { ModalConfig, ModalUiHandler } from "./modal-ui-handler";
|
||||
import { addTextObject, TextStyle } from "./text";
|
||||
import { Mode } from "./ui";
|
||||
|
||||
export default class OutdatedModalUiHandler extends ModalUiHandler {
|
||||
constructor(scene: BattleScene, mode?: Mode) {
|
||||
super(scene, mode);
|
||||
}
|
||||
|
||||
getModalTitle(): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
getWidth(): number {
|
||||
return 160;
|
||||
}
|
||||
|
||||
getHeight(): number {
|
||||
return 64;
|
||||
}
|
||||
|
||||
getMargin(): [number, number, number, number] {
|
||||
return [ 0, 0, 48, 0 ];
|
||||
}
|
||||
|
||||
getButtonLabels(): string[] {
|
||||
return [ ];
|
||||
}
|
||||
|
||||
setup(): void {
|
||||
super.setup();
|
||||
|
||||
const label = addTextObject(this.scene, this.getWidth() / 2, this.getHeight() / 2, 'Your client is currently outdated.\nPlease reload to update the game.\n\nIf this error persists, please clear your browser cache.', TextStyle.WINDOW, { fontSize: '48px', align: 'center' });
|
||||
label.setOrigin(0.5, 0.5);
|
||||
|
||||
this.modalContainer.add(label);
|
||||
}
|
||||
|
||||
show(args: any[]): boolean {
|
||||
const config: ModalConfig = {
|
||||
buttonActions: []
|
||||
};
|
||||
|
||||
return super.show([ config ]);
|
||||
}
|
||||
}
|
@ -114,8 +114,10 @@ export default class SaveSlotSelectUiHandler extends MessageUiHandler {
|
||||
ui.showText(null, 0);
|
||||
}, false, 0, 19, 2000);
|
||||
});
|
||||
} else
|
||||
} else if (this.sessionSlots[this.cursor].hasData === false)
|
||||
saveAndCallback();
|
||||
else
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
success = true;
|
||||
@ -210,7 +212,6 @@ class SessionSlot extends Phaser.GameObjects.Container {
|
||||
super(scene, 0, slotId * 56);
|
||||
|
||||
this.slotId = slotId;
|
||||
this.hasData = false;
|
||||
|
||||
this.setup();
|
||||
}
|
||||
@ -282,6 +283,7 @@ class SessionSlot extends Phaser.GameObjects.Container {
|
||||
return new Promise<boolean>(resolve => {
|
||||
this.scene.gameData.getSession(this.slotId).then(async sessionData => {
|
||||
if (!sessionData) {
|
||||
this.hasData = false;
|
||||
this.loadingLabel.setText('Empty');
|
||||
resolve(false);
|
||||
return;
|
||||
|
@ -1,15 +1,14 @@
|
||||
import BattleScene, { Button } from "../battle-scene";
|
||||
import PokemonSpecies, { allSpecies, getPokemonSpecies, getPokemonSpeciesForm, speciesStarters } from "../data/pokemon-species";
|
||||
import BattleScene, { Button, starterColors } from "../battle-scene";
|
||||
import PokemonSpecies, { allSpecies, getPokemonSpecies, getPokemonSpeciesForm, speciesStarters, starterPassiveAbilities } from "../data/pokemon-species";
|
||||
import { Species } from "../data/enums/species";
|
||||
import { TextStyle, addBBCodeTextObject, addTextObject, getTextColor } from "./text";
|
||||
import { TextStyle, addBBCodeTextObject, addTextObject } from "./text";
|
||||
import { Mode } from "./ui";
|
||||
import MessageUiHandler from "./message-ui-handler";
|
||||
import { Gender, getGenderColor, getGenderSymbol } from "../data/gender";
|
||||
import { allAbilities } from "../data/ability";
|
||||
import { GameMode, GameModes, gameModes } from "../game-mode";
|
||||
import { Unlockables } from "../system/unlockables";
|
||||
import { GameModes, gameModes } from "../game-mode";
|
||||
import { GrowthRate, getGrowthRateColor } from "../data/exp";
|
||||
import { DexAttr, DexAttrProps, DexEntry, StarterFormMoveData, StarterMoveset } from "../system/game-data";
|
||||
import { DexAttr, DexAttrProps, DexEntry, Passive as PassiveAttr, StarterFormMoveData, StarterMoveset } from "../system/game-data";
|
||||
import * as Utils from "../utils";
|
||||
import PokemonIconAnimHandler, { PokemonIconAnimMode } from "./pokemon-icon-anim-handler";
|
||||
import { StatsContainer } from "./stats-container";
|
||||
@ -24,17 +23,66 @@ import { Type } from "../data/type";
|
||||
import { Moves } from "../data/enums/moves";
|
||||
import { speciesEggMoves } from "../data/egg-moves";
|
||||
import { TitlePhase } from "../phases";
|
||||
import { argbFromRgba } from "@material/material-color-utilities";
|
||||
import { OptionSelectItem } from "./abstact-option-select-ui-handler";
|
||||
import { pokemonPrevolutions } from "#app/data/pokemon-evolutions";
|
||||
|
||||
export type StarterSelectCallback = (starters: Starter[]) => void;
|
||||
|
||||
export interface Starter {
|
||||
species: PokemonSpecies;
|
||||
dexAttr: bigint;
|
||||
passive: boolean;
|
||||
variant: integer;
|
||||
nature: Nature;
|
||||
moveset?: StarterMoveset;
|
||||
pokerus: boolean;
|
||||
}
|
||||
|
||||
function getPassiveCandyCount(baseValue: integer): integer {
|
||||
switch (baseValue) {
|
||||
case 1:
|
||||
return 50;
|
||||
case 2:
|
||||
return 45;
|
||||
case 3:
|
||||
return 40;
|
||||
case 4:
|
||||
return 30;
|
||||
case 5:
|
||||
return 25;
|
||||
case 6:
|
||||
return 20;
|
||||
case 7:
|
||||
return 15;
|
||||
default:
|
||||
return 10;
|
||||
}
|
||||
}
|
||||
|
||||
function getValueReductionCandyCounts(baseValue: integer): [integer, integer] {
|
||||
switch (baseValue) {
|
||||
case 1:
|
||||
return [ 30, 75];
|
||||
case 2:
|
||||
return [ 25, 60 ];
|
||||
case 3:
|
||||
return [ 20, 50 ];
|
||||
case 4:
|
||||
return [ 15, 40 ];
|
||||
case 5:
|
||||
return [ 12, 35 ];
|
||||
case 6:
|
||||
return [ 10, 30 ];
|
||||
case 7:
|
||||
return [ 8, 20 ];
|
||||
case 8:
|
||||
return [ 5, 15 ];
|
||||
default:
|
||||
return [ 3, 10 ];
|
||||
}
|
||||
}
|
||||
|
||||
const gens = [ 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX' ];
|
||||
|
||||
export default class StarterSelectUiHandler extends MessageUiHandler {
|
||||
@ -52,10 +100,10 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
||||
private pokemonUncaughtText: Phaser.GameObjects.Text;
|
||||
private pokemonAbilityLabelText: Phaser.GameObjects.Text;
|
||||
private pokemonAbilityText: Phaser.GameObjects.Text;
|
||||
private pokemonPassiveLabelText: Phaser.GameObjects.Text;
|
||||
private pokemonPassiveText: Phaser.GameObjects.Text;
|
||||
private pokemonNatureLabelText: Phaser.GameObjects.Text;
|
||||
private pokemonNatureText: BBCodeText;
|
||||
private pokemonCaughtCountLabelText: Phaser.GameObjects.Text;
|
||||
private pokemonCaughtCountText: Phaser.GameObjects.Text;
|
||||
private pokemonMovesContainer: Phaser.GameObjects.Container;
|
||||
private pokemonMoveContainers: Phaser.GameObjects.Container[];
|
||||
private pokemonMoveBgs: Phaser.GameObjects.NineSlice[];
|
||||
@ -65,6 +113,12 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
||||
private pokemonEggMoveContainers: Phaser.GameObjects.Container[];
|
||||
private pokemonEggMoveBgs: Phaser.GameObjects.NineSlice[];
|
||||
private pokemonEggMoveLabels: Phaser.GameObjects.Text[];
|
||||
private pokemonCandyIcon: Phaser.GameObjects.Sprite;
|
||||
private pokemonCandyOverlayIcon: Phaser.GameObjects.Sprite;
|
||||
private pokemonCandyCountText: Phaser.GameObjects.Text;
|
||||
private pokemonCaughtHatchedContainer: Phaser.GameObjects.Container;
|
||||
private pokemonCaughtCountText: Phaser.GameObjects.Text;
|
||||
private pokemonHatchedCountText: Phaser.GameObjects.Text;
|
||||
private genOptionsText: Phaser.GameObjects.Text;
|
||||
private instructionsText: Phaser.GameObjects.Text;
|
||||
private starterSelectMessageBox: Phaser.GameObjects.NineSlice;
|
||||
@ -109,6 +163,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
||||
private startCursorObj: Phaser.GameObjects.NineSlice;
|
||||
private starterValueLabels: Phaser.GameObjects.Text[];
|
||||
private shinyIcons: Phaser.GameObjects.Image[];
|
||||
private hiddenAbilityIcons: Phaser.GameObjects.Image[];
|
||||
|
||||
private iconAnimHandler: PokemonIconAnimHandler;
|
||||
|
||||
@ -169,51 +224,41 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
||||
this.pokemonGrowthRateText.setOrigin(0, 0);
|
||||
this.starterSelectContainer.add(this.pokemonGrowthRateText);
|
||||
|
||||
this.type1Icon = this.scene.add.sprite(8, 98, 'types');
|
||||
this.type1Icon.setScale(0.5);
|
||||
this.type1Icon.setOrigin(0, 0);
|
||||
this.starterSelectContainer.add(this.type1Icon);
|
||||
|
||||
this.type2Icon = this.scene.add.sprite(26, 98, 'types');
|
||||
this.type2Icon.setScale(0.5);
|
||||
this.type2Icon.setOrigin(0, 0);
|
||||
this.starterSelectContainer.add(this.type2Icon);
|
||||
|
||||
this.pokemonGenderText = addTextObject(this.scene, 96, 112, '', TextStyle.SUMMARY_ALT);
|
||||
this.pokemonGenderText.setOrigin(0, 0);
|
||||
this.starterSelectContainer.add(this.pokemonGenderText);
|
||||
|
||||
this.pokemonUncaughtText = addTextObject(this.scene, 6, 126, 'Uncaught', TextStyle.SUMMARY_ALT, { fontSize: '56px' });
|
||||
this.pokemonUncaughtText = addTextObject(this.scene, 6, 127, 'Uncaught', TextStyle.SUMMARY_ALT, { fontSize: '56px' });
|
||||
this.pokemonUncaughtText.setOrigin(0, 0);
|
||||
this.starterSelectContainer.add(this.pokemonUncaughtText);
|
||||
|
||||
this.pokemonAbilityLabelText = addTextObject(this.scene, 6, 126, 'Ability:', TextStyle.SUMMARY_ALT, { fontSize: '56px' });
|
||||
this.pokemonAbilityLabelText = addTextObject(this.scene, 6, 127, 'Ability:', TextStyle.SUMMARY_ALT, { fontSize: '56px' });
|
||||
this.pokemonAbilityLabelText.setOrigin(0, 0);
|
||||
this.pokemonAbilityLabelText.setVisible(false);
|
||||
this.starterSelectContainer.add(this.pokemonAbilityLabelText);
|
||||
|
||||
this.pokemonAbilityText = addTextObject(this.scene, 30, 126, '', TextStyle.SUMMARY_ALT, { fontSize: '56px' });
|
||||
this.pokemonAbilityText = addTextObject(this.scene, 31, 127, '', TextStyle.SUMMARY_ALT, { fontSize: '56px' });
|
||||
this.pokemonAbilityText.setOrigin(0, 0);
|
||||
this.starterSelectContainer.add(this.pokemonAbilityText);
|
||||
|
||||
this.pokemonNatureLabelText = addTextObject(this.scene, 6, 135, 'Nature:', TextStyle.SUMMARY_ALT, { fontSize: '56px' });
|
||||
this.pokemonPassiveLabelText = addTextObject(this.scene, 6, 136, 'Passive:', TextStyle.SUMMARY_ALT, { fontSize: '56px' });
|
||||
this.pokemonPassiveLabelText.setOrigin(0, 0);
|
||||
this.pokemonPassiveLabelText.setVisible(false);
|
||||
this.starterSelectContainer.add(this.pokemonPassiveLabelText);
|
||||
|
||||
this.pokemonPassiveText = addTextObject(this.scene, 31, 136, '', TextStyle.SUMMARY_ALT, { fontSize: '56px' });
|
||||
this.pokemonPassiveText.setOrigin(0, 0);
|
||||
this.starterSelectContainer.add(this.pokemonPassiveText);
|
||||
|
||||
this.pokemonNatureLabelText = addTextObject(this.scene, 6, 145, 'Nature:', TextStyle.SUMMARY_ALT, { fontSize: '56px' });
|
||||
this.pokemonNatureLabelText.setOrigin(0, 0);
|
||||
this.pokemonNatureLabelText.setVisible(false);
|
||||
this.starterSelectContainer.add(this.pokemonNatureLabelText);
|
||||
|
||||
this.pokemonNatureText = addBBCodeTextObject(this.scene, 30, 135, '', TextStyle.SUMMARY_ALT, { fontSize: '56px' });
|
||||
this.pokemonNatureText = addBBCodeTextObject(this.scene, 31, 145, '', TextStyle.SUMMARY_ALT, { fontSize: '56px' });
|
||||
this.pokemonNatureText.setOrigin(0, 0);
|
||||
this.starterSelectContainer.add(this.pokemonNatureText);
|
||||
|
||||
this.pokemonCaughtCountLabelText = addTextObject(this.scene, 6, 144, 'Caught/Hatched:', TextStyle.SUMMARY_ALT, { fontSize: '56px' });
|
||||
this.pokemonCaughtCountLabelText.setOrigin(0, 0);
|
||||
this.pokemonCaughtCountLabelText.setVisible(false);
|
||||
this.starterSelectContainer.add(this.pokemonCaughtCountLabelText);
|
||||
|
||||
this.pokemonCaughtCountText = addTextObject(this.scene, 58, 144, '0/0 (0)', TextStyle.SUMMARY_ALT, { fontSize: '56px' });
|
||||
this.pokemonCaughtCountText.setOrigin(0, 0);
|
||||
this.starterSelectContainer.add(this.pokemonCaughtCountText);
|
||||
|
||||
this.pokemonMoveContainers = [];
|
||||
this.pokemonMoveBgs = [];
|
||||
this.pokemonMoveLabels = [];
|
||||
@ -338,9 +383,66 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
||||
return ret;
|
||||
});
|
||||
|
||||
this.hiddenAbilityIcons = new Array(81).fill(null).map((_, i) => {
|
||||
const x = (i % 9) * 18;
|
||||
const y = Math.floor(i / 9) * 18;
|
||||
const ret = this.scene.add.image(x + 163, y + 16, 'ha_capsule');
|
||||
ret.setOrigin(0, 0);
|
||||
ret.setScale(0.5);
|
||||
ret.setVisible(false);
|
||||
this.starterSelectContainer.add(ret);
|
||||
return ret;
|
||||
});
|
||||
|
||||
this.pokemonSprite = this.scene.add.sprite(53, 63, `pkmn__sub`);
|
||||
this.starterSelectContainer.add(this.pokemonSprite);
|
||||
|
||||
this.type1Icon = this.scene.add.sprite(8, 98, 'types');
|
||||
this.type1Icon.setScale(0.5);
|
||||
this.type1Icon.setOrigin(0, 0);
|
||||
this.starterSelectContainer.add(this.type1Icon);
|
||||
|
||||
this.type2Icon = this.scene.add.sprite(26, 98, 'types');
|
||||
this.type2Icon.setScale(0.5);
|
||||
this.type2Icon.setOrigin(0, 0);
|
||||
this.starterSelectContainer.add(this.type2Icon);
|
||||
|
||||
this.pokemonCandyIcon = this.scene.add.sprite(1, 12, 'items', 'candy');
|
||||
this.pokemonCandyIcon.setScale(0.5);
|
||||
this.pokemonCandyIcon.setOrigin(0, 0);
|
||||
this.starterSelectContainer.add(this.pokemonCandyIcon);
|
||||
|
||||
this.pokemonCandyOverlayIcon = this.scene.add.sprite(1, 12, 'items', 'candy_overlay');
|
||||
this.pokemonCandyOverlayIcon.setScale(0.5);
|
||||
this.pokemonCandyOverlayIcon.setOrigin(0, 0);
|
||||
this.starterSelectContainer.add(this.pokemonCandyOverlayIcon);
|
||||
|
||||
this.pokemonCandyCountText = addTextObject(this.scene, 14, 18, 'x0', TextStyle.WINDOW_ALT, { fontSize: '56px' });
|
||||
this.pokemonCandyCountText.setOrigin(0, 0);
|
||||
this.starterSelectContainer.add(this.pokemonCandyCountText);
|
||||
|
||||
this.pokemonCaughtHatchedContainer = this.scene.add.container(2, 25);
|
||||
this.pokemonCaughtHatchedContainer.setScale(0.5);
|
||||
this.starterSelectContainer.add(this.pokemonCaughtHatchedContainer);
|
||||
|
||||
const pokemonCaughtIcon = this.scene.add.sprite(1, 0, 'items', 'pb');
|
||||
pokemonCaughtIcon.setOrigin(0, 0);
|
||||
pokemonCaughtIcon.setScale(0.75);
|
||||
this.pokemonCaughtHatchedContainer.add(pokemonCaughtIcon);
|
||||
|
||||
this.pokemonCaughtCountText = addTextObject(this.scene, 24, 4, '0', TextStyle.SUMMARY_ALT);
|
||||
this.pokemonCaughtCountText.setOrigin(0, 0);
|
||||
this.pokemonCaughtHatchedContainer.add(this.pokemonCaughtCountText);
|
||||
|
||||
const pokemonHatchedIcon = this.scene.add.sprite(1, 14, 'items', 'mystery_egg');
|
||||
pokemonHatchedIcon.setOrigin(0, 0);
|
||||
pokemonHatchedIcon.setScale(0.75);
|
||||
this.pokemonCaughtHatchedContainer.add(pokemonHatchedIcon);
|
||||
|
||||
this.pokemonHatchedCountText = addTextObject(this.scene, 24, 19, '0', TextStyle.SUMMARY_ALT);
|
||||
this.pokemonHatchedCountText.setOrigin(0, 0);
|
||||
this.pokemonCaughtHatchedContainer.add(this.pokemonHatchedCountText);
|
||||
|
||||
this.pokemonMovesContainer = this.scene.add.container(102, 16);
|
||||
this.pokemonMovesContainer.setScale(0.5);
|
||||
|
||||
@ -373,7 +475,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
||||
this.pokemonEggMovesContainer = this.scene.add.container(102, 85);
|
||||
this.pokemonEggMovesContainer.setScale(0.375);
|
||||
|
||||
const eggMovesLabel = addTextObject(this.scene, -46, 0, 'Egg Moves', TextStyle.SUMMARY_ALT);
|
||||
const eggMovesLabel = addTextObject(this.scene, -46, 0, 'Egg Moves', TextStyle.WINDOW_ALT);
|
||||
eggMovesLabel.setOrigin(0.5, 0);
|
||||
|
||||
this.pokemonEggMovesContainer.add(eggMovesLabel);
|
||||
@ -622,6 +724,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
||||
ui.playSelect();
|
||||
} else
|
||||
ui.playError();
|
||||
return true;
|
||||
},
|
||||
overrideSound: true
|
||||
},
|
||||
@ -630,6 +733,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
||||
handler: () => {
|
||||
this.toggleStatsMode();
|
||||
ui.setMode(Mode.STARTER_SELECT);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
];
|
||||
@ -639,7 +743,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
||||
ui.showText('Select a move to swap out.', null, () => {
|
||||
ui.setModeWithoutClear(Mode.OPTION_SELECT, {
|
||||
options: moveset.map((m: Moves, i: number) => {
|
||||
return {
|
||||
const option: OptionSelectItem = {
|
||||
label: allMoves[m].name,
|
||||
handler: () => {
|
||||
ui.setMode(Mode.STARTER_SELECT).then(() => {
|
||||
@ -647,29 +751,37 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
||||
ui.setModeWithoutClear(Mode.OPTION_SELECT, {
|
||||
options: this.speciesStarterMoves.filter((sm: Moves) => sm !== m).map(sm => {
|
||||
// make an option for each available starter move
|
||||
return {
|
||||
const option = {
|
||||
label: allMoves[sm].name,
|
||||
handler: () => {
|
||||
this.switchMoveHandler(i, sm, m)
|
||||
showSwapOptions(this.starterMoveset);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
return option;
|
||||
}).concat({
|
||||
label: 'Cancel',
|
||||
handler: () => showSwapOptions(this.starterMoveset)
|
||||
handler: () => {
|
||||
showSwapOptions(this.starterMoveset);
|
||||
return true;
|
||||
}
|
||||
}),
|
||||
maxOptions: 8,
|
||||
yOffset: 19
|
||||
});
|
||||
});
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
return option;
|
||||
}).concat({
|
||||
label: 'Cancel',
|
||||
handler: () => {
|
||||
this.clearText();
|
||||
ui.setMode(Mode.STARTER_SELECT);
|
||||
return true;
|
||||
}
|
||||
}),
|
||||
maxOptions: 8,
|
||||
@ -682,6 +794,105 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
||||
label: 'Manage Moves',
|
||||
handler: () => {
|
||||
showSwapOptions(this.starterMoveset);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
const starterData = this.scene.gameData.starterData[this.lastSpecies.speciesId];
|
||||
const candyCount = starterData.candyCount;
|
||||
const passiveAttr = starterData.passiveAttr;
|
||||
if (passiveAttr & PassiveAttr.UNLOCKED) {
|
||||
if (!(passiveAttr & PassiveAttr.ENABLED)) {
|
||||
options.push({
|
||||
label: 'Enable Passive',
|
||||
handler: () => {
|
||||
starterData.passiveAttr |= PassiveAttr.ENABLED;
|
||||
ui.setMode(Mode.STARTER_SELECT);
|
||||
this.setSpeciesDetails(this.lastSpecies, undefined, undefined, undefined, undefined, undefined);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
options.push({
|
||||
label: 'Disable Passive',
|
||||
handler: () => {
|
||||
starterData.passiveAttr ^= PassiveAttr.ENABLED;
|
||||
ui.setMode(Mode.STARTER_SELECT);
|
||||
this.setSpeciesDetails(this.lastSpecies, undefined, undefined, undefined, undefined, undefined);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
const showUseCandies = () => {
|
||||
const options = [];
|
||||
if (!(passiveAttr & PassiveAttr.UNLOCKED)) {
|
||||
const passiveCost = getPassiveCandyCount(speciesStarters[this.lastSpecies.speciesId]);
|
||||
options.push({
|
||||
label: `x${passiveCost} Unlock Passive (${allAbilities[starterPassiveAbilities[this.lastSpecies.speciesId]].name})`,
|
||||
handler: () => {
|
||||
if (candyCount >= passiveCost) {
|
||||
starterData.passiveAttr |= PassiveAttr.UNLOCKED | PassiveAttr.ENABLED;
|
||||
starterData.candyCount -= passiveCost;
|
||||
this.pokemonCandyCountText.setText(`x${starterData.candyCount}`);
|
||||
this.scene.gameData.saveSystem().then(success => {
|
||||
if (!success)
|
||||
return this.scene.reset(true);
|
||||
});
|
||||
ui.setMode(Mode.STARTER_SELECT);
|
||||
this.setSpeciesDetails(this.lastSpecies, undefined, undefined, undefined, undefined, undefined);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
item: 'candy',
|
||||
itemArgs: starterColors[this.lastSpecies.speciesId]
|
||||
});
|
||||
}
|
||||
const valueReduction = starterData.valueReduction;
|
||||
if (valueReduction < 2) {
|
||||
const reductionCost = getValueReductionCandyCounts(speciesStarters[this.lastSpecies.speciesId])[valueReduction];
|
||||
options.push({
|
||||
label: `x${reductionCost} Reduce Cost`,
|
||||
handler: () => {
|
||||
if (candyCount >= reductionCost) {
|
||||
starterData.valueReduction++;
|
||||
starterData.candyCount -= reductionCost;
|
||||
this.pokemonCandyCountText.setText(`x${starterData.candyCount}`);
|
||||
this.scene.gameData.saveSystem().then(success => {
|
||||
if (!success)
|
||||
return this.scene.reset(true);
|
||||
});
|
||||
this.updateStarterValueLabel(this.cursor);
|
||||
this.tryUpdateValue(0);
|
||||
ui.setMode(Mode.STARTER_SELECT);
|
||||
this.scene.playSound('buy');
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
item: 'candy',
|
||||
itemArgs: starterColors[this.lastSpecies.speciesId]
|
||||
});
|
||||
}
|
||||
options.push({
|
||||
label: 'Cancel',
|
||||
handler: () => {
|
||||
ui.setMode(Mode.STARTER_SELECT);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
ui.setModeWithoutClear(Mode.OPTION_SELECT, {
|
||||
options: options,
|
||||
yOffset: 47
|
||||
});
|
||||
};
|
||||
if (!pokemonPrevolutions.hasOwnProperty(this.lastSpecies.speciesId)) {
|
||||
options.push({
|
||||
label: 'Use Candies',
|
||||
handler: () => {
|
||||
ui.setMode(Mode.STARTER_SELECT).then(() => showUseCandies());
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -689,6 +900,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
||||
label: 'Cancel',
|
||||
handler: () => {
|
||||
ui.setMode(Mode.STARTER_SELECT);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
ui.setModeWithoutClear(Mode.OPTION_SELECT, {
|
||||
@ -809,22 +1021,22 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
||||
// species has different forms
|
||||
if (pokemonFormLevelMoves.hasOwnProperty(speciesId)) {
|
||||
// starterMoveData doesn't have base form moves or is using the single form format
|
||||
if (!this.scene.gameData.starterMoveData.hasOwnProperty(speciesId) || Array.isArray(this.scene.gameData.starterMoveData[speciesId]))
|
||||
this.scene.gameData.starterMoveData[speciesId] = { [props.formIndex]: this.starterMoveset.slice(0) as StarterMoveset };
|
||||
const starterMoveData = this.scene.gameData.starterMoveData[speciesId][props.formIndex];
|
||||
if (!this.scene.gameData.starterData[speciesId].moveset || Array.isArray(this.scene.gameData.starterData[speciesId].moveset))
|
||||
this.scene.gameData.starterData[speciesId].moveset = { [props.formIndex]: this.starterMoveset.slice(0) as StarterMoveset };
|
||||
const starterMoveData = this.scene.gameData.starterData[speciesId].moveset[props.formIndex];
|
||||
|
||||
// starterMoveData doesn't have active form moves
|
||||
if (!starterMoveData.hasOwnProperty(props.formIndex))
|
||||
this.scene.gameData.starterMoveData[speciesId][props.formIndex] = this.starterMoveset.slice(0) as StarterMoveset;
|
||||
this.scene.gameData.starterData[speciesId].moveset[props.formIndex] = this.starterMoveset.slice(0) as StarterMoveset;
|
||||
|
||||
// does the species' starter move data have its form's starter moves and has it been updated
|
||||
if (starterMoveData.hasOwnProperty(props.formIndex)) {
|
||||
// active form move hasn't been updated
|
||||
if (starterMoveData[props.formIndex][existingMoveIndex] !== newMove)
|
||||
this.scene.gameData.starterMoveData[speciesId][props.formIndex] = this.starterMoveset.slice(0) as StarterMoveset;
|
||||
this.scene.gameData.starterData[speciesId].moveset[props.formIndex] = this.starterMoveset.slice(0) as StarterMoveset;
|
||||
}
|
||||
} else
|
||||
this.scene.gameData.starterMoveData[speciesId] = this.starterMoveset.slice(0) as StarterMoveset;
|
||||
this.scene.gameData.starterData[speciesId].moveset = this.starterMoveset.slice(0) as StarterMoveset;
|
||||
this.setSpeciesDetails(this.lastSpecies, undefined, undefined, undefined, undefined, undefined, false);
|
||||
}
|
||||
|
||||
@ -902,31 +1114,11 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
||||
for (let s = 0; s < 81; s++) {
|
||||
const speciesId = s < genLimit ? this.genSpecies[genCursorWithScroll][s].speciesId : 0 as Species;
|
||||
const slotVisible = !!speciesId;
|
||||
if (slotVisible) {
|
||||
const baseStarterValue = speciesStarters[speciesId];
|
||||
const starterValue = slotVisible ? this.scene.gameData.getSpeciesStarterValue(speciesId) : 0;
|
||||
let valueStr = starterValue.toString();
|
||||
if (valueStr.startsWith('0.'))
|
||||
valueStr = valueStr.slice(1);
|
||||
this.starterValueLabels[s].setText(valueStr);
|
||||
let textStyle: TextStyle;
|
||||
switch (baseStarterValue - starterValue) {
|
||||
case 0:
|
||||
textStyle = TextStyle.WINDOW;
|
||||
break;
|
||||
case 1:
|
||||
case 0.5:
|
||||
textStyle = TextStyle.SUMMARY_BLUE;
|
||||
break;
|
||||
default:
|
||||
textStyle = TextStyle.SUMMARY_GOLD;
|
||||
break;
|
||||
}
|
||||
this.starterValueLabels[s].setColor(this.getTextColor(textStyle));
|
||||
this.starterValueLabels[s].setShadowColor(this.getTextColor(textStyle, true));
|
||||
}
|
||||
if (slotVisible)
|
||||
this.updateStarterValueLabel(s);
|
||||
this.starterValueLabels[s].setVisible(slotVisible);
|
||||
this.shinyIcons[s].setVisible(slotVisible && !!this.scene.gameData.dexData[speciesId].caughtAttr && !!(this.scene.gameData.dexData[speciesId].caughtAttr & DexAttr.SHINY));
|
||||
this.hiddenAbilityIcons[s].setVisible(slotVisible && !!this.scene.gameData.dexData[speciesId].caughtAttr && !!(this.scene.gameData.dexData[speciesId].caughtAttr & DexAttr.ABILITY_HIDDEN));
|
||||
}
|
||||
} else {
|
||||
changed = super.setCursor(cursor);
|
||||
@ -1007,16 +1199,31 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
||||
this.pokemonNameText.setText(species.name);
|
||||
|
||||
if (this.speciesStarterDexEntry?.caughtAttr) {
|
||||
const colorScheme = starterColors[species.speciesId];
|
||||
|
||||
this.pokemonGrowthRateText.setText(Utils.toReadableString(GrowthRate[species.growthRate]));
|
||||
this.pokemonGrowthRateText.setColor(getGrowthRateColor(species.growthRate));
|
||||
this.pokemonGrowthRateText.setShadowColor(getGrowthRateColor(species.growthRate, true));
|
||||
this.pokemonGrowthRateLabelText.setVisible(true);
|
||||
this.pokemonUncaughtText.setVisible(false);
|
||||
this.pokemonAbilityLabelText.setVisible(true);
|
||||
this.pokemonPassiveLabelText.setVisible(true);
|
||||
this.pokemonNatureLabelText.setVisible(true);
|
||||
this.pokemonCaughtCountLabelText.setVisible(true);
|
||||
this.pokemonCaughtCountText.setText(`${this.speciesStarterDexEntry.caughtCount}/${this.speciesStarterDexEntry.hatchedCount} (${this.speciesStarterDexEntry.caughtCount + this.speciesStarterDexEntry.hatchedCount})`);
|
||||
this.pokemonCaughtCountText.setVisible(true);
|
||||
this.pokemonCaughtCountText.setText(`${this.speciesStarterDexEntry.caughtCount}`);
|
||||
this.pokemonHatchedCountText.setText(`${this.speciesStarterDexEntry.hatchedCount}`);
|
||||
this.pokemonCaughtHatchedContainer.setVisible(true);
|
||||
if (pokemonPrevolutions.hasOwnProperty(species.speciesId)) {
|
||||
this.pokemonCaughtHatchedContainer.setY(16);
|
||||
[ this.pokemonCandyIcon, this.pokemonCandyOverlayIcon, this.pokemonCandyCountText ].map(c => c.setVisible(false));
|
||||
} else {
|
||||
this.pokemonCaughtHatchedContainer.setY(25);
|
||||
this.pokemonCandyIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(colorScheme[0])));
|
||||
this.pokemonCandyIcon.setVisible(true);
|
||||
this.pokemonCandyOverlayIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(colorScheme[1])));
|
||||
this.pokemonCandyOverlayIcon.setVisible(true);
|
||||
this.pokemonCandyCountText.setText(`x${this.scene.gameData.starterData[species.speciesId].candyCount}`);
|
||||
this.pokemonCandyCountText.setVisible(true);
|
||||
}
|
||||
this.iconAnimHandler.addOrUpdate(this.starterSelectGenIconContainers[species.generation - 1].getAt(this.genSpecies[species.generation - 1].indexOf(species)) as Phaser.GameObjects.Sprite, PokemonIconAnimMode.PASSIVE);
|
||||
|
||||
let starterIndex = -1;
|
||||
@ -1056,9 +1263,12 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
||||
this.type2Icon.setVisible(false);
|
||||
this.pokemonUncaughtText.setVisible(true);
|
||||
this.pokemonAbilityLabelText.setVisible(false);
|
||||
this.pokemonPassiveLabelText.setVisible(false);
|
||||
this.pokemonNatureLabelText.setVisible(false);
|
||||
this.pokemonCaughtCountLabelText.setVisible(false);
|
||||
this.pokemonCaughtCountText.setVisible(false);
|
||||
this.pokemonCaughtHatchedContainer.setVisible(false);
|
||||
this.pokemonCandyIcon.setVisible(false);
|
||||
this.pokemonCandyOverlayIcon.setVisible(false);
|
||||
this.pokemonCandyCountText.setVisible(false);
|
||||
|
||||
const defaultDexAttr = this.scene.gameData.getSpeciesDefaultDexAttr(species, true);
|
||||
const defaultNature = this.scene.gameData.getSpeciesDefaultNature(species);
|
||||
@ -1076,9 +1286,12 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
||||
this.type2Icon.setVisible(false);
|
||||
this.pokemonUncaughtText.setVisible(!!species);
|
||||
this.pokemonAbilityLabelText.setVisible(false);
|
||||
this.pokemonPassiveLabelText.setVisible(false);
|
||||
this.pokemonNatureLabelText.setVisible(false);
|
||||
this.pokemonCaughtCountLabelText.setVisible(false);
|
||||
this.pokemonCaughtCountText.setVisible(false);
|
||||
this.pokemonCaughtHatchedContainer.setVisible(false);
|
||||
this.pokemonCandyIcon.setVisible(false);
|
||||
this.pokemonCandyOverlayIcon.setVisible(false);
|
||||
this.pokemonCandyCountText.setVisible(false);
|
||||
|
||||
this.setSpeciesDetails(species, false, 0, false, 0, 0);
|
||||
this.pokemonSprite.clearTint();
|
||||
@ -1185,6 +1398,11 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
||||
this.pokemonAbilityText.setColor(this.getTextColor(!isHidden ? TextStyle.SUMMARY_ALT : TextStyle.SUMMARY_GOLD));
|
||||
this.pokemonAbilityText.setShadowColor(this.getTextColor(!isHidden ? TextStyle.SUMMARY_ALT : TextStyle.SUMMARY_GOLD, true));
|
||||
|
||||
const passiveAttr = this.scene.gameData.starterData[species.speciesId].passiveAttr;
|
||||
this.pokemonPassiveText.setText(passiveAttr & PassiveAttr.UNLOCKED ? passiveAttr & PassiveAttr.ENABLED ? allAbilities[starterPassiveAbilities[this.lastSpecies.speciesId]].name : 'Disabled' : 'Locked');
|
||||
this.pokemonPassiveText.setColor(this.getTextColor(passiveAttr === (PassiveAttr.UNLOCKED | PassiveAttr.ENABLED) ? TextStyle.SUMMARY_ALT : TextStyle.SUMMARY_GRAY));
|
||||
this.pokemonPassiveText.setShadowColor(this.getTextColor(passiveAttr === (PassiveAttr.UNLOCKED | PassiveAttr.ENABLED) ? TextStyle.SUMMARY_ALT : TextStyle.SUMMARY_GRAY, true));
|
||||
|
||||
this.pokemonNatureText.setText(getNatureName(natureIndex as unknown as Nature, true, true, false, this.scene.uiTheme));
|
||||
|
||||
let levelMoves: LevelMoves;
|
||||
@ -1195,18 +1413,18 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
||||
this.speciesStarterMoves.push(...levelMoves.filter(lm => lm[0] <= 5).map(lm => lm[1]));
|
||||
if (speciesEggMoves.hasOwnProperty(species.speciesId)) {
|
||||
for (let em = 0; em < 4; em++) {
|
||||
if (this.scene.gameData.starterEggMoveData[species.speciesId] & Math.pow(2, em))
|
||||
if (this.scene.gameData.starterData[species.speciesId].eggMoves & Math.pow(2, em))
|
||||
this.speciesStarterMoves.push(speciesEggMoves[species.speciesId][em]);
|
||||
}
|
||||
}
|
||||
|
||||
const speciesMoveData = this.scene.gameData.starterMoveData[species.speciesId];
|
||||
const speciesMoveData = this.scene.gameData.starterData[species.speciesId].moveset;
|
||||
let moveData: StarterMoveset = speciesMoveData
|
||||
? Array.isArray(speciesMoveData)
|
||||
? speciesMoveData as StarterMoveset
|
||||
: (speciesMoveData as StarterFormMoveData)[formIndex]
|
||||
: null;
|
||||
const availableStarterMoves = this.speciesStarterMoves.concat(speciesEggMoves.hasOwnProperty(species.speciesId) ? speciesEggMoves[species.speciesId].filter((_, em: integer) => this.scene.gameData.starterEggMoveData[species.speciesId] & Math.pow(2, em)) : []);
|
||||
const availableStarterMoves = this.speciesStarterMoves.concat(speciesEggMoves.hasOwnProperty(species.speciesId) ? speciesEggMoves[species.speciesId].filter((_, em: integer) => this.scene.gameData.starterData[species.speciesId].eggMoves & Math.pow(2, em)) : []);
|
||||
this.starterMoveset = (moveData || (this.speciesStarterMoves.slice(0, 4) as StarterMoveset)).filter(m => availableStarterMoves.find(sm => sm === m)) as StarterMoveset;
|
||||
// Consolidate move data if it contains an incompatible move
|
||||
if (this.starterMoveset.length < 4 && this.starterMoveset.length < availableStarterMoves.length)
|
||||
@ -1216,6 +1434,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
||||
this.setTypeIcons(speciesForm.type1, speciesForm.type2);
|
||||
} else {
|
||||
this.pokemonAbilityText.setText('');
|
||||
this.pokemonPassiveText.setText('');
|
||||
this.pokemonNatureText.setText('');
|
||||
this.setTypeIcons(null, null);
|
||||
}
|
||||
@ -1225,6 +1444,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
||||
this.pokemonNumberText.setShadowColor(this.getTextColor(TextStyle.SUMMARY, true));
|
||||
this.pokemonGenderText.setText('');
|
||||
this.pokemonAbilityText.setText('');
|
||||
this.pokemonPassiveText.setText('');
|
||||
this.pokemonNatureText.setText('');
|
||||
this.setTypeIcons(null, null);
|
||||
}
|
||||
@ -1243,7 +1463,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
||||
|
||||
for (let em = 0; em < 4; em++) {
|
||||
const eggMove = hasEggMoves ? allMoves[speciesEggMoves[species.speciesId][em]] : null;
|
||||
const eggMoveUnlocked = eggMove && this.scene.gameData.starterEggMoveData.hasOwnProperty(species.speciesId) && this.scene.gameData.starterEggMoveData[species.speciesId] & Math.pow(2, em);
|
||||
const eggMoveUnlocked = eggMove && this.scene.gameData.starterData[species.speciesId].eggMoves & Math.pow(2, em);
|
||||
this.pokemonEggMoveBgs[em].setFrame(Type[eggMove ? eggMove.type : Type.UNKNOWN].toString().toLowerCase());
|
||||
this.pokemonEggMoveLabels[em].setText(eggMove && eggMoveUnlocked ? eggMove.name : '???');
|
||||
}
|
||||
@ -1281,6 +1501,31 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
||||
this.tryUpdateValue();
|
||||
}
|
||||
|
||||
updateStarterValueLabel(cursor: integer): void {
|
||||
const speciesId = this.genSpecies[this.getGenCursorWithScroll()][cursor].speciesId;
|
||||
const baseStarterValue = speciesStarters[speciesId];
|
||||
const starterValue = this.scene.gameData.getSpeciesStarterValue(speciesId);
|
||||
let valueStr = starterValue.toString();
|
||||
if (valueStr.startsWith('0.'))
|
||||
valueStr = valueStr.slice(1);
|
||||
this.starterValueLabels[cursor].setText(valueStr);
|
||||
let textStyle: TextStyle;
|
||||
switch (baseStarterValue - starterValue) {
|
||||
case 0:
|
||||
textStyle = TextStyle.WINDOW;
|
||||
break;
|
||||
case 1:
|
||||
case 0.5:
|
||||
textStyle = TextStyle.SUMMARY_BLUE;
|
||||
break;
|
||||
default:
|
||||
textStyle = TextStyle.SUMMARY_GOLD;
|
||||
break;
|
||||
}
|
||||
this.starterValueLabels[cursor].setColor(this.getTextColor(textStyle));
|
||||
this.starterValueLabels[cursor].setShadowColor(this.getTextColor(textStyle, true));
|
||||
}
|
||||
|
||||
tryUpdateValue(add?: integer): boolean {
|
||||
const value = this.starterGens.reduce((total: integer, gen: integer, i: integer) => total += this.scene.gameData.getSpeciesStarterValue(this.genSpecies[gen][this.starterCursors[i]].speciesId), 0);
|
||||
const newValue = value + (add || 0);
|
||||
@ -1331,6 +1576,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
||||
return {
|
||||
species: starterSpecies,
|
||||
dexAttr: thisObj.starterAttr[i],
|
||||
passive: !(thisObj.scene.gameData.starterData[starterSpecies.speciesId].passiveAttr ^ (PassiveAttr.ENABLED | PassiveAttr.UNLOCKED)),
|
||||
variant: 0,
|
||||
nature: thisObj.starterNatures[i] as Nature,
|
||||
moveset: thisObj.starterMovesets[i],
|
||||
pokerus: !![ 0, 1, 2 ].filter(n => thisObj.pokerusGens[n] === starterSpecies.generation - 1 && thisObj.pokerusCursors[n] === thisObj.genSpecies[starterSpecies.generation - 1].indexOf(starterSpecies)).length
|
||||
|
@ -18,6 +18,7 @@ export enum TextStyle {
|
||||
SUMMARY_BLUE,
|
||||
SUMMARY_PINK,
|
||||
SUMMARY_GOLD,
|
||||
SUMMARY_GRAY,
|
||||
MONEY,
|
||||
SETTINGS_LABEL,
|
||||
SETTINGS_SELECTED,
|
||||
@ -75,10 +76,14 @@ function getTextStyleOptions(style: TextStyle, uiTheme: UiTheme, extraStyleOptio
|
||||
|
||||
switch (style) {
|
||||
case TextStyle.SUMMARY:
|
||||
case TextStyle.SUMMARY_ALT:
|
||||
case TextStyle.SUMMARY_BLUE:
|
||||
case TextStyle.SUMMARY_RED:
|
||||
case TextStyle.SUMMARY_PINK:
|
||||
case TextStyle.SUMMARY_GOLD:
|
||||
case TextStyle.SUMMARY_GRAY:
|
||||
case TextStyle.WINDOW:
|
||||
case TextStyle.WINDOW_ALT:
|
||||
case TextStyle.MESSAGE:
|
||||
case TextStyle.SETTINGS_LABEL:
|
||||
case TextStyle.SETTINGS_SELECTED:
|
||||
@ -152,7 +157,9 @@ export function getTextColor(textStyle: TextStyle, shadow?: boolean, uiTheme: Ui
|
||||
return !shadow ? '#f89890' : '#984038';
|
||||
case TextStyle.SUMMARY_GOLD:
|
||||
case TextStyle.MONEY:
|
||||
return !shadow ? '#e8e8a8' : '#a0a060'
|
||||
return !shadow ? '#e8e8a8' : '#a0a060';
|
||||
case TextStyle.SUMMARY_GRAY:
|
||||
return !shadow ? '#a0a0a0' : '#636363';
|
||||
case TextStyle.SETTINGS_LABEL:
|
||||
return !shadow ? '#f8b050' : '#c07800';
|
||||
case TextStyle.SETTINGS_SELECTED:
|
||||
|
10
src/ui/ui.ts
@ -34,6 +34,7 @@ import SaveSlotSelectUiHandler from './save-slot-select-ui-handler';
|
||||
import TitleUiHandler from './title-ui-handler';
|
||||
import SavingIconHandler from './saving-icon-handler';
|
||||
import UnavailableModalUiHandler from './unavailable-modal-ui-handler';
|
||||
import OutdatedModalUiHandler from './outdated-modal-ui-handler';
|
||||
|
||||
export enum Mode {
|
||||
MESSAGE,
|
||||
@ -63,7 +64,8 @@ export enum Mode {
|
||||
LOGIN_FORM,
|
||||
REGISTRATION_FORM,
|
||||
LOADING,
|
||||
UNAVAILABLE
|
||||
UNAVAILABLE,
|
||||
OUTDATED
|
||||
};
|
||||
|
||||
const transitionModes = [
|
||||
@ -90,7 +92,8 @@ const noTransitionModes = [
|
||||
Mode.LOGIN_FORM,
|
||||
Mode.REGISTRATION_FORM,
|
||||
Mode.LOADING,
|
||||
Mode.UNAVAILABLE
|
||||
Mode.UNAVAILABLE,
|
||||
Mode.OUTDATED
|
||||
];
|
||||
|
||||
export default class UI extends Phaser.GameObjects.Container {
|
||||
@ -141,7 +144,8 @@ export default class UI extends Phaser.GameObjects.Container {
|
||||
new LoginFormUiHandler(scene),
|
||||
new RegistrationFormUiHandler(scene),
|
||||
new LoadingModalUiHandler(scene),
|
||||
new UnavailableModalUiHandler(scene)
|
||||
new UnavailableModalUiHandler(scene),
|
||||
new OutdatedModalUiHandler(scene)
|
||||
];
|
||||
}
|
||||
|
||||
|
10
src/utils.ts
@ -312,3 +312,13 @@ export function deltaRgb(rgb1: integer[], rgb2: integer[]): integer {
|
||||
|
||||
return Math.ceil(Math.sqrt(2 * drp2 + 4 * dgp2 + 3 * dbp2 + t * (drp2 - dbp2) / 256));
|
||||
}
|
||||
|
||||
export function rgbHexToRgba(hex: string) {
|
||||
const color = hex.match(/^([\da-f]{2})([\da-f]{2})([\da-f]{2})$/i);
|
||||
return {
|
||||
r: parseInt(color[1], 16),
|
||||
g: parseInt(color[2], 16),
|
||||
b: parseInt(color[3], 16),
|
||||
a: 255
|
||||
};
|
||||
}
|