From ef5a29f9b260dd483d67d72733b1c825341a86db Mon Sep 17 00:00:00 2001 From: Xavion3 Date: Thu, 4 Jul 2024 05:13:25 +1000 Subject: [PATCH] Implement support for relearner only moves (#1667) Currently only used by Zangoose --- src/data/pokemon-level-moves.ts | 24 ++++---- src/data/pokemon-species.ts | 2 +- src/field/pokemon.ts | 89 ++++++++++++++--------------- src/ui/starter-select-ui-handler.ts | 2 +- 4 files changed, 56 insertions(+), 61 deletions(-) diff --git a/src/data/pokemon-level-moves.ts b/src/data/pokemon-level-moves.ts index 5f01b4db02f..b212ea413a3 100644 --- a/src/data/pokemon-level-moves.ts +++ b/src/data/pokemon-level-moves.ts @@ -5827,20 +5827,20 @@ export const pokemonSpeciesLevelMoves: PokemonSpeciesLevelMoves = { [ 56, Moves.SKY_ATTACK ], ], [Species.ZANGOOSE]: [ + [ -1, Moves.DOUBLE_KICK ], + [ -1, Moves.DISABLE ], + [ -1, Moves.COUNTER ], + [ -1, Moves.FURY_SWIPES ], + [ -1, Moves.CURSE ], + [ -1, Moves.FLAIL ], + [ -1, Moves.BELLY_DRUM ], + [ -1, Moves.FEINT ], + [ -1, Moves.NIGHT_SLASH ], + [ -1, Moves.DOUBLE_HIT ], + [ -1, Moves.QUICK_GUARD ], + [ -1, Moves.FINAL_GAMBIT ], [ 1, Moves.SCRATCH ], [ 1, Moves.LEER ], - [ 1, Moves.DOUBLE_KICK ], - [ 1, Moves.DISABLE ], - [ 1, Moves.COUNTER ], - [ 1, Moves.FURY_SWIPES ], - [ 1, Moves.CURSE ], - [ 1, Moves.FLAIL ], - [ 1, Moves.BELLY_DRUM ], - [ 1, Moves.FEINT ], - [ 1, Moves.NIGHT_SLASH ], - [ 1, Moves.DOUBLE_HIT ], - [ 1, Moves.QUICK_GUARD ], - [ 1, Moves.FINAL_GAMBIT ], [ 5, Moves.QUICK_ATTACK ], [ 8, Moves.FURY_CUTTER ], [ 12, Moves.METAL_CLAW ], diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts index 962772d6e78..2b488f330c4 100644 --- a/src/data/pokemon-species.ts +++ b/src/data/pokemon-species.ts @@ -779,7 +779,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali } // This could definitely be written better and more accurate to the getSpeciesForLevel logic, but it is only for generating movesets for evolved Pokemon - getSimulatedEvolutionChain(currentLevel: integer, forTrainer: boolean = false, isBoss: boolean = false, player: boolean = false) { + getSimulatedEvolutionChain(currentLevel: integer, forTrainer: boolean = false, isBoss: boolean = false, player: boolean = false): [Species, integer][] { const ret = []; if (pokemonPrevolutions.hasOwnProperty(this.speciesId)) { const prevolutionLevels = this.getPrevolutionLevels().reverse(); diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 2c05492c2c6..b31cdae9b1c 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -849,8 +849,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return ret; } + /** + * All moves that could be relearned by this pokemon at this point. Used for memory mushrooms. + * @returns {Moves[]} The valid moves + */ getLearnableLevelMoves(): Moves[] { - return this.getLevelMoves(1, true).map(lm => lm[1]).filter(lm => !this.moveset.filter(m => m.moveId === lm).length).filter((move: Moves, i: integer, array: Moves[]) => array.indexOf(move) === i); + return this.getLevelMoves(1, true, false, true).map(lm => lm[1]).filter(lm => !this.moveset.filter(m => m.moveId === lm).length).filter((move: Moves, i: integer, array: Moves[]) => array.indexOf(move) === i); } /** @@ -1235,7 +1239,15 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return null; } - getLevelMoves(startingLevel?: integer, includeEvolutionMoves: boolean = false, simulateEvolutionChain: boolean = false): LevelMoves { + /** + * Gets all level up moves in a given range for a particular pokemon. + * @param {integer} startingLevel Don't include moves below this level + * @param {boolean} includeEvolutionMoves Whether to include evolution moves + * @param {boolean} simulateEvolutionChain Whether to include moves from prior evolutions + * @param {boolean} includeRelearnerMoves Whether to include moves that would require a relearner. Note the move relearner inherently allows evolution moves + * @returns {LevelMoves} A list of moves and the levels they can be learned at + */ + getLevelMoves(startingLevel?: integer, includeEvolutionMoves: boolean = false, simulateEvolutionChain: boolean = false, includeRelearnerMoves: boolean = false): LevelMoves { const ret: LevelMoves = []; let levelMoves: LevelMoves = []; if (!startingLevel) { @@ -1246,62 +1258,45 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { for (let e = 0; e < evolutionChain.length; e++) { // TODO: Might need to pass specific form index in simulated evolution chain const speciesLevelMoves = getPokemonSpeciesForm(evolutionChain[e][0] as Species, this.formIndex).getLevelMoves(); - levelMoves.push(...speciesLevelMoves.filter(lm => (includeEvolutionMoves && !lm[0]) || ((!e || lm[0] > 1) && (e === evolutionChain.length - 1 || lm[0] <= evolutionChain[e + 1][1])))); - } - levelMoves.sort((lma: [integer, integer], lmb: [integer, integer]) => lma[0] > lmb[0] ? 1 : lma[0] < lmb[0] ? -1 : 0); - const uniqueMoves: Moves[] = []; - levelMoves = levelMoves.filter(lm => { - if (uniqueMoves.find(m => m === lm[1])) { - return false; + if (includeRelearnerMoves) { + levelMoves.push(...speciesLevelMoves); + } else { + levelMoves.push(...speciesLevelMoves.filter(lm => (includeEvolutionMoves && lm[0] === 0) || ((!e || lm[0] > 1) && (e === evolutionChain.length - 1 || lm[0] <= evolutionChain[e + 1][1])))); } - uniqueMoves.push(lm[1]); - return true; - }); + } } else { - levelMoves = this.getSpeciesForm(true).getLevelMoves(); + levelMoves = this.getSpeciesForm(true).getLevelMoves().filter(lm => (includeEvolutionMoves && lm[0] === 0) || (includeRelearnerMoves && lm[0] === -1) || lm[0] > 0); } if (this.fusionSpecies) { - const evolutionLevelMoves = levelMoves.slice(0, Math.max(levelMoves.findIndex(lm => !!lm[0]), 0)); - const fusionLevelMoves = this.getFusionSpeciesForm(true).getLevelMoves(); - const fusionEvolutionLevelMoves = fusionLevelMoves.slice(0, Math.max(fusionLevelMoves.findIndex(flm => !!flm[0]), 0)); - const newLevelMoves: LevelMoves = []; - while (levelMoves.length && levelMoves[0][0] < startingLevel) { - levelMoves.shift(); - } - while (fusionLevelMoves.length && fusionLevelMoves[0][0] < startingLevel) { - fusionLevelMoves.shift(); - } - if (includeEvolutionMoves) { - for (const elm of evolutionLevelMoves.reverse()) { - levelMoves.unshift(elm); - } - for (const felm of fusionEvolutionLevelMoves.reverse()) { - fusionLevelMoves.unshift(felm); - } - } - for (let l = includeEvolutionMoves ? 0 : startingLevel; l <= this.level; l++) { - if (l === 1 && startingLevel > 1) { - l = startingLevel; - } - while (levelMoves.length && levelMoves[0][0] === l) { - const levelMove = levelMoves.shift(); - if (!newLevelMoves.find(lm => lm[1] === levelMove[1])) { - newLevelMoves.push(levelMove); - } - } - while (fusionLevelMoves.length && fusionLevelMoves[0][0] === l) { - const fusionLevelMove = fusionLevelMoves.shift(); - if (!newLevelMoves.find(lm => lm[1] === fusionLevelMove[1])) { - newLevelMoves.push(fusionLevelMove); + if (simulateEvolutionChain) { + const fusionEvolutionChain = this.fusionSpecies.getSimulatedEvolutionChain(this.level, this.hasTrainer(), this.isBoss(), this.isPlayer()); + for (let e = 0; e < fusionEvolutionChain.length; e++) { + // TODO: Might need to pass specific form index in simulated evolution chain + const speciesLevelMoves = getPokemonSpeciesForm(fusionEvolutionChain[e][0] as Species, this.fusionFormIndex).getLevelMoves(); + if (includeRelearnerMoves) { + levelMoves.push(...speciesLevelMoves.filter(lm => (includeEvolutionMoves && lm[0] === 0) || lm[0] !== 0)); + } else { + levelMoves.push(...speciesLevelMoves.filter(lm => (includeEvolutionMoves && lm[0] === 0) || ((!e || lm[0] > 1) && (e === fusionEvolutionChain.length - 1 || lm[0] <= fusionEvolutionChain[e + 1][1])))); } } + } else { + levelMoves.push(...this.getFusionSpeciesForm(true).getLevelMoves().filter(lm => (includeEvolutionMoves && lm[0] === 0) || (includeRelearnerMoves && lm[0] === -1) || lm[0] > 0)); } - levelMoves = newLevelMoves; } + levelMoves.sort((lma: [integer, integer], lmb: [integer, integer]) => lma[0] > lmb[0] ? 1 : lma[0] < lmb[0] ? -1 : 0); + const uniqueMoves: Moves[] = []; + levelMoves = levelMoves.filter(lm => { + if (uniqueMoves.find(m => m === lm[1])) { + return false; + } + uniqueMoves.push(lm[1]); + return true; + }); + if (levelMoves) { for (const lm of levelMoves) { const level = lm[0]; - if ((!includeEvolutionMoves || level) && level < startingLevel) { + if (!includeRelearnerMoves && ((level > 0 && level < startingLevel) || (!includeEvolutionMoves && level === 0) || level < 0)) { continue; } else if (level > this.level) { break; diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index 687f65281b4..d0e644870a1 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -2366,7 +2366,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } else { levelMoves = pokemonSpeciesLevelMoves[species.speciesId]; } - this.speciesStarterMoves.push(...levelMoves.filter(lm => lm[0] <= 5).map(lm => lm[1])); + this.speciesStarterMoves.push(...levelMoves.filter(lm => lm[0] > 0 && lm[0] <= 5).map(lm => lm[1])); if (speciesEggMoves.hasOwnProperty(species.speciesId)) { for (let em = 0; em < 4; em++) { if (this.scene.gameData.starterData[species.speciesId].eggMoves & Math.pow(2, em)) {