From 649717a3cd5b3fddd98b2f3f97e193cd29161f5c Mon Sep 17 00:00:00 2001 From: Flashfyre Date: Sun, 25 Feb 2024 12:45:41 -0500 Subject: [PATCH] Add egg moves logic --- src/data/egg-moves.ts | 43 ++++++++++++++++++++++ src/data/pokemon-species.ts | 18 ++++++++-- src/egg-hatch-phase.ts | 16 +++++++-- src/phases.ts | 2 +- src/pokemon.ts | 2 +- src/system/game-data.ts | 55 ++++++++++++++++++++++++++++- src/ui/starter-select-ui-handler.ts | 31 +++++++++++----- 7 files changed, 151 insertions(+), 16 deletions(-) create mode 100644 src/data/egg-moves.ts diff --git a/src/data/egg-moves.ts b/src/data/egg-moves.ts new file mode 100644 index 000000000..0f2a3c005 --- /dev/null +++ b/src/data/egg-moves.ts @@ -0,0 +1,43 @@ +import { Moves } from "./enums/moves"; +import { Species } from "./enums/species"; +import { allMoves } from "./move"; +import * as Utils from "../utils"; + + +export const speciesEggMoves = { +}; + +function parseEggMoves(content: string): void { + let output = ''; + + const speciesNames = Utils.getEnumKeys(Species); + const speciesValues = Utils.getEnumValues(Species); + const lines = content.split(/\n/g); + + lines.forEach((line, l) => { + const cols = line.split(',').slice(0, 5); + const moveNames = allMoves.map(m => m.name.replace(/ \([A-Z]\)$/, '').toLowerCase()); + const enumSpeciesName = cols[0].toUpperCase().replace(/[ -]/g, '_'); + const species = speciesValues[speciesNames.findIndex(s => s === enumSpeciesName)]; + + let eggMoves: Moves[] = []; + + for (let m = 0; m < 4; m++) { + const moveName = cols[m + 1].trim(); + const moveIndex = moveName !== 'N/A' ? moveNames.findIndex(mn => mn === moveName.toLowerCase()) : -1; + eggMoves.push(moveIndex > -1 ? moveIndex as Moves : Moves.NONE); + } + + if (eggMoves.find(m => m !== Moves.NONE)) + output += `[Species.${Species[species]}]: [ ${eggMoves.map(m => `Moves.${Moves[m]}`).join(', ')} ],\n`; + }); + + console.log(output); +} + +const eggMovesStr = ``; +if (eggMovesStr) { + setTimeout(() => { + parseEggMoves(eggMovesStr); + }, 1000); +} \ No newline at end of file diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts index 9f3cb1fbf..7dc0d56d0 100644 --- a/src/data/pokemon-species.ts +++ b/src/data/pokemon-species.ts @@ -7,7 +7,8 @@ import { Type } from './type'; import { LevelMoves, pokemonFormLevelMoves, pokemonFormLevelMoves as pokemonSpeciesFormLevelMoves, pokemonSpeciesLevelMoves } from './pokemon-level-moves'; import { uncatchableSpecies } from './biomes'; import * as Utils from '../utils'; -import { StarterMoveset } from '../system/game-data'; +import { StarterEggMoveData, StarterMoveset } from '../system/game-data'; +import { speciesEggMoves } from './egg-moves'; export enum Region { NORMAL, @@ -98,6 +99,13 @@ export abstract class PokemonSpeciesForm { this.genderDiffs = genderDiffs; } + getRootSpeciesId(): Species { + let ret = this.speciesId; + while (pokemonPrevolutions.hasOwnProperty(ret)) + ret = pokemonPrevolutions[ret]; + return ret; + } + isOfType(type: integer): boolean { return this.type1 === type || (this.type2 !== null && this.type2 === type); } @@ -261,8 +269,14 @@ export abstract class PokemonSpeciesForm { return ret; } - validateStarterMoveset(moveset: StarterMoveset): boolean { + validateStarterMoveset(moveset: StarterMoveset, eggMoves: integer): boolean { + const rootSpeciesId = this.getRootSpeciesId(); for (let moveId of moveset) { + if (speciesEggMoves.hasOwnProperty(rootSpeciesId)) { + const eggMoveIndex = speciesEggMoves[rootSpeciesId].findIndex(m => m === moveId); + if (eggMoveIndex > -1 && eggMoves & Math.pow(2, eggMoveIndex)) + continue; + } if (pokemonFormLevelMoves.hasOwnProperty(this.speciesId) && pokemonFormLevelMoves[this.speciesId].hasOwnProperty(this.formIndex)) { if (!pokemonFormLevelMoves[this.speciesId][this.formIndex].find(lm => lm[0] <= 5 && lm[1] === moveId)) return false; diff --git a/src/egg-hatch-phase.ts b/src/egg-hatch-phase.ts index 570752ba3..19a29f1d8 100644 --- a/src/egg-hatch-phase.ts +++ b/src/egg-hatch-phase.ts @@ -15,6 +15,7 @@ import { Gender, getGenderColor, getGenderSymbol } from "./data/gender"; import { achvs } from "./system/achv"; import { addWindow } from "./ui/window"; import { getNatureName } from "./data/nature"; +import { pokemonPrevolutions } from "./data/pokemon-evolutions"; export class EggHatchPhase extends Phase { private egg: Egg; @@ -33,6 +34,7 @@ export class EggHatchPhase extends Phase { private statsContainer: StatsContainer; private pokemon: PlayerPokemon; + private eggMoveIndex: integer; private canSkip: boolean; private skipped: boolean; private evolutionBgm: AnySound; @@ -293,8 +295,10 @@ export class EggHatchPhase extends Phase { this.scene.ui.showText(`${this.pokemon.name} hatched from the egg!`, null, () => { this.scene.gameData.updateSpeciesDexIvs(this.pokemon.species.speciesId, this.pokemon.ivs); this.scene.gameData.setPokemonCaught(this.pokemon, true, true).then(() => { - this.scene.ui.showText(null, 0); - this.end(); + this.scene.gameData.setEggMoveUnlocked(this.pokemon.species, this.eggMoveIndex).then(() => { + this.scene.ui.showText(null, 0); + this.end(); + }); }); }, null, true, 3000); //this.scene.time.delayedCall(Utils.fixedInt(4250), () => this.scene.playBgm()); @@ -412,7 +416,7 @@ export class EggHatchPhase extends Phase { let speciesPool = Object.keys(speciesStarters) .filter(s => speciesStarters[s] >= minStarterValue && speciesStarters[s] <= maxStarterValue) .map(s => parseInt(s) as Species) - .filter(s => getPokemonSpecies(s).isObtainable() && ignoredSpecies.indexOf(s) === -1); + .filter(s => !pokemonPrevolutions.hasOwnProperty(s) && getPokemonSpecies(s).isObtainable() && ignoredSpecies.indexOf(s) === -1); if (this.egg.gachaType === GachaType.TYPE) { let tryOverrideType: boolean; @@ -473,6 +477,12 @@ export class EggHatchPhase extends Phase { for (let s = 0; s < ret.ivs.length; s++) ret.ivs[s] = Math.max(ret.ivs[s], secondaryIvs[s]); }, ret.id, EGG_SEED.toString()); + + this.scene.executeWithSeedOffset(() => { + const rand = Utils.randSeedInt(10); + + this.eggMoveIndex = rand ? Math.floor((rand - 1) / 3) : 3; + }, this.egg.id, EGG_SEED.toString()); return ret; } diff --git a/src/phases.ts b/src/phases.ts index 03bb89158..93375a227 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -1712,8 +1712,8 @@ export class TurnEndPhase extends FieldPhase { pokemon.lapseTags(BattlerTagLapseType.TURN_END); if (pokemon.summonData.disabledMove && !--pokemon.summonData.disabledTurns) { - pokemon.summonData.disabledMove = Moves.NONE; this.scene.pushPhase(new MessagePhase(this.scene, `${allMoves[pokemon.summonData.disabledMove].name} is disabled\nno more!`)); + pokemon.summonData.disabledMove = Moves.NONE; } const hasUsableBerry = !!this.scene.findModifier(m => m instanceof BerryModifier && m.shouldApply([ pokemon ]), pokemon.isPlayer()); diff --git a/src/pokemon.ts b/src/pokemon.ts index 32fae5fdb..68a7ff909 100644 --- a/src/pokemon.ts +++ b/src/pokemon.ts @@ -1925,7 +1925,7 @@ export class PlayerPokemon extends Pokemon { } tryPopulateMoveset(moveset: StarterMoveset): boolean { - if (!this.getSpeciesForm().validateStarterMoveset(moveset)) + if (!this.getSpeciesForm().validateStarterMoveset(moveset, this.scene.gameData.starterEggMoveData[this.species.getRootSpeciesId()])) return false; this.moveset = moveset.map(m => new PokemonMove(m)); diff --git a/src/system/game-data.ts b/src/system/game-data.ts index 5ba228884..408b030d2 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -25,6 +25,8 @@ import { GameStats } from "./game-stats"; import { Tutorial } from "../tutorial"; import { BattleSpec } from "../enums/battle-spec"; import { Moves } from "../data/enums/moves"; +import { speciesEggMoves } from "../data/egg-moves"; +import { allMoves } from "../data/move"; const saveKey = 'x0i2O7WRiANTqPmZ'; // Temporary; secure encryption is not yet necessary @@ -60,6 +62,7 @@ interface SystemSaveData { gender: PlayerGender; dexData: DexData; starterMoveData: StarterMoveData; + starterEggMoveData: StarterEggMoveData; gameStats: GameStats; unlocks: Unlocks; achvUnlocks: AchvUnlocks; @@ -146,6 +149,10 @@ export interface StarterFormMoveData { [key: integer]: StarterMoveset } +export interface StarterEggMoveData { + [key: integer]: integer +} + export interface TutorialFlags { [key: string]: boolean } @@ -172,6 +179,8 @@ export class GameData { public starterMoveData: StarterMoveData; + public starterEggMoveData: StarterEggMoveData; + public gameStats: GameStats; public unlocks: Unlocks; @@ -188,6 +197,7 @@ export class GameData { this.trainerId = Utils.randSeedInt(65536); this.secretId = Utils.randSeedInt(65536); this.starterMoveData = {}; + this.starterEggMoveData = {}; this.gameStats = new GameStats(); this.unlocks = { [Unlockables.ENDLESS_MODE]: false, @@ -204,6 +214,7 @@ export class GameData { }; this.eggs = []; this.initDexData(); + this.initEggMoveData(); } public saveSystem(): Promise { @@ -220,6 +231,7 @@ export class GameData { gender: this.gender, dexData: this.dexData, starterMoveData: this.starterMoveData, + starterEggMoveData: this.starterEggMoveData, gameStats: this.gameStats, unlocks: this.unlocks, achvUnlocks: this.achvUnlocks, @@ -279,6 +291,13 @@ export class GameData { this.saveSetting(Setting.Player_Gender, systemData.gender === PlayerGender.FEMALE ? 1 : 0); this.starterMoveData = systemData.starterMoveData || {}; + + if (systemData.starterEggMoveData) + this.starterEggMoveData = systemData.starterEggMoveData; + else { + this.starterEggMoveData = {}; + this.initEggMoveData(); + } if (systemData.gameStats) this.gameStats = systemData.gameStats; @@ -806,6 +825,15 @@ export class GameData { this.dexData = data; } + private initEggMoveData(): void { + const data: StarterEggMoveData = {}; + + const starterSpeciesIds = Object.keys(speciesEggMoves).map(k => parseInt(k) as Species); + + for (let speciesId of starterSpeciesIds) + data[speciesId] = 0; + } + setPokemonSeen(pokemon: Pokemon, incrementCount: boolean = true): void { const dexEntry = this.dexData[pokemon.species.speciesId]; dexEntry.seenAttr |= pokemon.getDexAttr(); @@ -822,7 +850,7 @@ export class GameData { } setPokemonSpeciesCaught(pokemon: Pokemon, species: PokemonSpecies, incrementCount: boolean = true, fromEgg: boolean = false): Promise { - return new Promise((resolve) => { + return new Promise(resolve => { const dexEntry = this.dexData[species.speciesId]; const caughtAttr = dexEntry.caughtAttr; dexEntry.caughtAttr |= pokemon.getDexAttr(); @@ -868,6 +896,31 @@ export class GameData { }); } + setEggMoveUnlocked(species: PokemonSpecies, eggMoveIndex: integer): Promise { + return new Promise(resolve => { + const speciesId = species.speciesId; + if (!speciesEggMoves.hasOwnProperty(speciesId) || !speciesEggMoves[speciesId][eggMoveIndex]) { + resolve(false); + return; + } + + if (!this.starterEggMoveData.hasOwnProperty(speciesId)) + this.starterEggMoveData[speciesId] = 0; + + const value = Math.pow(2, eggMoveIndex); + + if (this.starterEggMoveData[speciesId] & eggMoveIndex) { + resolve(false); + return; + } + + this.starterEggMoveData[speciesId] |= value; + + this.scene.playSoundWithoutBgm('level_up_fanfare', 1500); + this.scene.ui.showText(`${eggMoveIndex === 3 ? 'Rare ' : ''}Egg Move unlocked: ${allMoves[speciesEggMoves[speciesId][eggMoveIndex]].name}`, null, () => resolve(true), null, true); + }); + } + updateSpeciesDexIvs(speciesId: Species, ivs: integer[]): void { let dexEntry: DexEntry; do { diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index 25aff7459..aace255ee 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -22,6 +22,7 @@ import { LevelMoves, pokemonFormLevelMoves, pokemonSpeciesLevelMoves } from "../ import { allMoves } from "../data/move"; import { Type } from "../data/type"; import { Moves } from "../data/enums/moves"; +import { speciesEggMoves } from "../data/egg-moves"; export type StarterSelectCallback = (starters: Starter[]) => void; @@ -144,12 +145,12 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonNameText.setOrigin(0, 0); this.starterSelectContainer.add(this.pokemonNameText); - this.pokemonGrowthRateLabelText = addTextObject(this.scene, 8, 103, 'Growth Rate:', TextStyle.SUMMARY, { fontSize: '48px' }); + this.pokemonGrowthRateLabelText = addTextObject(this.scene, 8, 106, 'Growth Rate:', TextStyle.SUMMARY, { fontSize: '36px' }); this.pokemonGrowthRateLabelText.setOrigin(0, 0); this.pokemonGrowthRateLabelText.setVisible(false); this.starterSelectContainer.add(this.pokemonGrowthRateLabelText); - this.pokemonGrowthRateText = addTextObject(this.scene, 44, 103, '', TextStyle.SUMMARY_PINK, { fontSize: '48px' }); + this.pokemonGrowthRateText = addTextObject(this.scene, 34, 106, '', TextStyle.SUMMARY_PINK, { fontSize: '36px' }); this.pokemonGrowthRateText.setOrigin(0, 0); this.starterSelectContainer.add(this.pokemonGrowthRateText); @@ -344,9 +345,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.starterSelectContainer.add(this.pokemonMovesContainer); - this.pokemonEggMovesContainer = this.scene.add.container(102, 94); - this.pokemonEggMovesContainer.setScale(0.25); - this.pokemonEggMovesContainer.setVisible(false); + this.pokemonEggMovesContainer = this.scene.add.container(102, 85); + this.pokemonEggMovesContainer.setScale(0.375); const eggMovesLabel = addTextObject(this.scene, -46, 0, 'Egg Moves', TextStyle.SUMMARY); eggMovesLabel.setOrigin(0.5, 0); @@ -617,9 +617,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { }; }).concat({ label: 'Cancel', - handler: () => { - showSwapOptions(this.starterMoveset); - } + handler: () => showSwapOptions(this.starterMoveset) }), maxOptions: 8, yOffset: 19 @@ -1106,6 +1104,12 @@ export default class StarterSelectUiHandler extends MessageUiHandler { else levelMoves = pokemonSpeciesLevelMoves[species.speciesId]; 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)) + this.speciesStarterMoves.push(speciesEggMoves[species.speciesId][em]); + } + } const speciesMoveData = this.scene.gameData.starterMoveData[species.speciesId]; let moveData: StarterMoveset = speciesMoveData @@ -1134,6 +1138,17 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonMoveContainers[m].setVisible(!!move); } + const hasEggMoves = species && speciesEggMoves.hasOwnProperty(species.speciesId); + + 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); + this.pokemonEggMoveBgs[em].setFrame(Type[eggMove ? eggMove.type : Type.UNKNOWN].toString().toLowerCase()); + this.pokemonEggMoveLabels[em].setText(eggMove && eggMoveUnlocked ? eggMove.name : '???'); + } + + this.pokemonEggMovesContainer.setVisible(hasEggMoves); + this.pokemonAdditionalMoveCountLabel.setText(`(+${Math.max(this.speciesStarterMoves.length - 4, 0)})`); this.pokemonAdditionalMoveCountLabel.setVisible(this.speciesStarterMoves.length > 4);