mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-02-12 19:25:51 +00:00
[Bug][Refactor] Custom types from MEs are correctly applied on form changes (#5229)
* customPokemonData.types now accepts Type.UNKNOWN, ignores when determining type * Changed test for clowning around encounter to look at getTypes() instead of directly accessing customData * Simplifying logic for fusions when overrides are involved, introducing new tests in pokemon.test.ts * Fixed typo * Fixed another typo * Renamed overrideTypes to customTypes to avoid confusion with override * Fixing comments
This commit is contained in:
parent
dcb4299aaf
commit
82da3c1b6d
@ -15,7 +15,7 @@ import { TrainerType } from "#enums/trainer-type";
|
|||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
import { Abilities } from "#enums/abilities";
|
import { Abilities } from "#enums/abilities";
|
||||||
import { applyAbilityOverrideToPokemon, applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
import { applyAbilityOverrideToPokemon, applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
import type { Type } from "#enums/type";
|
import { Type } from "#enums/type";
|
||||||
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
import { randSeedInt, randSeedShuffle } from "#app/utils";
|
import { randSeedInt, randSeedShuffle } from "#app/utils";
|
||||||
@ -347,7 +347,7 @@ export const ClowningAroundEncounter: MysteryEncounter =
|
|||||||
priorityTypes = randSeedShuffle(priorityTypes);
|
priorityTypes = randSeedShuffle(priorityTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
const newTypes = [ originalTypes[0] ];
|
const newTypes = [ Type.UNKNOWN ];
|
||||||
let secondType: Type | null = null;
|
let secondType: Type | null = null;
|
||||||
while (secondType === null || secondType === newTypes[0] || originalTypes.includes(secondType)) {
|
while (secondType === null || secondType === newTypes[0] || originalTypes.includes(secondType)) {
|
||||||
if (priorityTypes.length > 0) {
|
if (priorityTypes.length > 0) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { Type } from "#enums/type";
|
import { Type } from "#enums/type";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
@ -528,7 +528,7 @@ async function postProcessTransformedPokemon(previousPokemon: PlayerPokemon, new
|
|||||||
|
|
||||||
// Randomize the second type of the pokemon
|
// Randomize the second type of the pokemon
|
||||||
// If the pokemon does not normally have a second type, it will gain 1
|
// If the pokemon does not normally have a second type, it will gain 1
|
||||||
const newTypes = [ newPokemon.getTypes()[0] ];
|
const newTypes = [ Type.UNKNOWN ];
|
||||||
let newType = randSeedInt(18) as Type;
|
let newType = randSeedInt(18) as Type;
|
||||||
while (newType === newTypes[0]) {
|
while (newType === newTypes[0]) {
|
||||||
newType = randSeedInt(18) as Type;
|
newType = randSeedInt(18) as Type;
|
||||||
|
@ -1259,52 +1259,39 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
if (!types.length || !includeTeraType) {
|
if (!types.length || !includeTeraType) {
|
||||||
if (!ignoreOverride && this.summonData?.types && this.summonData.types.length > 0) {
|
if (!ignoreOverride && this.summonData?.types && this.summonData.types.length > 0) {
|
||||||
this.summonData.types.forEach(t => types.push(t));
|
this.summonData.types.forEach(t => types.push(t));
|
||||||
} else if (this.customPokemonData.types && this.customPokemonData.types.length > 0) {
|
|
||||||
// "Permanent" override for a Pokemon's normal types, currently only used by Mystery Encounters
|
|
||||||
types.push(this.customPokemonData.types[0]);
|
|
||||||
|
|
||||||
// Fusing a Pokemon onto something with "permanently changed" types will still apply the fusion's types as normal
|
|
||||||
const fusionSpeciesForm = this.getFusionSpeciesForm(ignoreOverride);
|
|
||||||
if (fusionSpeciesForm) {
|
|
||||||
// Check if the fusion Pokemon also had "permanently changed" types
|
|
||||||
const fusionMETypes = this.fusionCustomPokemonData?.types;
|
|
||||||
if (fusionMETypes && fusionMETypes.length >= 2 && fusionMETypes[1] !== types[0]) {
|
|
||||||
types.push(fusionMETypes[1]);
|
|
||||||
} else if (fusionMETypes && fusionMETypes.length === 1 && fusionMETypes[0] !== types[0]) {
|
|
||||||
types.push(fusionMETypes[0]);
|
|
||||||
} else if (fusionSpeciesForm.type2 !== null && fusionSpeciesForm.type2 !== types[0]) {
|
|
||||||
types.push(fusionSpeciesForm.type2);
|
|
||||||
} else if (fusionSpeciesForm.type1 !== types[0]) {
|
|
||||||
types.push(fusionSpeciesForm.type1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (types.length === 1 && this.customPokemonData.types.length >= 2) {
|
|
||||||
types.push(this.customPokemonData.types[1]);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
const speciesForm = this.getSpeciesForm(ignoreOverride);
|
const speciesForm = this.getSpeciesForm(ignoreOverride);
|
||||||
|
|
||||||
types.push(speciesForm.type1);
|
|
||||||
|
|
||||||
const fusionSpeciesForm = this.getFusionSpeciesForm(ignoreOverride);
|
const fusionSpeciesForm = this.getFusionSpeciesForm(ignoreOverride);
|
||||||
|
const customTypes = this.customPokemonData.types?.length > 0;
|
||||||
|
|
||||||
|
// First type, checking for "permanently changed" types from ME
|
||||||
|
const firstType = (customTypes && this.customPokemonData.types[0] !== Type.UNKNOWN) ? this.customPokemonData.types[0] : speciesForm.type1;
|
||||||
|
types.push(firstType);
|
||||||
|
|
||||||
|
// Second type
|
||||||
|
let secondType: Type | null = null;
|
||||||
|
|
||||||
if (fusionSpeciesForm) {
|
if (fusionSpeciesForm) {
|
||||||
// Check if the fusion Pokemon also had "permanently changed" types
|
// Check if the fusion Pokemon also has permanent changes from ME when determining the fusion types
|
||||||
// Otherwise, use standard fusion type logic
|
const fusionType1 = (this.fusionCustomPokemonData?.types && this.fusionCustomPokemonData.types.length > 0 && this.fusionCustomPokemonData.types[0] !== Type.UNKNOWN)
|
||||||
const fusionMETypes = this.fusionCustomPokemonData?.types;
|
? this.fusionCustomPokemonData.types[0] : fusionSpeciesForm.type1;
|
||||||
if (fusionMETypes && fusionMETypes.length >= 2 && fusionMETypes[1] !== types[0]) {
|
const fusionType2 = (this.fusionCustomPokemonData?.types && this.fusionCustomPokemonData.types.length > 1 && this.fusionCustomPokemonData.types[1] !== Type.UNKNOWN)
|
||||||
types.push(fusionMETypes[1]);
|
? this.fusionCustomPokemonData.types[1] : fusionSpeciesForm.type2;
|
||||||
} else if (fusionMETypes && fusionMETypes.length === 1 && fusionMETypes[0] !== types[0]) {
|
|
||||||
types.push(fusionMETypes[0]);
|
// Assign second type if the fusion can provide one
|
||||||
} else if (fusionSpeciesForm.type2 !== null && fusionSpeciesForm.type2 !== speciesForm.type1) {
|
if (fusionType2 !== null && fusionType2 !== types[0]) {
|
||||||
types.push(fusionSpeciesForm.type2);
|
secondType = fusionType2;
|
||||||
} else if (fusionSpeciesForm.type1 !== speciesForm.type1) {
|
} else if (fusionType1 !== types[0]) {
|
||||||
types.push(fusionSpeciesForm.type1);
|
secondType = fusionType1;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// If not a fusion, just get the second type from the species, checking for permanent changes from ME
|
||||||
|
secondType = (customTypes && this.customPokemonData.types.length > 1 && this.customPokemonData.types[1] !== Type.UNKNOWN)
|
||||||
|
? this.customPokemonData.types[1] : speciesForm.type2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (types.length === 1 && speciesForm.type2 !== null) {
|
if (secondType) {
|
||||||
types.push(speciesForm.type2);
|
types.push(secondType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4565,7 +4552,6 @@ export class PlayerPokemon extends Pokemon {
|
|||||||
|
|
||||||
changeForm(formChange: SpeciesFormChange): Promise<void> {
|
changeForm(formChange: SpeciesFormChange): Promise<void> {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const previousFormIndex = this.formIndex;
|
|
||||||
this.formIndex = Math.max(this.species.forms.findIndex(f => f.formKey === formChange.formKey), 0);
|
this.formIndex = Math.max(this.species.forms.findIndex(f => f.formKey === formChange.formKey), 0);
|
||||||
this.generateName();
|
this.generateName();
|
||||||
const abilityCount = this.getSpeciesForm().getAbilityCount();
|
const abilityCount = this.getSpeciesForm().getAbilityCount();
|
||||||
@ -4573,25 +4559,6 @@ export class PlayerPokemon extends Pokemon {
|
|||||||
this.abilityIndex = abilityCount - 1;
|
this.abilityIndex = abilityCount - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// In cases where a form change updates the type of a Pokemon from its previous form (Arceus, Silvally, Castform, etc.),
|
|
||||||
// persist that type change in customPokemonData if necessary
|
|
||||||
const baseForm = this.species.forms[previousFormIndex];
|
|
||||||
const baseFormTypes = [ baseForm.type1, baseForm.type2 ];
|
|
||||||
if (this.customPokemonData.types.length > 0) {
|
|
||||||
if (this.getSpeciesForm().type1 !== baseFormTypes[0]) {
|
|
||||||
this.customPokemonData.types[0] = this.getSpeciesForm().type1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const type2 = this.getSpeciesForm().type2;
|
|
||||||
if (!isNullOrUndefined(type2) && type2 !== baseFormTypes[1]) {
|
|
||||||
if (this.customPokemonData.types.length > 1) {
|
|
||||||
this.customPokemonData.types[1] = type2;
|
|
||||||
} else {
|
|
||||||
this.customPokemonData.types.push(type2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.compatibleTms.splice(0, this.compatibleTms.length);
|
this.compatibleTms.splice(0, this.compatibleTms.length);
|
||||||
this.generateCompatibleTms();
|
this.generateCompatibleTms();
|
||||||
const updateAndResolve = () => {
|
const updateAndResolve = () => {
|
||||||
|
@ -4,6 +4,8 @@ import GameManager from "../utils/gameManager";
|
|||||||
import { PokeballType } from "#enums/pokeball";
|
import { PokeballType } from "#enums/pokeball";
|
||||||
import type BattleScene from "#app/battle-scene";
|
import type BattleScene from "#app/battle-scene";
|
||||||
import { Moves } from "#app/enums/moves";
|
import { Moves } from "#app/enums/moves";
|
||||||
|
import { Type } from "#app/enums/type";
|
||||||
|
import { CustomPokemonData } from "#app/data/custom-pokemon-data";
|
||||||
|
|
||||||
describe("Spec - Pokemon", () => {
|
describe("Spec - Pokemon", () => {
|
||||||
let phaserGame: Phaser.Game;
|
let phaserGame: Phaser.Game;
|
||||||
@ -75,4 +77,131 @@ describe("Spec - Pokemon", () => {
|
|||||||
expect(fanRotom.compatibleTms).not.toContain(Moves.BLIZZARD);
|
expect(fanRotom.compatibleTms).not.toContain(Moves.BLIZZARD);
|
||||||
expect(fanRotom.compatibleTms).toContain(Moves.AIR_SLASH);
|
expect(fanRotom.compatibleTms).toContain(Moves.AIR_SLASH);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("Get correct fusion type", () => {
|
||||||
|
let scene: BattleScene;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
game.override.enemySpecies(Species.ZUBAT);
|
||||||
|
game.override.starterSpecies(Species.ABRA);
|
||||||
|
game.override.enableStarterFusion();
|
||||||
|
scene = game.scene;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Fusing two mons with a single type", async () => {
|
||||||
|
game.override.starterFusionSpecies(Species.CHARMANDER);
|
||||||
|
await game.classicMode.startBattle();
|
||||||
|
const pokemon = scene.getPlayerParty()[0];
|
||||||
|
|
||||||
|
let types = pokemon.getTypes();
|
||||||
|
expect(types[0]).toBe(Type.PSYCHIC);
|
||||||
|
expect(types[1]).toBe(Type.FIRE);
|
||||||
|
|
||||||
|
// Abra Psychic/Grass
|
||||||
|
pokemon.customPokemonData.types = [ Type.UNKNOWN, Type.GRASS ];
|
||||||
|
types = pokemon.getTypes();
|
||||||
|
expect(types[0]).toBe(Type.PSYCHIC);
|
||||||
|
expect(types[1]).toBe(Type.FIRE);
|
||||||
|
|
||||||
|
// Abra Grass
|
||||||
|
pokemon.customPokemonData.types = [ Type.GRASS, Type.UNKNOWN ];
|
||||||
|
types = pokemon.getTypes();
|
||||||
|
expect(types[0]).toBe(Type.GRASS);
|
||||||
|
expect(types[1]).toBe(Type.FIRE);
|
||||||
|
|
||||||
|
if (!pokemon.fusionCustomPokemonData) {
|
||||||
|
pokemon.fusionCustomPokemonData = new CustomPokemonData();
|
||||||
|
}
|
||||||
|
pokemon.customPokemonData.types = [];
|
||||||
|
|
||||||
|
// Charmander Fire/Grass
|
||||||
|
pokemon.fusionCustomPokemonData.types = [ Type.UNKNOWN, Type.GRASS ];
|
||||||
|
types = pokemon.getTypes();
|
||||||
|
expect(types[0]).toBe(Type.PSYCHIC);
|
||||||
|
expect(types[1]).toBe(Type.GRASS);
|
||||||
|
|
||||||
|
// Charmander Grass
|
||||||
|
pokemon.fusionCustomPokemonData.types = [ Type.GRASS, Type.UNKNOWN ];
|
||||||
|
types = pokemon.getTypes();
|
||||||
|
expect(types[0]).toBe(Type.PSYCHIC);
|
||||||
|
expect(types[1]).toBe(Type.GRASS);
|
||||||
|
|
||||||
|
// Abra Grass
|
||||||
|
// Charmander Fire/Grass
|
||||||
|
pokemon.customPokemonData.types = [ Type.GRASS, Type.UNKNOWN ];
|
||||||
|
pokemon.fusionCustomPokemonData.types = [ Type.UNKNOWN, Type.GRASS ];
|
||||||
|
types = pokemon.getTypes();
|
||||||
|
expect(types[0]).toBe(Type.GRASS);
|
||||||
|
expect(types[1]).toBe(Type.FIRE);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Fusing two mons with same single type", async () => {
|
||||||
|
game.override.starterFusionSpecies(Species.DROWZEE);
|
||||||
|
await game.classicMode.startBattle();
|
||||||
|
const pokemon = scene.getPlayerParty()[0];
|
||||||
|
|
||||||
|
const types = pokemon.getTypes();
|
||||||
|
expect(types[0]).toBe(Type.PSYCHIC);
|
||||||
|
expect(types.length).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Fusing mons with one and two types", async () => {
|
||||||
|
game.override.starterSpecies(Species.CHARMANDER);
|
||||||
|
game.override.starterFusionSpecies(Species.HOUNDOUR);
|
||||||
|
await game.classicMode.startBattle();
|
||||||
|
const pokemon = scene.getPlayerParty()[0];
|
||||||
|
|
||||||
|
const types = pokemon.getTypes();
|
||||||
|
expect(types[0]).toBe(Type.FIRE);
|
||||||
|
expect(types[1]).toBe(Type.DARK);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Fusing two mons with two types", async () => {
|
||||||
|
game.override.starterSpecies(Species.NATU);
|
||||||
|
game.override.starterFusionSpecies(Species.HOUNDOUR);
|
||||||
|
await game.classicMode.startBattle();
|
||||||
|
const pokemon = scene.getPlayerParty()[0];
|
||||||
|
|
||||||
|
let types = pokemon.getTypes();
|
||||||
|
expect(types[0]).toBe(Type.PSYCHIC);
|
||||||
|
expect(types[1]).toBe(Type.FIRE);
|
||||||
|
|
||||||
|
// Natu Psychic/Grass
|
||||||
|
pokemon.customPokemonData.types = [ Type.UNKNOWN, Type.GRASS ];
|
||||||
|
types = pokemon.getTypes();
|
||||||
|
expect(types[0]).toBe(Type.PSYCHIC);
|
||||||
|
expect(types[1]).toBe(Type.FIRE);
|
||||||
|
|
||||||
|
// Natu Grass/Flying
|
||||||
|
pokemon.customPokemonData.types = [ Type.GRASS, Type.UNKNOWN ];
|
||||||
|
types = pokemon.getTypes();
|
||||||
|
expect(types[0]).toBe(Type.GRASS);
|
||||||
|
expect(types[1]).toBe(Type.FIRE);
|
||||||
|
|
||||||
|
if (!pokemon.fusionCustomPokemonData) {
|
||||||
|
pokemon.fusionCustomPokemonData = new CustomPokemonData();
|
||||||
|
}
|
||||||
|
pokemon.customPokemonData.types = [];
|
||||||
|
|
||||||
|
// Houndour Dark/Grass
|
||||||
|
pokemon.fusionCustomPokemonData.types = [ Type.UNKNOWN, Type.GRASS ];
|
||||||
|
types = pokemon.getTypes();
|
||||||
|
expect(types[0]).toBe(Type.PSYCHIC);
|
||||||
|
expect(types[1]).toBe(Type.GRASS);
|
||||||
|
|
||||||
|
// Houndour Grass/Fire
|
||||||
|
pokemon.fusionCustomPokemonData.types = [ Type.GRASS, Type.UNKNOWN ];
|
||||||
|
types = pokemon.getTypes();
|
||||||
|
expect(types[0]).toBe(Type.PSYCHIC);
|
||||||
|
expect(types[1]).toBe(Type.FIRE);
|
||||||
|
|
||||||
|
// Natu Grass/Flying
|
||||||
|
// Houndour Dark/Grass
|
||||||
|
pokemon.customPokemonData.types = [ Type.GRASS, Type.UNKNOWN ];
|
||||||
|
pokemon.fusionCustomPokemonData.types = [ Type.UNKNOWN, Type.GRASS ];
|
||||||
|
types = pokemon.getTypes();
|
||||||
|
expect(types[0]).toBe(Type.GRASS);
|
||||||
|
expect(types[1]).toBe(Type.DARK);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -345,9 +345,9 @@ describe("Clowning Around - Mystery Encounter", () => {
|
|||||||
scene.getPlayerParty()[2].moveset = [];
|
scene.getPlayerParty()[2].moveset = [];
|
||||||
await runMysteryEncounterToEnd(game, 3);
|
await runMysteryEncounterToEnd(game, 3);
|
||||||
|
|
||||||
const leadTypesAfter = scene.getPlayerParty()[0].customPokemonData?.types;
|
const leadTypesAfter = scene.getPlayerParty()[0].getTypes();
|
||||||
const secondaryTypesAfter = scene.getPlayerParty()[1].customPokemonData?.types;
|
const secondaryTypesAfter = scene.getPlayerParty()[1].getTypes();
|
||||||
const thirdTypesAfter = scene.getPlayerParty()[2].customPokemonData?.types;
|
const thirdTypesAfter = scene.getPlayerParty()[2].getTypes();
|
||||||
|
|
||||||
expect(leadTypesAfter.length).toBe(2);
|
expect(leadTypesAfter.length).toBe(2);
|
||||||
expect(leadTypesAfter[0]).toBe(Type.WATER);
|
expect(leadTypesAfter[0]).toBe(Type.WATER);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user