Add candy system for passives and value reductions

This commit is contained in:
Flashfyre 2024-04-13 18:59:58 -04:00
parent ccf956a63c
commit 824a73ef71
22 changed files with 2497 additions and 1848 deletions

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

(image error) Size: 46 KiB

After

(image error) Size: 46 KiB

Binary file not shown.

After

(image error) Size: 402 B

Binary file not shown.

After

(image error) Size: 263 B

Binary file not shown.

After

(image error) Size: 252 B

File diff suppressed because one or more lines are too long

@ -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';
@ -57,6 +57,7 @@ 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 +82,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 +148,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;
@ -359,6 +366,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 +451,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 +484,53 @@ export default class BattleScene extends SceneBase {
this.updateScoreText();
}
initExpSprites(): void {
initExpSprites(): Promise<void> {
return new Promise(resolve => {
if (expSpriteKeys.length)
return;
fetch('./exp_sprites.json').then(res => res.json()).then(keys => {
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();
});
});
}
@ -1075,6 +1128,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);
}

@ -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
};

@ -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 {
@ -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,
@ -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,
@ -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,

@ -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() {

@ -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;
@ -727,7 +731,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
}
canApplyPassive(): boolean {
return this.isBoss();
return this.passive || this.isBoss();
}
canApplyAbility(passive: boolean = false): boolean {
@ -1237,8 +1241,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) {
@ -1247,8 +1249,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;
@ -1275,6 +1275,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);
@ -2156,7 +2160,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));

@ -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";
@ -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,
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 = {
@ -377,6 +396,7 @@ export class SelectGenderPhase extends Phase {
this.scene.gameData.gender = PlayerGender.MALE;
this.scene.gameData.saveSetting(Setting.Player_Gender, 0);
this.scene.gameData.saveSystem().then(() => this.end());
return true;
}
},
{
@ -385,6 +405,7 @@ export class SelectGenderPhase extends Phase {
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 +458,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)

@ -43,6 +43,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 +69,7 @@ interface SystemSaveData {
secretId: integer;
gender: PlayerGender;
dexData: DexData;
starterMoveData: StarterMoveData;
starterEggMoveData: StarterEggMoveData;
starterData: StarterData;
gameStats: GameStats;
unlocks: Unlocks;
achvUnlocks: AchvUnlocks;
@ -145,16 +149,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 +180,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 +199,7 @@ export class GameData {
public dexData: DexData;
private defaultDexData: DexData;
public starterMoveData: StarterMoveData;
public starterEggMoveData: StarterEggMoveData;
public starterData: StarterData;
public gameStats: GameStats;
@ -200,8 +216,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 +233,7 @@ export class GameData {
};
this.eggs = [];
this.initDexData();
this.initEggMoveData();
this.initStarterData();
}
public saveSystem(): Promise<boolean> {
@ -234,8 +249,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,
@ -297,17 +311,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 +369,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 +419,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 +937,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 +982,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 +1006,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 +1027,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 +1040,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 +1142,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 +1151,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.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

@ -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;
}
}
];

@ -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,65 @@ 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";
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 100;
case 2:
return 75;
case 3:
return 50;
case 4:
return 40
case 5:
return 30;
case 6:
return 20;
case 7:
return 15;
default:
return 10;
}
}
function getValueReductionCandyCounts(baseValue: integer): [integer, integer] {
switch (baseValue) {
case 1:
return [ 50, 150 ];
case 2:
return [ 30, 100 ];
case 3:
return [ 25, 75 ];
case 4:
return [ 20, 60 ];
case 5:
return [ 15, 50 ];
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 +99,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 +112,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;
@ -179,41 +232,77 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
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.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 = [];
@ -373,7 +462,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 +711,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
ui.playSelect();
} else
ui.playError();
return true;
},
overrideSound: true
},
@ -630,6 +720,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
handler: () => {
this.toggleStatsMode();
ui.setMode(Mode.STARTER_SELECT);
return true;
}
}
];
@ -639,7 +730,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 +738,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 +781,103 @@ 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
});
};
options.push({
label: 'Use Candies',
handler: () => {
ui.setMode(Mode.STARTER_SELECT).then(() => showUseCandies());
return true;
}
});
}
@ -689,6 +885,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
label: 'Cancel',
handler: () => {
ui.setMode(Mode.STARTER_SELECT);
return true;
}
});
ui.setModeWithoutClear(Mode.OPTION_SELECT, {
@ -809,22 +1006,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,29 +1099,8 @@ 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));
}
@ -1007,16 +1183,25 @@ 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);
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 +1241,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 +1264,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 +1376,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 +1391,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 +1412,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 +1422,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 +1441,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 +1479,31 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.tryUpdateValue();
}
updateStarterValueLabel(cursor: integer): void {
const speciesId = this.genSpecies[this.genCursor][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 +1554,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:

@ -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
};
}