Merge branch 'beta' into new-team
This commit is contained in:
commit
25cdba4bc3
Binary file not shown.
After Width: | Height: | Size: 1.0 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
Binary file not shown.
After Width: | Height: | Size: 179 B |
Binary file not shown.
After Width: | Height: | Size: 1.0 KiB |
Binary file not shown.
After Width: | Height: | Size: 179 B |
|
@ -2748,6 +2748,29 @@ export default class BattleScene extends SceneBase {
|
||||||
(window as any).gameInfo = gameInfo;
|
(window as any).gameInfo = gameInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function retrieves the sprite and audio keys for active Pokemon.
|
||||||
|
* Active Pokemon include both enemy and player Pokemon of the current wave.
|
||||||
|
* Note: Questions on garbage collection go to @frutescens
|
||||||
|
* @returns a string array of active sprite and audio keys that should not be deleted
|
||||||
|
*/
|
||||||
|
getActiveKeys(): string[] {
|
||||||
|
const keys: string[] = [];
|
||||||
|
const playerParty = this.getParty();
|
||||||
|
playerParty.forEach(p => {
|
||||||
|
keys.push("pkmn__" + p.species.getSpriteId(p.gender === Gender.FEMALE, p.species.formIndex, p.shiny, p.variant));
|
||||||
|
keys.push("pkmn__" + p.species.getSpriteId(p.gender === Gender.FEMALE, p.species.formIndex, p.shiny, p.variant, true));
|
||||||
|
keys.push("cry/" + p.species.getCryKey(p.species.formIndex));
|
||||||
|
});
|
||||||
|
// enemyParty has to be operated on separately from playerParty because playerPokemon =/= enemyPokemon
|
||||||
|
const enemyParty = this.getEnemyParty();
|
||||||
|
enemyParty.forEach(p => {
|
||||||
|
keys.push(p.species.getSpriteKey(p.gender === Gender.FEMALE, p.species.formIndex, p.shiny, p.variant));
|
||||||
|
keys.push("cry/" + p.species.getCryKey(p.species.formIndex));
|
||||||
|
});
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialized the 2nd phase of the final boss (e.g. form-change for Eternatus)
|
* Initialized the 2nd phase of the final boss (e.g. form-change for Eternatus)
|
||||||
* @param pokemon The (enemy) pokemon
|
* @param pokemon The (enemy) pokemon
|
||||||
|
|
|
@ -39,13 +39,15 @@ export class BattlerTag {
|
||||||
public turnCount: number;
|
public turnCount: number;
|
||||||
public sourceMove: Moves;
|
public sourceMove: Moves;
|
||||||
public sourceId?: number;
|
public sourceId?: number;
|
||||||
|
public isBatonPassable: boolean;
|
||||||
|
|
||||||
constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType | BattlerTagLapseType[], turnCount: number, sourceMove?: Moves, sourceId?: number) {
|
constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType | BattlerTagLapseType[], turnCount: number, sourceMove?: Moves, sourceId?: number, isBatonPassable: boolean = false) {
|
||||||
this.tagType = tagType;
|
this.tagType = tagType;
|
||||||
this.lapseTypes = Array.isArray(lapseType) ? lapseType : [ lapseType ];
|
this.lapseTypes = Array.isArray(lapseType) ? lapseType : [ lapseType ];
|
||||||
this.turnCount = turnCount;
|
this.turnCount = turnCount;
|
||||||
this.sourceMove = sourceMove!; // TODO: is this bang correct?
|
this.sourceMove = sourceMove!; // TODO: is this bang correct?
|
||||||
this.sourceId = sourceId;
|
this.sourceId = sourceId;
|
||||||
|
this.isBatonPassable = isBatonPassable;
|
||||||
}
|
}
|
||||||
|
|
||||||
canAdd(pokemon: Pokemon): boolean {
|
canAdd(pokemon: Pokemon): boolean {
|
||||||
|
@ -206,7 +208,7 @@ export class ShellTrapTag extends BattlerTag {
|
||||||
|
|
||||||
export class TrappedTag extends BattlerTag {
|
export class TrappedTag extends BattlerTag {
|
||||||
constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType, turnCount: number, sourceMove: Moves, sourceId: number) {
|
constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType, turnCount: number, sourceMove: Moves, sourceId: number) {
|
||||||
super(tagType, lapseType, turnCount, sourceMove, sourceId);
|
super(tagType, lapseType, turnCount, sourceMove, sourceId, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
canAdd(pokemon: Pokemon): boolean {
|
canAdd(pokemon: Pokemon): boolean {
|
||||||
|
@ -326,7 +328,7 @@ export class InterruptedTag extends BattlerTag {
|
||||||
*/
|
*/
|
||||||
export class ConfusedTag extends BattlerTag {
|
export class ConfusedTag extends BattlerTag {
|
||||||
constructor(turnCount: number, sourceMove: Moves) {
|
constructor(turnCount: number, sourceMove: Moves) {
|
||||||
super(BattlerTagType.CONFUSED, BattlerTagLapseType.MOVE, turnCount, sourceMove);
|
super(BattlerTagType.CONFUSED, BattlerTagLapseType.MOVE, turnCount, sourceMove, undefined, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
canAdd(pokemon: Pokemon): boolean {
|
canAdd(pokemon: Pokemon): boolean {
|
||||||
|
@ -386,7 +388,7 @@ export class ConfusedTag extends BattlerTag {
|
||||||
*/
|
*/
|
||||||
export class DestinyBondTag extends BattlerTag {
|
export class DestinyBondTag extends BattlerTag {
|
||||||
constructor(sourceMove: Moves, sourceId: number) {
|
constructor(sourceMove: Moves, sourceId: number) {
|
||||||
super(BattlerTagType.DESTINY_BOND, BattlerTagLapseType.PRE_MOVE, 1, sourceMove, sourceId);
|
super(BattlerTagType.DESTINY_BOND, BattlerTagLapseType.PRE_MOVE, 1, sourceMove, sourceId, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -505,7 +507,7 @@ export class SeedTag extends BattlerTag {
|
||||||
private sourceIndex: number;
|
private sourceIndex: number;
|
||||||
|
|
||||||
constructor(sourceId: number) {
|
constructor(sourceId: number) {
|
||||||
super(BattlerTagType.SEEDED, BattlerTagLapseType.TURN_END, 1, Moves.LEECH_SEED, sourceId);
|
super(BattlerTagType.SEEDED, BattlerTagLapseType.TURN_END, 1, Moves.LEECH_SEED, sourceId, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -776,7 +778,7 @@ export class OctolockTag extends TrappedTag {
|
||||||
|
|
||||||
export class AquaRingTag extends BattlerTag {
|
export class AquaRingTag extends BattlerTag {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(BattlerTagType.AQUA_RING, BattlerTagLapseType.TURN_END, 1, Moves.AQUA_RING, undefined);
|
super(BattlerTagType.AQUA_RING, BattlerTagLapseType.TURN_END, 1, Moves.AQUA_RING, undefined, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
onAdd(pokemon: Pokemon): void {
|
onAdd(pokemon: Pokemon): void {
|
||||||
|
@ -808,7 +810,7 @@ export class AquaRingTag extends BattlerTag {
|
||||||
/** Tag used to allow moves that interact with {@link Moves.MINIMIZE} to function */
|
/** Tag used to allow moves that interact with {@link Moves.MINIMIZE} to function */
|
||||||
export class MinimizeTag extends BattlerTag {
|
export class MinimizeTag extends BattlerTag {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(BattlerTagType.MINIMIZED, BattlerTagLapseType.TURN_END, 1, Moves.MINIMIZE, undefined);
|
super(BattlerTagType.MINIMIZED, BattlerTagLapseType.TURN_END, 1, Moves.MINIMIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
canAdd(pokemon: Pokemon): boolean {
|
canAdd(pokemon: Pokemon): boolean {
|
||||||
|
@ -1206,7 +1208,7 @@ export class SturdyTag extends BattlerTag {
|
||||||
|
|
||||||
export class PerishSongTag extends BattlerTag {
|
export class PerishSongTag extends BattlerTag {
|
||||||
constructor(turnCount: number) {
|
constructor(turnCount: number) {
|
||||||
super(BattlerTagType.PERISH_SONG, BattlerTagLapseType.TURN_END, turnCount, Moves.PERISH_SONG);
|
super(BattlerTagType.PERISH_SONG, BattlerTagLapseType.TURN_END, turnCount, Moves.PERISH_SONG, undefined, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
canAdd(pokemon: Pokemon): boolean {
|
canAdd(pokemon: Pokemon): boolean {
|
||||||
|
@ -1262,7 +1264,7 @@ export class AbilityBattlerTag extends BattlerTag {
|
||||||
public ability: Abilities;
|
public ability: Abilities;
|
||||||
|
|
||||||
constructor(tagType: BattlerTagType, ability: Abilities, lapseType: BattlerTagLapseType, turnCount: number) {
|
constructor(tagType: BattlerTagType, ability: Abilities, lapseType: BattlerTagLapseType, turnCount: number) {
|
||||||
super(tagType, lapseType, turnCount, undefined);
|
super(tagType, lapseType, turnCount);
|
||||||
|
|
||||||
this.ability = ability;
|
this.ability = ability;
|
||||||
}
|
}
|
||||||
|
@ -1438,7 +1440,7 @@ export class TypeImmuneTag extends BattlerTag {
|
||||||
public immuneType: Type;
|
public immuneType: Type;
|
||||||
|
|
||||||
constructor(tagType: BattlerTagType, sourceMove: Moves, immuneType: Type, length: number = 1) {
|
constructor(tagType: BattlerTagType, sourceMove: Moves, immuneType: Type, length: number = 1) {
|
||||||
super(tagType, BattlerTagLapseType.TURN_END, length, sourceMove);
|
super(tagType, BattlerTagLapseType.TURN_END, length, sourceMove, undefined, true);
|
||||||
|
|
||||||
this.immuneType = immuneType;
|
this.immuneType = immuneType;
|
||||||
}
|
}
|
||||||
|
@ -1502,7 +1504,7 @@ export class TypeBoostTag extends BattlerTag {
|
||||||
|
|
||||||
export class CritBoostTag extends BattlerTag {
|
export class CritBoostTag extends BattlerTag {
|
||||||
constructor(tagType: BattlerTagType, sourceMove: Moves) {
|
constructor(tagType: BattlerTagType, sourceMove: Moves) {
|
||||||
super(tagType, BattlerTagLapseType.TURN_END, 1, sourceMove);
|
super(tagType, BattlerTagLapseType.TURN_END, 1, sourceMove, undefined, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
onAdd(pokemon: Pokemon): void {
|
onAdd(pokemon: Pokemon): void {
|
||||||
|
@ -1594,7 +1596,7 @@ export class CursedTag extends BattlerTag {
|
||||||
private sourceIndex: number;
|
private sourceIndex: number;
|
||||||
|
|
||||||
constructor(sourceId: number) {
|
constructor(sourceId: number) {
|
||||||
super(BattlerTagType.CURSED, BattlerTagLapseType.TURN_END, 1, Moves.CURSE, sourceId);
|
super(BattlerTagType.CURSED, BattlerTagLapseType.TURN_END, 1, Moves.CURSE, sourceId, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import { PlayerPokemon } from "#app/field/pokemon";
|
||||||
|
import { DexEntry, StarterDataEntry } from "#app/system/game-data";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores data associated with a specific egg and the hatched pokemon
|
||||||
|
* Allows hatch info to be stored at hatch then retrieved for display during egg summary
|
||||||
|
*/
|
||||||
|
export class EggHatchData {
|
||||||
|
/** the pokemon that hatched from the file (including shiny, IVs, ability) */
|
||||||
|
public pokemon: PlayerPokemon;
|
||||||
|
/** index of the egg move from the hatched pokemon (not stored in PlayerPokemon) */
|
||||||
|
public eggMoveIndex: number;
|
||||||
|
/** boolean indicating if the egg move for the hatch is new */
|
||||||
|
public eggMoveUnlocked: boolean;
|
||||||
|
/** stored copy of the hatched pokemon's dex entry before it was updated due to hatch */
|
||||||
|
public dexEntryBeforeUpdate: DexEntry;
|
||||||
|
/** stored copy of the hatched pokemon's starter entry before it was updated due to hatch */
|
||||||
|
public starterDataEntryBeforeUpdate: StarterDataEntry;
|
||||||
|
/** reference to the battle scene to get gamedata and update dex */
|
||||||
|
private scene: BattleScene;
|
||||||
|
|
||||||
|
constructor(scene: BattleScene, pokemon: PlayerPokemon, eggMoveIndex: number) {
|
||||||
|
this.scene = scene;
|
||||||
|
this.pokemon = pokemon;
|
||||||
|
this.eggMoveIndex = eggMoveIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the boolean for if the egg move for the hatch is a new unlock
|
||||||
|
* @param unlocked True if the EM is new
|
||||||
|
*/
|
||||||
|
setEggMoveUnlocked(unlocked: boolean) {
|
||||||
|
this.eggMoveUnlocked = unlocked;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a copy of the current DexEntry of the pokemon and StarterDataEntry of its starter
|
||||||
|
* Used before updating the dex, so comparing the pokemon to these entries will show the new attributes
|
||||||
|
*/
|
||||||
|
setDex() {
|
||||||
|
const currDexEntry = this.scene.gameData.dexData[this.pokemon.species.speciesId];
|
||||||
|
const currStarterDataEntry = this.scene.gameData.starterData[this.pokemon.species.getRootSpeciesId()];
|
||||||
|
this.dexEntryBeforeUpdate = {
|
||||||
|
seenAttr: currDexEntry.seenAttr,
|
||||||
|
caughtAttr: currDexEntry.caughtAttr,
|
||||||
|
natureAttr: currDexEntry.natureAttr,
|
||||||
|
seenCount: currDexEntry.seenCount,
|
||||||
|
caughtCount: currDexEntry.caughtCount,
|
||||||
|
hatchedCount: currDexEntry.hatchedCount,
|
||||||
|
ivs: [...currDexEntry.ivs]
|
||||||
|
};
|
||||||
|
this.starterDataEntryBeforeUpdate = {
|
||||||
|
moveset: currStarterDataEntry.moveset,
|
||||||
|
eggMoves: currStarterDataEntry.eggMoves,
|
||||||
|
candyCount: currStarterDataEntry.candyCount,
|
||||||
|
friendship: currStarterDataEntry.friendship,
|
||||||
|
abilityAttr: currStarterDataEntry.abilityAttr,
|
||||||
|
passiveAttr: currStarterDataEntry.passiveAttr,
|
||||||
|
valueReduction: currStarterDataEntry.valueReduction,
|
||||||
|
classicWinCount: currStarterDataEntry.classicWinCount
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the dex entry before update
|
||||||
|
* @returns Dex Entry corresponding to this pokemon before the pokemon was added / updated to dex
|
||||||
|
*/
|
||||||
|
getDex(): DexEntry {
|
||||||
|
return this.dexEntryBeforeUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the starter dex entry before update
|
||||||
|
* @returns Starter Dex Entry corresponding to this pokemon before the pokemon was added / updated to dex
|
||||||
|
*/
|
||||||
|
getStarterEntry(): StarterDataEntry {
|
||||||
|
return this.starterDataEntryBeforeUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the pokedex data corresponding with the new hatch's pokemon data
|
||||||
|
* Also sets whether the egg move is a new unlock or not
|
||||||
|
* @param showMessage boolean to show messages for the new catches and egg moves (false by default)
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
updatePokemon(showMessage : boolean = false) {
|
||||||
|
return new Promise<void>(resolve => {
|
||||||
|
this.scene.gameData.setPokemonCaught(this.pokemon, true, true, showMessage).then(() => {
|
||||||
|
this.scene.gameData.updateSpeciesDexIvs(this.pokemon.species.speciesId, this.pokemon.ivs);
|
||||||
|
this.scene.gameData.setEggMoveUnlocked(this.pokemon.species, this.eggMoveIndex, showMessage).then((value) => {
|
||||||
|
this.setEggMoveUnlocked(value);
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -2660,11 +2660,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const tag of source.summonData.tags) {
|
for (const tag of source.summonData.tags) {
|
||||||
|
if (!tag.isBatonPassable) {
|
||||||
// bypass those can not be passed via Baton Pass
|
|
||||||
const excludeTagTypes = new Set([BattlerTagType.DROWSY, BattlerTagType.INFATUATED, BattlerTagType.FIRE_BOOST]);
|
|
||||||
|
|
||||||
if (excludeTagTypes.has(tag.tagType)) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -78,6 +78,7 @@ export class LoadingScene extends SceneBase {
|
||||||
this.loadAtlas("overlay_hp_boss", "ui");
|
this.loadAtlas("overlay_hp_boss", "ui");
|
||||||
this.loadImage("overlay_exp", "ui");
|
this.loadImage("overlay_exp", "ui");
|
||||||
this.loadImage("icon_owned", "ui");
|
this.loadImage("icon_owned", "ui");
|
||||||
|
this.loadImage("icon_egg_move", "ui");
|
||||||
this.loadImage("ability_bar_left", "ui");
|
this.loadImage("ability_bar_left", "ui");
|
||||||
this.loadImage("bgm_bar", "ui");
|
this.loadImage("bgm_bar", "ui");
|
||||||
this.loadImage("party_exp_bar", "ui");
|
this.loadImage("party_exp_bar", "ui");
|
||||||
|
@ -272,6 +273,7 @@ export class LoadingScene extends SceneBase {
|
||||||
this.loadImage("gacha_knob", "egg");
|
this.loadImage("gacha_knob", "egg");
|
||||||
|
|
||||||
this.loadImage("egg_list_bg", "ui");
|
this.loadImage("egg_list_bg", "ui");
|
||||||
|
this.loadImage("egg_summary_bg", "ui");
|
||||||
|
|
||||||
this.loadImage("end_m", "cg");
|
this.loadImage("end_m", "cg");
|
||||||
this.loadImage("end_f", "cg");
|
this.loadImage("end_f", "cg");
|
||||||
|
|
|
@ -39,5 +39,6 @@
|
||||||
"matBlock": "Mat Block",
|
"matBlock": "Mat Block",
|
||||||
"craftyShield": "Crafty Shield",
|
"craftyShield": "Crafty Shield",
|
||||||
"tailwind": "Tailwind",
|
"tailwind": "Tailwind",
|
||||||
"happyHour": "Happy Hour"
|
"happyHour": "Happy Hour",
|
||||||
|
"safeguard": "Safeguard"
|
||||||
}
|
}
|
|
@ -47,5 +47,11 @@
|
||||||
"tailwindOnRemovePlayer": "Your team's Tailwind petered out!",
|
"tailwindOnRemovePlayer": "Your team's Tailwind petered out!",
|
||||||
"tailwindOnRemoveEnemy": "The opposing team's Tailwind petered out!",
|
"tailwindOnRemoveEnemy": "The opposing team's Tailwind petered out!",
|
||||||
"happyHourOnAdd": "Everyone is caught up in the happy atmosphere!",
|
"happyHourOnAdd": "Everyone is caught up in the happy atmosphere!",
|
||||||
"happyHourOnRemove": "The atmosphere returned to normal."
|
"happyHourOnRemove": "The atmosphere returned to normal.",
|
||||||
|
"safeguardOnAdd": "The whole field is cloaked in a mystical veil!",
|
||||||
|
"safeguardOnAddPlayer": "Your team cloaked itself in a mystical veil!",
|
||||||
|
"safeguardOnAddEnemy": "The opposing team cloaked itself in a mystical veil!",
|
||||||
|
"safeguardOnRemove": "The field is no longer protected by Safeguard!",
|
||||||
|
"safeguardOnRemovePlayer": "Your team is no longer protected by Safeguard!",
|
||||||
|
"safeguardOnRemoveEnemy": "The opposing team is no longer protected by Safeguard!"
|
||||||
}
|
}
|
|
@ -61,6 +61,7 @@
|
||||||
"skipItemQuestion": "Are you sure you want to skip taking an item?",
|
"skipItemQuestion": "Are you sure you want to skip taking an item?",
|
||||||
"itemStackFull": "The stack for {{fullItemName}} is full.\nYou will receive {{itemName}} instead.",
|
"itemStackFull": "The stack for {{fullItemName}} is full.\nYou will receive {{itemName}} instead.",
|
||||||
"eggHatching": "Oh?",
|
"eggHatching": "Oh?",
|
||||||
|
"eggSkipPrompt": "Skip to egg summary?",
|
||||||
"ivScannerUseQuestion": "Use IV Scanner on {{pokemonName}}?",
|
"ivScannerUseQuestion": "Use IV Scanner on {{pokemonName}}?",
|
||||||
"wildPokemonWithAffix": "Wild {{pokemonName}}",
|
"wildPokemonWithAffix": "Wild {{pokemonName}}",
|
||||||
"foePokemonWithAffix": "Foe {{pokemonName}}",
|
"foePokemonWithAffix": "Foe {{pokemonName}}",
|
||||||
|
|
|
@ -413,7 +413,7 @@
|
||||||
},
|
},
|
||||||
"ariana": {
|
"ariana": {
|
||||||
"encounter": {
|
"encounter": {
|
||||||
"1": "Hold it right there! We can't someone on the loose.\n$It's harmful to Team Rocket's pride, you see.",
|
"1": "Hold it right there!\nWe can't have someone on the loose.\n$It's harmful to Team Rocket's pride, you see.",
|
||||||
"2": "I don't know or care if what I'm doing is right or wrong...\n$I just put my faith in Giovanni and do as I am told",
|
"2": "I don't know or care if what I'm doing is right or wrong...\n$I just put my faith in Giovanni and do as I am told",
|
||||||
"3": "Your trip ends here. I'm going to take you down!"
|
"3": "Your trip ends here. I'm going to take you down!"
|
||||||
},
|
},
|
||||||
|
|
|
@ -65,5 +65,6 @@
|
||||||
"suppressAbilities": "{{pokemonName}}'s ability\nwas suppressed!",
|
"suppressAbilities": "{{pokemonName}}'s ability\nwas suppressed!",
|
||||||
"revivalBlessing": "{{pokemonName}} was revived!",
|
"revivalBlessing": "{{pokemonName}} was revived!",
|
||||||
"swapArenaTags": "{{pokemonName}} swapped the battle effects affecting each side of the field!",
|
"swapArenaTags": "{{pokemonName}} swapped the battle effects affecting each side of the field!",
|
||||||
"exposedMove": "{{pokemonName}} identified\n{{targetPokemonName}}!"
|
"exposedMove": "{{pokemonName}} identified\n{{targetPokemonName}}!",
|
||||||
|
"safeguard": "{{targetName}} is protected by Safeguard!"
|
||||||
}
|
}
|
|
@ -1,23 +1,29 @@
|
||||||
import BattleScene, { AnySound } from "#app/battle-scene.js";
|
import BattleScene, { AnySound } from "#app/battle-scene";
|
||||||
import { Egg, EGG_SEED } from "#app/data/egg.js";
|
import { Egg } from "#app/data/egg";
|
||||||
import { EggCountChangedEvent } from "#app/events/egg.js";
|
import { EggCountChangedEvent } from "#app/events/egg";
|
||||||
import { PlayerPokemon } from "#app/field/pokemon.js";
|
import { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages.js";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import { Phase } from "#app/phase.js";
|
import { Phase } from "#app/phase";
|
||||||
import { achvs } from "#app/system/achv.js";
|
import { achvs } from "#app/system/achv";
|
||||||
import EggCounterContainer from "#app/ui/egg-counter-container.js";
|
import EggCounterContainer from "#app/ui/egg-counter-container";
|
||||||
import EggHatchSceneHandler from "#app/ui/egg-hatch-scene-handler.js";
|
import EggHatchSceneHandler from "#app/ui/egg-hatch-scene-handler";
|
||||||
import PokemonInfoContainer from "#app/ui/pokemon-info-container.js";
|
import PokemonInfoContainer from "#app/ui/pokemon-info-container";
|
||||||
import { Mode } from "#app/ui/ui.js";
|
import { Mode } from "#app/ui/ui";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
|
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
|
||||||
import * as Utils from "#app/utils.js";
|
import * as Utils from "#app/utils";
|
||||||
|
import { EggLapsePhase } from "./egg-lapse-phase";
|
||||||
|
import { EggHatchData } from "#app/data/egg-hatch-data";
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class that represents egg hatching
|
* Class that represents egg hatching
|
||||||
*/
|
*/
|
||||||
export class EggHatchPhase extends Phase {
|
export class EggHatchPhase extends Phase {
|
||||||
/** The egg that is hatching */
|
/** The egg that is hatching */
|
||||||
private egg: Egg;
|
private egg: Egg;
|
||||||
|
/** The new EggHatchData for the egg/pokemon that hatches */
|
||||||
|
private eggHatchData: EggHatchData;
|
||||||
|
|
||||||
/** The number of eggs that are hatching */
|
/** The number of eggs that are hatching */
|
||||||
private eggsToHatchCount: integer;
|
private eggsToHatchCount: integer;
|
||||||
|
@ -58,10 +64,11 @@ export class EggHatchPhase extends Phase {
|
||||||
private skipped: boolean;
|
private skipped: boolean;
|
||||||
/** The sound effect being played when the egg is hatched */
|
/** The sound effect being played when the egg is hatched */
|
||||||
private evolutionBgm: AnySound;
|
private evolutionBgm: AnySound;
|
||||||
|
private eggLapsePhase: EggLapsePhase;
|
||||||
|
|
||||||
constructor(scene: BattleScene, egg: Egg, eggsToHatchCount: integer) {
|
constructor(scene: BattleScene, hatchScene: EggLapsePhase, egg: Egg, eggsToHatchCount: integer) {
|
||||||
super(scene);
|
super(scene);
|
||||||
|
this.eggLapsePhase = hatchScene;
|
||||||
this.egg = egg;
|
this.egg = egg;
|
||||||
this.eggsToHatchCount = eggsToHatchCount;
|
this.eggsToHatchCount = eggsToHatchCount;
|
||||||
}
|
}
|
||||||
|
@ -307,6 +314,7 @@ export class EggHatchPhase extends Phase {
|
||||||
* Function to do the logic and animation of completing a hatch and revealing the Pokemon
|
* Function to do the logic and animation of completing a hatch and revealing the Pokemon
|
||||||
*/
|
*/
|
||||||
doReveal(): void {
|
doReveal(): void {
|
||||||
|
// set the previous dex data so info container can show new unlocks in egg summary
|
||||||
const isShiny = this.pokemon.isShiny();
|
const isShiny = this.pokemon.isShiny();
|
||||||
if (this.pokemon.species.subLegendary) {
|
if (this.pokemon.species.subLegendary) {
|
||||||
this.scene.validateAchv(achvs.HATCH_SUB_LEGENDARY);
|
this.scene.validateAchv(achvs.HATCH_SUB_LEGENDARY);
|
||||||
|
@ -345,13 +353,13 @@ export class EggHatchPhase extends Phase {
|
||||||
this.scene.ui.showText(i18next.t("egg:hatchFromTheEgg", { pokemonName: getPokemonNameWithAffix(this.pokemon) }), null, () => {
|
this.scene.ui.showText(i18next.t("egg:hatchFromTheEgg", { pokemonName: getPokemonNameWithAffix(this.pokemon) }), null, () => {
|
||||||
this.scene.gameData.updateSpeciesDexIvs(this.pokemon.species.speciesId, this.pokemon.ivs);
|
this.scene.gameData.updateSpeciesDexIvs(this.pokemon.species.speciesId, this.pokemon.ivs);
|
||||||
this.scene.gameData.setPokemonCaught(this.pokemon, true, true).then(() => {
|
this.scene.gameData.setPokemonCaught(this.pokemon, true, true).then(() => {
|
||||||
this.scene.gameData.setEggMoveUnlocked(this.pokemon.species, this.eggMoveIndex).then(() => {
|
this.scene.gameData.setEggMoveUnlocked(this.pokemon.species, this.eggMoveIndex).then((value) => {
|
||||||
|
this.eggHatchData.setEggMoveUnlocked(value);
|
||||||
this.scene.ui.showText("", 0);
|
this.scene.ui.showText("", 0);
|
||||||
this.end();
|
this.end();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}, null, true, 3000);
|
}, null, true, 3000);
|
||||||
//this.scene.time.delayedCall(Utils.fixedInt(4250), () => this.scene.playBgm());
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this.scene.tweens.add({
|
this.scene.tweens.add({
|
||||||
|
@ -435,17 +443,11 @@ export class EggHatchPhase extends Phase {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a Pokemon to be hatched by the egg
|
* Generates a Pokemon to be hatched by the egg
|
||||||
|
* Also stores the generated pokemon in this.eggHatchData
|
||||||
* @returns the hatched PlayerPokemon
|
* @returns the hatched PlayerPokemon
|
||||||
*/
|
*/
|
||||||
generatePokemon(): PlayerPokemon {
|
generatePokemon(): PlayerPokemon {
|
||||||
let ret: PlayerPokemon;
|
this.eggHatchData = this.eggLapsePhase.generatePokemon(this.egg);
|
||||||
|
return this.eggHatchData.pokemon;
|
||||||
this.scene.executeWithSeedOffset(() => {
|
|
||||||
ret = this.egg.generatePlayerPokemon(this.scene);
|
|
||||||
this.eggMoveIndex = this.egg.eggMoveIndex;
|
|
||||||
|
|
||||||
}, this.egg.id, EGG_SEED.toString());
|
|
||||||
|
|
||||||
return ret!;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,23 @@
|
||||||
import BattleScene from "#app/battle-scene.js";
|
import BattleScene from "#app/battle-scene";
|
||||||
import { Egg } from "#app/data/egg.js";
|
import { Egg, EGG_SEED } from "#app/data/egg";
|
||||||
import { Phase } from "#app/phase.js";
|
import { Phase } from "#app/phase";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import Overrides from "#app/overrides";
|
import Overrides from "#app/overrides";
|
||||||
import { EggHatchPhase } from "./egg-hatch-phase";
|
import { EggHatchPhase } from "./egg-hatch-phase";
|
||||||
|
import { Mode } from "#app/ui/ui";
|
||||||
|
import { achvs } from "#app/system/achv";
|
||||||
|
import { PlayerPokemon } from "#app/field/pokemon";
|
||||||
|
import { EggSummaryPhase } from "./egg-summary-phase";
|
||||||
|
import { EggHatchData } from "#app/data/egg-hatch-data";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Phase that handles updating eggs, and hatching any ready eggs
|
||||||
|
* Also handles prompts for skipping animation, and calling the egg summary phase
|
||||||
|
*/
|
||||||
export class EggLapsePhase extends Phase {
|
export class EggLapsePhase extends Phase {
|
||||||
|
|
||||||
|
private eggHatchData: EggHatchData[] = [];
|
||||||
|
private readonly minEggsToPromptSkip: number = 5;
|
||||||
constructor(scene: BattleScene) {
|
constructor(scene: BattleScene) {
|
||||||
super(scene);
|
super(scene);
|
||||||
}
|
}
|
||||||
|
@ -16,20 +28,111 @@ export class EggLapsePhase extends Phase {
|
||||||
const eggsToHatch: Egg[] = this.scene.gameData.eggs.filter((egg: Egg) => {
|
const eggsToHatch: Egg[] = this.scene.gameData.eggs.filter((egg: Egg) => {
|
||||||
return Overrides.EGG_IMMEDIATE_HATCH_OVERRIDE ? true : --egg.hatchWaves < 1;
|
return Overrides.EGG_IMMEDIATE_HATCH_OVERRIDE ? true : --egg.hatchWaves < 1;
|
||||||
});
|
});
|
||||||
|
const eggsToHatchCount: number = eggsToHatch.length;
|
||||||
|
this.eggHatchData= [];
|
||||||
|
|
||||||
let eggCount: integer = eggsToHatch.length;
|
if (eggsToHatchCount > 0) {
|
||||||
|
|
||||||
if (eggCount) {
|
if (eggsToHatchCount >= this.minEggsToPromptSkip) {
|
||||||
|
this.scene.ui.showText(i18next.t("battle:eggHatching"), 0, () => {
|
||||||
|
// show prompt for skip
|
||||||
|
this.scene.ui.showText(i18next.t("battle:eggSkipPrompt"), 0);
|
||||||
|
this.scene.ui.setModeWithoutClear(Mode.CONFIRM, () => {
|
||||||
|
this.hatchEggsSkipped(eggsToHatch);
|
||||||
|
this.showSummary();
|
||||||
|
}, () => {
|
||||||
|
this.hatchEggsRegular(eggsToHatch);
|
||||||
|
this.showSummary();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}, 100, true);
|
||||||
|
} else {
|
||||||
|
// regular hatches, no summary
|
||||||
this.scene.queueMessage(i18next.t("battle:eggHatching"));
|
this.scene.queueMessage(i18next.t("battle:eggHatching"));
|
||||||
|
this.hatchEggsRegular(eggsToHatch);
|
||||||
for (const egg of eggsToHatch) {
|
|
||||||
this.scene.unshiftPhase(new EggHatchPhase(this.scene, egg, eggCount));
|
|
||||||
if (eggCount > 0) {
|
|
||||||
eggCount--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
this.end();
|
this.end();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
this.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hatches eggs normally one by one, showing animations
|
||||||
|
* @param eggsToHatch list of eggs to hatch
|
||||||
|
*/
|
||||||
|
hatchEggsRegular(eggsToHatch: Egg[]) {
|
||||||
|
let eggsToHatchCount: number = eggsToHatch.length;
|
||||||
|
for (const egg of eggsToHatch) {
|
||||||
|
this.scene.unshiftPhase(new EggHatchPhase(this.scene, this, egg, eggsToHatchCount));
|
||||||
|
eggsToHatchCount--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hatches eggs with no animations
|
||||||
|
* @param eggsToHatch list of eggs to hatch
|
||||||
|
*/
|
||||||
|
hatchEggsSkipped(eggsToHatch: Egg[]) {
|
||||||
|
for (const egg of eggsToHatch) {
|
||||||
|
this.hatchEggSilently(egg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showSummary() {
|
||||||
|
this.scene.unshiftPhase(new EggSummaryPhase(this.scene, this.eggHatchData));
|
||||||
|
this.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hatches an egg and stores it in the local EggHatchData array without animations
|
||||||
|
* Also validates the achievements for the hatched pokemon and removes the egg
|
||||||
|
* @param egg egg to hatch
|
||||||
|
*/
|
||||||
|
hatchEggSilently(egg: Egg) {
|
||||||
|
const eggIndex = this.scene.gameData.eggs.findIndex(e => e.id === egg.id);
|
||||||
|
if (eggIndex === -1) {
|
||||||
|
return this.end();
|
||||||
|
}
|
||||||
|
this.scene.gameData.eggs.splice(eggIndex, 1);
|
||||||
|
|
||||||
|
const data = this.generatePokemon(egg);
|
||||||
|
const pokemon = data.pokemon;
|
||||||
|
if (pokemon.fusionSpecies) {
|
||||||
|
pokemon.clearFusionSpecies();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pokemon.species.subLegendary) {
|
||||||
|
this.scene.validateAchv(achvs.HATCH_SUB_LEGENDARY);
|
||||||
|
}
|
||||||
|
if (pokemon.species.legendary) {
|
||||||
|
this.scene.validateAchv(achvs.HATCH_LEGENDARY);
|
||||||
|
}
|
||||||
|
if (pokemon.species.mythical) {
|
||||||
|
this.scene.validateAchv(achvs.HATCH_MYTHICAL);
|
||||||
|
}
|
||||||
|
if (pokemon.isShiny()) {
|
||||||
|
this.scene.validateAchv(achvs.HATCH_SHINY);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a Pokemon and creates a new EggHatchData instance for the given egg
|
||||||
|
* @param egg the egg to hatch
|
||||||
|
* @returns the hatched PlayerPokemon
|
||||||
|
*/
|
||||||
|
generatePokemon(egg: Egg): EggHatchData {
|
||||||
|
let ret: PlayerPokemon;
|
||||||
|
let newHatchData: EggHatchData;
|
||||||
|
this.scene.executeWithSeedOffset(() => {
|
||||||
|
ret = egg.generatePlayerPokemon(this.scene);
|
||||||
|
newHatchData = new EggHatchData(this.scene, ret, egg.eggMoveIndex);
|
||||||
|
newHatchData.setDex();
|
||||||
|
this.eggHatchData.push(newHatchData);
|
||||||
|
|
||||||
|
}, egg.id, EGG_SEED.toString());
|
||||||
|
return newHatchData!;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import { Phase } from "#app/phase";
|
||||||
|
import { Mode } from "#app/ui/ui";
|
||||||
|
import EggHatchSceneHandler from "#app/ui/egg-hatch-scene-handler";
|
||||||
|
import { EggHatchData } from "#app/data/egg-hatch-data";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that represents the egg summary phase
|
||||||
|
* It does some of the function for updating egg data
|
||||||
|
* Phase is handled mostly by the egg-hatch-scene-handler UI
|
||||||
|
*/
|
||||||
|
export class EggSummaryPhase extends Phase {
|
||||||
|
private eggHatchData: EggHatchData[];
|
||||||
|
private eggHatchHandler: EggHatchSceneHandler;
|
||||||
|
|
||||||
|
constructor(scene: BattleScene, eggHatchData: EggHatchData[]) {
|
||||||
|
super(scene);
|
||||||
|
this.eggHatchData = eggHatchData;
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
super.start();
|
||||||
|
|
||||||
|
// updates next pokemon once the current update has been completed
|
||||||
|
const updateNextPokemon = (i: number) => {
|
||||||
|
if (i >= this.eggHatchData.length) {
|
||||||
|
this.scene.ui.setModeForceTransition(Mode.EGG_HATCH_SUMMARY, this.eggHatchData).then(() => {
|
||||||
|
this.scene.fadeOutBgm(undefined, false);
|
||||||
|
this.eggHatchHandler = this.scene.ui.getHandler() as EggHatchSceneHandler;
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
this.eggHatchData[i].setDex();
|
||||||
|
this.eggHatchData[i].updatePokemon().then(() => {
|
||||||
|
if (i < this.eggHatchData.length) {
|
||||||
|
updateNextPokemon(i + 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
updateNextPokemon(0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
end() {
|
||||||
|
this.eggHatchHandler.clear();
|
||||||
|
this.scene.ui.setModeForceTransition(Mode.MESSAGE).then(() => {});
|
||||||
|
super.end();
|
||||||
|
}
|
||||||
|
}
|
|
@ -77,4 +77,8 @@ export class EnemyCommandPhase extends FieldPhase {
|
||||||
|
|
||||||
this.end();
|
this.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getFieldIndex(): number {
|
||||||
|
return this.fieldIndex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1553,11 +1553,11 @@ export class GameData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setPokemonCaught(pokemon: Pokemon, incrementCount: boolean = true, fromEgg: boolean = false): Promise<void> {
|
setPokemonCaught(pokemon: Pokemon, incrementCount: boolean = true, fromEgg: boolean = false, showMessage: boolean = true): Promise<void> {
|
||||||
return this.setPokemonSpeciesCaught(pokemon, pokemon.species, incrementCount, fromEgg);
|
return this.setPokemonSpeciesCaught(pokemon, pokemon.species, incrementCount, fromEgg, showMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
setPokemonSpeciesCaught(pokemon: Pokemon, species: PokemonSpecies, incrementCount: boolean = true, fromEgg: boolean = false): Promise<void> {
|
setPokemonSpeciesCaught(pokemon: Pokemon, species: PokemonSpecies, incrementCount: boolean = true, fromEgg: boolean = false, showMessage: boolean = true): Promise<void> {
|
||||||
return new Promise<void>(resolve => {
|
return new Promise<void>(resolve => {
|
||||||
const dexEntry = this.dexData[species.speciesId];
|
const dexEntry = this.dexData[species.speciesId];
|
||||||
const caughtAttr = dexEntry.caughtAttr;
|
const caughtAttr = dexEntry.caughtAttr;
|
||||||
|
@ -1616,13 +1616,17 @@ export class GameData {
|
||||||
const checkPrevolution = () => {
|
const checkPrevolution = () => {
|
||||||
if (hasPrevolution) {
|
if (hasPrevolution) {
|
||||||
const prevolutionSpecies = pokemonPrevolutions[species.speciesId];
|
const prevolutionSpecies = pokemonPrevolutions[species.speciesId];
|
||||||
return this.setPokemonSpeciesCaught(pokemon, getPokemonSpecies(prevolutionSpecies), incrementCount, fromEgg).then(() => resolve());
|
this.setPokemonSpeciesCaught(pokemon, getPokemonSpecies(prevolutionSpecies), incrementCount, fromEgg, showMessage).then(() => resolve());
|
||||||
} else {
|
} else {
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (newCatch && speciesStarters.hasOwnProperty(species.speciesId)) {
|
if (newCatch && speciesStarters.hasOwnProperty(species.speciesId)) {
|
||||||
|
if (!showMessage) {
|
||||||
|
resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.scene.playSound("level_up_fanfare");
|
this.scene.playSound("level_up_fanfare");
|
||||||
this.scene.ui.showText(i18next.t("battle:addedAsAStarter", { pokemonName: species.name }), null, () => checkPrevolution(), null, true);
|
this.scene.ui.showText(i18next.t("battle:addedAsAStarter", { pokemonName: species.name }), null, () => checkPrevolution(), null, true);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1668,7 +1672,7 @@ export class GameData {
|
||||||
this.starterData[species.speciesId].candyCount += count;
|
this.starterData[species.speciesId].candyCount += count;
|
||||||
}
|
}
|
||||||
|
|
||||||
setEggMoveUnlocked(species: PokemonSpecies, eggMoveIndex: integer): Promise<boolean> {
|
setEggMoveUnlocked(species: PokemonSpecies, eggMoveIndex: integer, showMessage: boolean = true): Promise<boolean> {
|
||||||
return new Promise<boolean>(resolve => {
|
return new Promise<boolean>(resolve => {
|
||||||
const speciesId = species.speciesId;
|
const speciesId = species.speciesId;
|
||||||
if (!speciesEggMoves.hasOwnProperty(speciesId) || !speciesEggMoves[speciesId][eggMoveIndex]) {
|
if (!speciesEggMoves.hasOwnProperty(speciesId) || !speciesEggMoves[speciesId][eggMoveIndex]) {
|
||||||
|
@ -1688,11 +1692,15 @@ export class GameData {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.starterData[speciesId].eggMoves |= value;
|
this.starterData[speciesId].eggMoves |= value;
|
||||||
|
if (!showMessage) {
|
||||||
|
resolve(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.scene.playSound("level_up_fanfare");
|
this.scene.playSound("level_up_fanfare");
|
||||||
|
|
||||||
const moveName = allMoves[speciesEggMoves[speciesId][eggMoveIndex]].name;
|
const moveName = allMoves[speciesEggMoves[speciesId][eggMoveIndex]].name;
|
||||||
this.scene.ui.showText(eggMoveIndex === 3 ? i18next.t("egg:rareEggMoveUnlock", { moveName: moveName }) : i18next.t("egg:eggMoveUnlock", { moveName: moveName }), null, () => resolve(true), null, true);
|
this.scene.ui.showText(eggMoveIndex === 3 ? i18next.t("egg:rareEggMoveUnlock", { moveName: moveName }) : i18next.t("egg:eggMoveUnlock", { moveName: moveName }), null, (() => {
|
||||||
|
resolve(true);
|
||||||
|
}), null, true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import { Stat } from "#enums/stat";
|
import { BattlerIndex } from "#app/battle";
|
||||||
import { PostSummonPhase } from "#app/phases/post-summon-phase";
|
|
||||||
import { TurnEndPhase } from "#app/phases/turn-end-phase";
|
|
||||||
import GameManager from "#app/test/utils/gameManager";
|
import GameManager from "#app/test/utils/gameManager";
|
||||||
|
import { Abilities } from "#enums/abilities";
|
||||||
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
|
import { Stat } from "#enums/stat";
|
||||||
|
import { SPLASH_ONLY } from "#test/utils/testUtils";
|
||||||
import Phaser from "phaser";
|
import Phaser from "phaser";
|
||||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||||
import { SPLASH_ONLY } from "../utils/testUtils";
|
|
||||||
|
|
||||||
|
|
||||||
describe("Moves - Baton Pass", () => {
|
describe("Moves - Baton Pass", () => {
|
||||||
let phaserGame: Phaser.Game;
|
let phaserGame: Phaser.Game;
|
||||||
|
@ -27,20 +27,17 @@ describe("Moves - Baton Pass", () => {
|
||||||
game = new GameManager(phaserGame);
|
game = new GameManager(phaserGame);
|
||||||
game.override
|
game.override
|
||||||
.battleType("single")
|
.battleType("single")
|
||||||
.enemySpecies(Species.DUGTRIO)
|
.enemySpecies(Species.MAGIKARP)
|
||||||
.startingLevel(1)
|
.enemyAbility(Abilities.BALL_FETCH)
|
||||||
.startingWave(97)
|
|
||||||
.moveset([Moves.BATON_PASS, Moves.NASTY_PLOT, Moves.SPLASH])
|
.moveset([Moves.BATON_PASS, Moves.NASTY_PLOT, Moves.SPLASH])
|
||||||
|
.ability(Abilities.BALL_FETCH)
|
||||||
.enemyMoveset(SPLASH_ONLY)
|
.enemyMoveset(SPLASH_ONLY)
|
||||||
.disableCrits();
|
.disableCrits();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("transfers all stat stages when player uses it", async() => {
|
it("transfers all stat stages when player uses it", async() => {
|
||||||
// arrange
|
// arrange
|
||||||
await game.startBattle([
|
await game.classicMode.startBattle([Species.RAICHU, Species.SHUCKLE]);
|
||||||
Species.RAICHU,
|
|
||||||
Species.SHUCKLE
|
|
||||||
]);
|
|
||||||
|
|
||||||
// round 1 - buff
|
// round 1 - buff
|
||||||
game.move.select(Moves.NASTY_PLOT);
|
game.move.select(Moves.NASTY_PLOT);
|
||||||
|
@ -53,7 +50,7 @@ describe("Moves - Baton Pass", () => {
|
||||||
// round 2 - baton pass
|
// round 2 - baton pass
|
||||||
game.move.select(Moves.BATON_PASS);
|
game.move.select(Moves.BATON_PASS);
|
||||||
game.doSelectPartyPokemon(1);
|
game.doSelectPartyPokemon(1);
|
||||||
await game.phaseInterceptor.to(TurnEndPhase);
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
playerPokemon = game.scene.getPlayerPokemon()!;
|
playerPokemon = game.scene.getPlayerPokemon()!;
|
||||||
|
@ -66,10 +63,7 @@ describe("Moves - Baton Pass", () => {
|
||||||
game.override
|
game.override
|
||||||
.startingWave(5)
|
.startingWave(5)
|
||||||
.enemyMoveset(new Array(4).fill([Moves.NASTY_PLOT]));
|
.enemyMoveset(new Array(4).fill([Moves.NASTY_PLOT]));
|
||||||
await game.startBattle([
|
await game.classicMode.startBattle([Species.RAICHU, Species.SHUCKLE]);
|
||||||
Species.RAICHU,
|
|
||||||
Species.SHUCKLE
|
|
||||||
]);
|
|
||||||
|
|
||||||
// round 1 - ai buffs
|
// round 1 - ai buffs
|
||||||
game.move.select(Moves.SPLASH);
|
game.move.select(Moves.SPLASH);
|
||||||
|
@ -79,7 +73,7 @@ describe("Moves - Baton Pass", () => {
|
||||||
game.scene.getEnemyPokemon()!.hp = 100;
|
game.scene.getEnemyPokemon()!.hp = 100;
|
||||||
game.override.enemyMoveset(new Array(4).fill(Moves.BATON_PASS));
|
game.override.enemyMoveset(new Array(4).fill(Moves.BATON_PASS));
|
||||||
game.move.select(Moves.SPLASH);
|
game.move.select(Moves.SPLASH);
|
||||||
await game.phaseInterceptor.to(PostSummonPhase, false);
|
await game.phaseInterceptor.to("PostSummonPhase", false);
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
// check buffs are still there
|
// check buffs are still there
|
||||||
|
@ -94,4 +88,20 @@ describe("Moves - Baton Pass", () => {
|
||||||
"PostSummonPhase"
|
"PostSummonPhase"
|
||||||
]);
|
]);
|
||||||
}, 20000);
|
}, 20000);
|
||||||
|
|
||||||
|
it("doesn't transfer effects that aren't transferrable", async() => {
|
||||||
|
game.override.enemyMoveset(Array(4).fill(Moves.SALT_CURE));
|
||||||
|
await game.classicMode.startBattle([Species.PIKACHU, Species.FEEBAS]);
|
||||||
|
|
||||||
|
const [player1, player2] = game.scene.getParty();
|
||||||
|
|
||||||
|
game.move.select(Moves.BATON_PASS);
|
||||||
|
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
|
||||||
|
await game.phaseInterceptor.to("MoveEndPhase");
|
||||||
|
expect(player1.findTag((t) => t.tagType === BattlerTagType.SALT_CURED)).toBeTruthy();
|
||||||
|
game.doSelectPartyPokemon(1);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(player2.findTag((t) => t.tagType === BattlerTagType.SALT_CURED)).toBeUndefined();
|
||||||
|
}, 20000);
|
||||||
});
|
});
|
||||||
|
|
|
@ -110,7 +110,7 @@ describe("Moves - Ceaseless Edge", () => {
|
||||||
|
|
||||||
const hpBeforeSpikes = game.scene.currentBattle.enemyParty[1].hp;
|
const hpBeforeSpikes = game.scene.currentBattle.enemyParty[1].hp;
|
||||||
// Check HP of pokemon that WILL BE switched in (index 1)
|
// Check HP of pokemon that WILL BE switched in (index 1)
|
||||||
game.forceOpponentToSwitch();
|
game.forceEnemyToSwitch();
|
||||||
game.move.select(Moves.SPLASH);
|
game.move.select(Moves.SPLASH);
|
||||||
await game.phaseInterceptor.to(TurnEndPhase, false);
|
await game.phaseInterceptor.to(TurnEndPhase, false);
|
||||||
expect(game.scene.currentBattle.enemyParty[0].hp).toBeLessThan(hpBeforeSpikes);
|
expect(game.scene.currentBattle.enemyParty[0].hp).toBeLessThan(hpBeforeSpikes);
|
||||||
|
|
|
@ -123,7 +123,7 @@ describe("Moves - Focus Punch", () => {
|
||||||
|
|
||||||
await game.startBattle([Species.CHARIZARD]);
|
await game.startBattle([Species.CHARIZARD]);
|
||||||
|
|
||||||
game.forceOpponentToSwitch();
|
game.forceEnemyToSwitch();
|
||||||
game.move.select(Moves.FOCUS_PUNCH);
|
game.move.select(Moves.FOCUS_PUNCH);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(TurnStartPhase);
|
await game.phaseInterceptor.to(TurnStartPhase);
|
||||||
|
|
|
@ -28,48 +28,55 @@ describe("Moves - Follow Me", () => {
|
||||||
game = new GameManager(phaserGame);
|
game = new GameManager(phaserGame);
|
||||||
game.override.battleType("double");
|
game.override.battleType("double");
|
||||||
game.override.starterSpecies(Species.AMOONGUSS);
|
game.override.starterSpecies(Species.AMOONGUSS);
|
||||||
|
game.override.ability(Abilities.BALL_FETCH);
|
||||||
game.override.enemySpecies(Species.SNORLAX);
|
game.override.enemySpecies(Species.SNORLAX);
|
||||||
game.override.startingLevel(100);
|
game.override.startingLevel(100);
|
||||||
game.override.enemyLevel(100);
|
game.override.enemyLevel(100);
|
||||||
game.override.moveset([Moves.FOLLOW_ME, Moves.RAGE_POWDER, Moves.SPOTLIGHT, Moves.QUICK_ATTACK]);
|
game.override.moveset([Moves.FOLLOW_ME, Moves.RAGE_POWDER, Moves.SPOTLIGHT, Moves.QUICK_ATTACK]);
|
||||||
game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]);
|
game.override.enemyMoveset([Moves.TACKLE, Moves.FOLLOW_ME, Moves.SPLASH]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test(
|
test(
|
||||||
"move should redirect enemy attacks to the user",
|
"move should redirect enemy attacks to the user",
|
||||||
async () => {
|
async () => {
|
||||||
await game.startBattle([Species.AMOONGUSS, Species.CHARIZARD]);
|
await game.classicMode.startBattle([Species.AMOONGUSS, Species.CHARIZARD]);
|
||||||
|
|
||||||
const playerPokemon = game.scene.getPlayerField();
|
const playerPokemon = game.scene.getPlayerField();
|
||||||
|
|
||||||
const playerStartingHp = playerPokemon.map(p => p.hp);
|
|
||||||
|
|
||||||
game.move.select(Moves.FOLLOW_ME);
|
game.move.select(Moves.FOLLOW_ME);
|
||||||
game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY);
|
game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY);
|
||||||
|
|
||||||
|
// Force both enemies to target the player Pokemon that did not use Follow Me
|
||||||
|
await game.forceEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER_2);
|
||||||
|
await game.forceEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER_2);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(TurnEndPhase, false);
|
await game.phaseInterceptor.to(TurnEndPhase, false);
|
||||||
|
|
||||||
expect(playerPokemon[0].hp).toBeLessThan(playerStartingHp[0]);
|
expect(playerPokemon[0].hp).toBeLessThan(playerPokemon[0].getMaxHp());
|
||||||
expect(playerPokemon[1].hp).toBe(playerStartingHp[1]);
|
expect(playerPokemon[1].hp).toBe(playerPokemon[1].getMaxHp());
|
||||||
}, TIMEOUT
|
}, TIMEOUT
|
||||||
);
|
);
|
||||||
|
|
||||||
test(
|
test(
|
||||||
"move should redirect enemy attacks to the first ally that uses it",
|
"move should redirect enemy attacks to the first ally that uses it",
|
||||||
async () => {
|
async () => {
|
||||||
await game.startBattle([Species.AMOONGUSS, Species.CHARIZARD]);
|
await game.classicMode.startBattle([Species.AMOONGUSS, Species.CHARIZARD]);
|
||||||
|
|
||||||
const playerPokemon = game.scene.getPlayerField();
|
const playerPokemon = game.scene.getPlayerField();
|
||||||
|
|
||||||
const playerStartingHp = playerPokemon.map(p => p.hp);
|
|
||||||
|
|
||||||
game.move.select(Moves.FOLLOW_ME);
|
game.move.select(Moves.FOLLOW_ME);
|
||||||
game.move.select(Moves.FOLLOW_ME, 1);
|
game.move.select(Moves.FOLLOW_ME, 1);
|
||||||
|
|
||||||
|
// Each player is targeted by an enemy
|
||||||
|
await game.forceEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER);
|
||||||
|
await game.forceEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER_2);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(TurnEndPhase, false);
|
await game.phaseInterceptor.to(TurnEndPhase, false);
|
||||||
|
|
||||||
playerPokemon.sort((a, b) => a.getEffectiveStat(Stat.SPD) - b.getEffectiveStat(Stat.SPD));
|
playerPokemon.sort((a, b) => a.getEffectiveStat(Stat.SPD) - b.getEffectiveStat(Stat.SPD));
|
||||||
|
|
||||||
expect(playerPokemon[1].hp).toBeLessThan(playerStartingHp[1]);
|
expect(playerPokemon[1].hp).toBeLessThan(playerPokemon[1].getMaxHp());
|
||||||
expect(playerPokemon[0].hp).toBe(playerStartingHp[0]);
|
expect(playerPokemon[0].hp).toBe(playerPokemon[0].getMaxHp());
|
||||||
}, TIMEOUT
|
}, TIMEOUT
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -78,21 +85,23 @@ describe("Moves - Follow Me", () => {
|
||||||
async () => {
|
async () => {
|
||||||
game.override.ability(Abilities.STALWART);
|
game.override.ability(Abilities.STALWART);
|
||||||
game.override.moveset([Moves.QUICK_ATTACK]);
|
game.override.moveset([Moves.QUICK_ATTACK]);
|
||||||
game.override.enemyMoveset([Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME]);
|
|
||||||
|
|
||||||
await game.startBattle([Species.AMOONGUSS, Species.CHARIZARD]);
|
await game.classicMode.startBattle([Species.AMOONGUSS, Species.CHARIZARD]);
|
||||||
|
|
||||||
const enemyPokemon = game.scene.getEnemyField();
|
const enemyPokemon = game.scene.getEnemyField();
|
||||||
|
|
||||||
const enemyStartingHp = enemyPokemon.map(p => p.hp);
|
|
||||||
|
|
||||||
game.move.select(Moves.QUICK_ATTACK, 0, BattlerIndex.ENEMY);
|
game.move.select(Moves.QUICK_ATTACK, 0, BattlerIndex.ENEMY);
|
||||||
game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY_2);
|
game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY_2);
|
||||||
|
|
||||||
|
// Target doesn't need to be specified if the move is self-targeted
|
||||||
|
await game.forceEnemyMove(Moves.FOLLOW_ME);
|
||||||
|
await game.forceEnemyMove(Moves.SPLASH);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(TurnEndPhase, false);
|
await game.phaseInterceptor.to(TurnEndPhase, false);
|
||||||
|
|
||||||
// If redirection was bypassed, both enemies should be damaged
|
// If redirection was bypassed, both enemies should be damaged
|
||||||
expect(enemyPokemon[0].hp).toBeLessThan(enemyStartingHp[0]);
|
expect(enemyPokemon[0].hp).toBeLessThan(enemyPokemon[0].getMaxHp());
|
||||||
expect(enemyPokemon[1].hp).toBeLessThan(enemyStartingHp[1]);
|
expect(enemyPokemon[1].hp).toBeLessThan(enemyPokemon[1].getMaxHp());
|
||||||
}, TIMEOUT
|
}, TIMEOUT
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -100,21 +109,22 @@ describe("Moves - Follow Me", () => {
|
||||||
"move effect should be bypassed by Snipe Shot",
|
"move effect should be bypassed by Snipe Shot",
|
||||||
async () => {
|
async () => {
|
||||||
game.override.moveset([Moves.SNIPE_SHOT]);
|
game.override.moveset([Moves.SNIPE_SHOT]);
|
||||||
game.override.enemyMoveset([Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME]);
|
|
||||||
|
|
||||||
await game.startBattle([Species.AMOONGUSS, Species.CHARIZARD]);
|
await game.classicMode.startBattle([Species.AMOONGUSS, Species.CHARIZARD]);
|
||||||
|
|
||||||
const enemyPokemon = game.scene.getEnemyField();
|
const enemyPokemon = game.scene.getEnemyField();
|
||||||
|
|
||||||
const enemyStartingHp = enemyPokemon.map(p => p.hp);
|
|
||||||
|
|
||||||
game.move.select(Moves.SNIPE_SHOT, 0, BattlerIndex.ENEMY);
|
game.move.select(Moves.SNIPE_SHOT, 0, BattlerIndex.ENEMY);
|
||||||
game.move.select(Moves.SNIPE_SHOT, 1, BattlerIndex.ENEMY_2);
|
game.move.select(Moves.SNIPE_SHOT, 1, BattlerIndex.ENEMY_2);
|
||||||
|
|
||||||
|
await game.forceEnemyMove(Moves.FOLLOW_ME);
|
||||||
|
await game.forceEnemyMove(Moves.SPLASH);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(TurnEndPhase, false);
|
await game.phaseInterceptor.to(TurnEndPhase, false);
|
||||||
|
|
||||||
// If redirection was bypassed, both enemies should be damaged
|
// If redirection was bypassed, both enemies should be damaged
|
||||||
expect(enemyPokemon[0].hp).toBeLessThan(enemyStartingHp[0]);
|
expect(enemyPokemon[0].hp).toBeLessThan(enemyPokemon[0].getMaxHp());
|
||||||
expect(enemyPokemon[1].hp).toBeLessThan(enemyStartingHp[1]);
|
expect(enemyPokemon[1].hp).toBeLessThan(enemyPokemon[1].getMaxHp());
|
||||||
}, TIMEOUT
|
}, TIMEOUT
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#app/battle";
|
||||||
import { TurnEndPhase } from "#app/phases/turn-end-phase";
|
|
||||||
import { Abilities } from "#enums/abilities";
|
import { Abilities } from "#enums/abilities";
|
||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
|
@ -31,27 +30,27 @@ describe("Moves - Rage Powder", () => {
|
||||||
game.override.startingLevel(100);
|
game.override.startingLevel(100);
|
||||||
game.override.enemyLevel(100);
|
game.override.enemyLevel(100);
|
||||||
game.override.moveset([Moves.FOLLOW_ME, Moves.RAGE_POWDER, Moves.SPOTLIGHT, Moves.QUICK_ATTACK]);
|
game.override.moveset([Moves.FOLLOW_ME, Moves.RAGE_POWDER, Moves.SPOTLIGHT, Moves.QUICK_ATTACK]);
|
||||||
game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]);
|
game.override.enemyMoveset([Moves.RAGE_POWDER, Moves.TACKLE, Moves.SPLASH]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test(
|
test(
|
||||||
"move effect should be bypassed by Grass type",
|
"move effect should be bypassed by Grass type",
|
||||||
async () => {
|
async () => {
|
||||||
game.override.enemyMoveset([Moves.RAGE_POWDER, Moves.RAGE_POWDER, Moves.RAGE_POWDER, Moves.RAGE_POWDER]);
|
await game.classicMode.startBattle([Species.AMOONGUSS, Species.VENUSAUR]);
|
||||||
|
|
||||||
await game.startBattle([Species.AMOONGUSS, Species.VENUSAUR]);
|
|
||||||
|
|
||||||
const enemyPokemon = game.scene.getEnemyField();
|
const enemyPokemon = game.scene.getEnemyField();
|
||||||
|
|
||||||
const enemyStartingHp = enemyPokemon.map(p => p.hp);
|
|
||||||
|
|
||||||
game.move.select(Moves.QUICK_ATTACK, 0, BattlerIndex.ENEMY);
|
game.move.select(Moves.QUICK_ATTACK, 0, BattlerIndex.ENEMY);
|
||||||
game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY_2);
|
game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY_2);
|
||||||
await game.phaseInterceptor.to(TurnEndPhase, false);
|
|
||||||
|
await game.forceEnemyMove(Moves.RAGE_POWDER);
|
||||||
|
await game.forceEnemyMove(Moves.SPLASH);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to("BerryPhase", false);
|
||||||
|
|
||||||
// If redirection was bypassed, both enemies should be damaged
|
// If redirection was bypassed, both enemies should be damaged
|
||||||
expect(enemyPokemon[0].hp).toBeLessThan(enemyStartingHp[0]);
|
expect(enemyPokemon[0].hp).toBeLessThan(enemyPokemon[0].getMaxHp());
|
||||||
expect(enemyPokemon[1].hp).toBeLessThan(enemyStartingHp[1]);
|
expect(enemyPokemon[1].hp).toBeLessThan(enemyPokemon[0].getMaxHp());
|
||||||
}, TIMEOUT
|
}, TIMEOUT
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -59,10 +58,9 @@ describe("Moves - Rage Powder", () => {
|
||||||
"move effect should be bypassed by Overcoat",
|
"move effect should be bypassed by Overcoat",
|
||||||
async () => {
|
async () => {
|
||||||
game.override.ability(Abilities.OVERCOAT);
|
game.override.ability(Abilities.OVERCOAT);
|
||||||
game.override.enemyMoveset([Moves.RAGE_POWDER, Moves.RAGE_POWDER, Moves.RAGE_POWDER, Moves.RAGE_POWDER]);
|
|
||||||
|
|
||||||
// Test with two non-Grass type player Pokemon
|
// Test with two non-Grass type player Pokemon
|
||||||
await game.startBattle([Species.BLASTOISE, Species.CHARIZARD]);
|
await game.classicMode.startBattle([Species.BLASTOISE, Species.CHARIZARD]);
|
||||||
|
|
||||||
const enemyPokemon = game.scene.getEnemyField();
|
const enemyPokemon = game.scene.getEnemyField();
|
||||||
|
|
||||||
|
@ -70,7 +68,7 @@ describe("Moves - Rage Powder", () => {
|
||||||
|
|
||||||
game.move.select(Moves.QUICK_ATTACK, 0, BattlerIndex.ENEMY);
|
game.move.select(Moves.QUICK_ATTACK, 0, BattlerIndex.ENEMY);
|
||||||
game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY_2);
|
game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY_2);
|
||||||
await game.phaseInterceptor.to(TurnEndPhase, false);
|
await game.phaseInterceptor.to("BerryPhase", false);
|
||||||
|
|
||||||
// If redirection was bypassed, both enemies should be damaged
|
// If redirection was bypassed, both enemies should be damaged
|
||||||
expect(enemyPokemon[0].hp).toBeLessThan(enemyStartingHp[0]);
|
expect(enemyPokemon[0].hp).toBeLessThan(enemyStartingHp[0]);
|
||||||
|
|
|
@ -73,7 +73,7 @@ describe("Moves - Spikes", () => {
|
||||||
await game.toNextTurn();
|
await game.toNextTurn();
|
||||||
|
|
||||||
game.move.select(Moves.SPLASH);
|
game.move.select(Moves.SPLASH);
|
||||||
game.forceOpponentToSwitch();
|
game.forceEnemyToSwitch();
|
||||||
await game.toNextTurn();
|
await game.toNextTurn();
|
||||||
|
|
||||||
const enemy = game.scene.getEnemyParty()[0];
|
const enemy = game.scene.getEnemyParty()[0];
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#app/battle";
|
||||||
import { Stat } from "#enums/stat";
|
|
||||||
import { TurnEndPhase } from "#app/phases/turn-end-phase";
|
import { TurnEndPhase } from "#app/phases/turn-end-phase";
|
||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
|
@ -31,52 +30,46 @@ describe("Moves - Spotlight", () => {
|
||||||
game.override.startingLevel(100);
|
game.override.startingLevel(100);
|
||||||
game.override.enemyLevel(100);
|
game.override.enemyLevel(100);
|
||||||
game.override.moveset([Moves.FOLLOW_ME, Moves.RAGE_POWDER, Moves.SPOTLIGHT, Moves.QUICK_ATTACK]);
|
game.override.moveset([Moves.FOLLOW_ME, Moves.RAGE_POWDER, Moves.SPOTLIGHT, Moves.QUICK_ATTACK]);
|
||||||
game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]);
|
game.override.enemyMoveset([Moves.FOLLOW_ME, Moves.SPLASH]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test(
|
test(
|
||||||
"move should redirect attacks to the target",
|
"move should redirect attacks to the target",
|
||||||
async () => {
|
async () => {
|
||||||
await game.startBattle([Species.AMOONGUSS, Species.CHARIZARD]);
|
await game.classicMode.startBattle([Species.AMOONGUSS, Species.CHARIZARD]);
|
||||||
|
|
||||||
const enemyPokemon = game.scene.getEnemyField();
|
const enemyPokemon = game.scene.getEnemyField();
|
||||||
|
|
||||||
const enemyStartingHp = enemyPokemon.map(p => p.hp);
|
|
||||||
|
|
||||||
game.move.select(Moves.SPOTLIGHT, 0, BattlerIndex.ENEMY);
|
game.move.select(Moves.SPOTLIGHT, 0, BattlerIndex.ENEMY);
|
||||||
game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY_2);
|
game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY_2);
|
||||||
|
|
||||||
|
await game.forceEnemyMove(Moves.SPLASH);
|
||||||
|
await game.forceEnemyMove(Moves.SPLASH);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(TurnEndPhase, false);
|
await game.phaseInterceptor.to(TurnEndPhase, false);
|
||||||
|
|
||||||
expect(enemyPokemon[0].hp).toBeLessThan(enemyStartingHp[0]);
|
expect(enemyPokemon[0].hp).toBeLessThan(enemyPokemon[0].getMaxHp());
|
||||||
expect(enemyPokemon[1].hp).toBe(enemyStartingHp[1]);
|
expect(enemyPokemon[1].hp).toBe(enemyPokemon[1].getMaxHp());
|
||||||
}, TIMEOUT
|
}, TIMEOUT
|
||||||
);
|
);
|
||||||
|
|
||||||
test(
|
test(
|
||||||
"move should cause other redirection moves to fail",
|
"move should cause other redirection moves to fail",
|
||||||
async () => {
|
async () => {
|
||||||
game.override.enemyMoveset([Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME]);
|
await game.classicMode.startBattle([Species.AMOONGUSS, Species.CHARIZARD]);
|
||||||
|
|
||||||
await game.startBattle([Species.AMOONGUSS, Species.CHARIZARD]);
|
|
||||||
|
|
||||||
const enemyPokemon = game.scene.getEnemyField();
|
const enemyPokemon = game.scene.getEnemyField();
|
||||||
|
|
||||||
/**
|
game.move.select(Moves.SPOTLIGHT, 0, BattlerIndex.ENEMY);
|
||||||
* Spotlight will target the slower enemy. In this situation without Spotlight being used,
|
game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY_2);
|
||||||
* the faster enemy would normally end up with the Center of Attention tag.
|
|
||||||
*/
|
|
||||||
enemyPokemon.sort((a, b) => b.getEffectiveStat(Stat.SPD) - a.getEffectiveStat(Stat.SPD));
|
|
||||||
const spotTarget = enemyPokemon[1].getBattlerIndex();
|
|
||||||
const attackTarget = enemyPokemon[0].getBattlerIndex();
|
|
||||||
|
|
||||||
const enemyStartingHp = enemyPokemon.map(p => p.hp);
|
await game.forceEnemyMove(Moves.SPLASH);
|
||||||
|
await game.forceEnemyMove(Moves.FOLLOW_ME);
|
||||||
|
|
||||||
game.move.select(Moves.SPOTLIGHT, 0, spotTarget);
|
await game.phaseInterceptor.to("BerryPhase", false);
|
||||||
game.move.select(Moves.QUICK_ATTACK, 1, attackTarget);
|
|
||||||
await game.phaseInterceptor.to(TurnEndPhase, false);
|
|
||||||
|
|
||||||
expect(enemyPokemon[1].hp).toBeLessThan(enemyStartingHp[1]);
|
expect(enemyPokemon[0].hp).toBeLessThan(enemyPokemon[0].getMaxHp());
|
||||||
expect(enemyPokemon[0].hp).toBe(enemyStartingHp[0]);
|
expect(enemyPokemon[1].hp).toBe(enemyPokemon[1].getMaxHp());
|
||||||
}, TIMEOUT
|
}, TIMEOUT
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -53,9 +53,6 @@ describe("UI - Starter select", () => {
|
||||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||||
handler.processInput(Button.RIGHT);
|
handler.processInput(Button.RIGHT);
|
||||||
handler.processInput(Button.LEFT);
|
handler.processInput(Button.LEFT);
|
||||||
handler.processInput(Button.CYCLE_SHINY);
|
|
||||||
handler.processInput(Button.V);
|
|
||||||
handler.processInput(Button.V);
|
|
||||||
handler.processInput(Button.ACTION);
|
handler.processInput(Button.ACTION);
|
||||||
game.phaseInterceptor.unlock();
|
game.phaseInterceptor.unlock();
|
||||||
});
|
});
|
||||||
|
@ -117,9 +114,6 @@ describe("UI - Starter select", () => {
|
||||||
handler.processInput(Button.RIGHT);
|
handler.processInput(Button.RIGHT);
|
||||||
handler.processInput(Button.LEFT);
|
handler.processInput(Button.LEFT);
|
||||||
handler.processInput(Button.CYCLE_GENDER);
|
handler.processInput(Button.CYCLE_GENDER);
|
||||||
handler.processInput(Button.CYCLE_SHINY);
|
|
||||||
handler.processInput(Button.V);
|
|
||||||
handler.processInput(Button.V);
|
|
||||||
handler.processInput(Button.ACTION);
|
handler.processInput(Button.ACTION);
|
||||||
game.phaseInterceptor.unlock();
|
game.phaseInterceptor.unlock();
|
||||||
});
|
});
|
||||||
|
@ -184,9 +178,6 @@ describe("UI - Starter select", () => {
|
||||||
handler.processInput(Button.CYCLE_GENDER);
|
handler.processInput(Button.CYCLE_GENDER);
|
||||||
handler.processInput(Button.CYCLE_NATURE);
|
handler.processInput(Button.CYCLE_NATURE);
|
||||||
handler.processInput(Button.CYCLE_ABILITY);
|
handler.processInput(Button.CYCLE_ABILITY);
|
||||||
handler.processInput(Button.CYCLE_SHINY);
|
|
||||||
handler.processInput(Button.V);
|
|
||||||
handler.processInput(Button.V);
|
|
||||||
handler.processInput(Button.ACTION);
|
handler.processInput(Button.ACTION);
|
||||||
game.phaseInterceptor.unlock();
|
game.phaseInterceptor.unlock();
|
||||||
});
|
});
|
||||||
|
@ -227,11 +218,12 @@ describe("UI - Starter select", () => {
|
||||||
expect(game.scene.getParty()[0].species.speciesId).toBe(Species.BULBASAUR);
|
expect(game.scene.getParty()[0].species.speciesId).toBe(Species.BULBASAUR);
|
||||||
expect(game.scene.getParty()[0].shiny).toBe(true);
|
expect(game.scene.getParty()[0].shiny).toBe(true);
|
||||||
expect(game.scene.getParty()[0].variant).toBe(2);
|
expect(game.scene.getParty()[0].variant).toBe(2);
|
||||||
|
expect(game.scene.getParty()[0].gender).toBe(Gender.FEMALE);
|
||||||
expect(game.scene.getParty()[0].nature).toBe(Nature.LONELY);
|
expect(game.scene.getParty()[0].nature).toBe(Nature.LONELY);
|
||||||
expect(game.scene.getParty()[0].getAbility().id).toBe(Abilities.CHLOROPHYLL);
|
expect(game.scene.getParty()[0].getAbility().id).toBe(Abilities.CHLOROPHYLL);
|
||||||
}, 20000);
|
}, 20000);
|
||||||
|
|
||||||
it("Bulbasaur - shiny - variant 2 female lonely chlorophyl", async() => {
|
it("Bulbasaur - shiny - variant 2 female", async() => {
|
||||||
await game.importData("src/test/utils/saves/everything.prsv");
|
await game.importData("src/test/utils/saves/everything.prsv");
|
||||||
const caughtCount = Object.keys(game.scene.gameData.dexData).filter((key) => {
|
const caughtCount = Object.keys(game.scene.gameData.dexData).filter((key) => {
|
||||||
const species = game.scene.gameData.dexData[key];
|
const species = game.scene.gameData.dexData[key];
|
||||||
|
@ -249,9 +241,6 @@ describe("UI - Starter select", () => {
|
||||||
handler.processInput(Button.RIGHT);
|
handler.processInput(Button.RIGHT);
|
||||||
handler.processInput(Button.LEFT);
|
handler.processInput(Button.LEFT);
|
||||||
handler.processInput(Button.CYCLE_GENDER);
|
handler.processInput(Button.CYCLE_GENDER);
|
||||||
handler.processInput(Button.CYCLE_SHINY);
|
|
||||||
handler.processInput(Button.V);
|
|
||||||
handler.processInput(Button.V);
|
|
||||||
handler.processInput(Button.ACTION);
|
handler.processInput(Button.ACTION);
|
||||||
game.phaseInterceptor.unlock();
|
game.phaseInterceptor.unlock();
|
||||||
});
|
});
|
||||||
|
@ -313,6 +302,7 @@ describe("UI - Starter select", () => {
|
||||||
handler.processInput(Button.RIGHT);
|
handler.processInput(Button.RIGHT);
|
||||||
handler.processInput(Button.LEFT);
|
handler.processInput(Button.LEFT);
|
||||||
handler.processInput(Button.ACTION);
|
handler.processInput(Button.ACTION);
|
||||||
|
handler.processInput(Button.CYCLE_SHINY);
|
||||||
game.phaseInterceptor.unlock();
|
game.phaseInterceptor.unlock();
|
||||||
});
|
});
|
||||||
await game.phaseInterceptor.run(SelectStarterPhase);
|
await game.phaseInterceptor.run(SelectStarterPhase);
|
||||||
|
@ -371,7 +361,7 @@ describe("UI - Starter select", () => {
|
||||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||||
handler.processInput(Button.RIGHT);
|
handler.processInput(Button.RIGHT);
|
||||||
handler.processInput(Button.LEFT);
|
handler.processInput(Button.LEFT);
|
||||||
handler.processInput(Button.CYCLE_SHINY);
|
handler.processInput(Button.V);
|
||||||
handler.processInput(Button.V);
|
handler.processInput(Button.V);
|
||||||
handler.processInput(Button.ACTION);
|
handler.processInput(Button.ACTION);
|
||||||
game.phaseInterceptor.unlock();
|
game.phaseInterceptor.unlock();
|
||||||
|
@ -415,7 +405,7 @@ describe("UI - Starter select", () => {
|
||||||
expect(game.scene.getParty()[0].variant).toBe(1);
|
expect(game.scene.getParty()[0].variant).toBe(1);
|
||||||
}, 20000);
|
}, 20000);
|
||||||
|
|
||||||
it("Bulbasaur - shiny - variant 2", async() => {
|
it("Bulbasaur - shiny - variant 0", async() => {
|
||||||
await game.importData("src/test/utils/saves/everything.prsv");
|
await game.importData("src/test/utils/saves/everything.prsv");
|
||||||
const caughtCount = Object.keys(game.scene.gameData.dexData).filter((key) => {
|
const caughtCount = Object.keys(game.scene.gameData.dexData).filter((key) => {
|
||||||
const species = game.scene.gameData.dexData[key];
|
const species = game.scene.gameData.dexData[key];
|
||||||
|
@ -432,8 +422,6 @@ describe("UI - Starter select", () => {
|
||||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||||
handler.processInput(Button.RIGHT);
|
handler.processInput(Button.RIGHT);
|
||||||
handler.processInput(Button.LEFT);
|
handler.processInput(Button.LEFT);
|
||||||
handler.processInput(Button.CYCLE_SHINY);
|
|
||||||
handler.processInput(Button.V);
|
|
||||||
handler.processInput(Button.V);
|
handler.processInput(Button.V);
|
||||||
handler.processInput(Button.ACTION);
|
handler.processInput(Button.ACTION);
|
||||||
game.phaseInterceptor.unlock();
|
game.phaseInterceptor.unlock();
|
||||||
|
@ -474,7 +462,7 @@ describe("UI - Starter select", () => {
|
||||||
|
|
||||||
expect(game.scene.getParty()[0].species.speciesId).toBe(Species.BULBASAUR);
|
expect(game.scene.getParty()[0].species.speciesId).toBe(Species.BULBASAUR);
|
||||||
expect(game.scene.getParty()[0].shiny).toBe(true);
|
expect(game.scene.getParty()[0].shiny).toBe(true);
|
||||||
expect(game.scene.getParty()[0].variant).toBe(2);
|
expect(game.scene.getParty()[0].variant).toBe(0);
|
||||||
}, 20000);
|
}, 20000);
|
||||||
|
|
||||||
it("Check if first pokemon in party is caterpie from gen 1 and 1rd row, 3rd column", async() => {
|
it("Check if first pokemon in party is caterpie from gen 1 and 1rd row, 3rd column", async() => {
|
||||||
|
|
|
@ -2,6 +2,8 @@ import { updateUserInfo } from "#app/account";
|
||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#app/battle";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import { BattleStyle } from "#app/enums/battle-style";
|
import { BattleStyle } from "#app/enums/battle-style";
|
||||||
|
import { Moves } from "#app/enums/moves";
|
||||||
|
import { getMoveTargets } from "#app/data/move";
|
||||||
import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon";
|
import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon";
|
||||||
import Trainer from "#app/field/trainer";
|
import Trainer from "#app/field/trainer";
|
||||||
import { GameModes, getGameMode } from "#app/game-mode";
|
import { GameModes, getGameMode } from "#app/game-mode";
|
||||||
|
@ -9,6 +11,7 @@ import { ModifierTypeOption, modifierTypes } from "#app/modifier/modifier-type";
|
||||||
import overrides from "#app/overrides";
|
import overrides from "#app/overrides";
|
||||||
import { CommandPhase } from "#app/phases/command-phase";
|
import { CommandPhase } from "#app/phases/command-phase";
|
||||||
import { EncounterPhase } from "#app/phases/encounter-phase";
|
import { EncounterPhase } from "#app/phases/encounter-phase";
|
||||||
|
import { EnemyCommandPhase } from "#app/phases/enemy-command-phase";
|
||||||
import { FaintPhase } from "#app/phases/faint-phase";
|
import { FaintPhase } from "#app/phases/faint-phase";
|
||||||
import { LoginPhase } from "#app/phases/login-phase";
|
import { LoginPhase } from "#app/phases/login-phase";
|
||||||
import { MovePhase } from "#app/phases/move-phase";
|
import { MovePhase } from "#app/phases/move-phase";
|
||||||
|
@ -243,7 +246,34 @@ export default class GameManager {
|
||||||
}, () => this.isCurrentPhase(CommandPhase) || this.isCurrentPhase(NewBattlePhase) || this.isCurrentPhase(CheckSwitchPhase));
|
}, () => this.isCurrentPhase(CommandPhase) || this.isCurrentPhase(NewBattlePhase) || this.isCurrentPhase(CheckSwitchPhase));
|
||||||
}
|
}
|
||||||
|
|
||||||
forceOpponentToSwitch() {
|
/**
|
||||||
|
* Forces the next enemy selecting a move to use the given move in its moveset against the
|
||||||
|
* given target (if applicable).
|
||||||
|
* @param moveId {@linkcode Moves} the move the enemy will use
|
||||||
|
* @param target {@linkcode BattlerIndex} the target on which the enemy will use the given move
|
||||||
|
*/
|
||||||
|
async forceEnemyMove(moveId: Moves, target?: BattlerIndex) {
|
||||||
|
// Wait for the next EnemyCommandPhase to start
|
||||||
|
await this.phaseInterceptor.to(EnemyCommandPhase, false);
|
||||||
|
const enemy = this.scene.getEnemyField()[(this.scene.getCurrentPhase() as EnemyCommandPhase).getFieldIndex()];
|
||||||
|
const legalTargets = getMoveTargets(enemy, moveId);
|
||||||
|
|
||||||
|
vi.spyOn(enemy, "getNextMove").mockReturnValueOnce({
|
||||||
|
move: moveId,
|
||||||
|
targets: (target && !legalTargets.multiple && legalTargets.targets.includes(target))
|
||||||
|
? [target]
|
||||||
|
: enemy.getNextTargets(moveId)
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the EnemyCommandPhase to completion.
|
||||||
|
* This allows this function to be called consecutively to
|
||||||
|
* force a move for each enemy in a double battle.
|
||||||
|
*/
|
||||||
|
await this.phaseInterceptor.to(EnemyCommandPhase);
|
||||||
|
}
|
||||||
|
|
||||||
|
forceEnemyToSwitch() {
|
||||||
const originalMatchupScore = Trainer.prototype.getPartyMemberMatchupScores;
|
const originalMatchupScore = Trainer.prototype.getPartyMemberMatchupScores;
|
||||||
Trainer.prototype.getPartyMemberMatchupScores = () => {
|
Trainer.prototype.getPartyMemberMatchupScores = () => {
|
||||||
Trainer.prototype.getPartyMemberMatchupScores = originalMatchupScore;
|
Trainer.prototype.getPartyMemberMatchupScores = originalMatchupScore;
|
||||||
|
|
|
@ -215,12 +215,11 @@ export default class BattleMessageUiHandler extends MessageUiHandler {
|
||||||
getTopIvs(ivs: integer[], shownIvsCount: integer): Stat[] {
|
getTopIvs(ivs: integer[], shownIvsCount: integer): Stat[] {
|
||||||
let shownStats: Stat[] = [];
|
let shownStats: Stat[] = [];
|
||||||
if (shownIvsCount < 6) {
|
if (shownIvsCount < 6) {
|
||||||
let highestIv = -1;
|
const statsPool = PERMANENT_STATS.slice();
|
||||||
|
// Sort the stats from highest to lowest iv
|
||||||
|
statsPool.sort((s1, s2) => ivs[s2] - ivs[s1]);
|
||||||
for (let i = 0; i < shownIvsCount; i++) {
|
for (let i = 0; i < shownIvsCount; i++) {
|
||||||
if (ivs[i] > highestIv) {
|
shownStats.push(statsPool[i]);
|
||||||
shownStats.push(PERMANENT_STATS[i]);
|
|
||||||
highestIv = ivs[i];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
shownStats = PERMANENT_STATS.slice();
|
shownStats = PERMANENT_STATS.slice();
|
||||||
|
|
|
@ -0,0 +1,320 @@
|
||||||
|
import BattleScene from "../battle-scene";
|
||||||
|
import { Mode } from "./ui";
|
||||||
|
import PokemonIconAnimHandler, { PokemonIconAnimMode } from "./pokemon-icon-anim-handler";
|
||||||
|
import MessageUiHandler from "./message-ui-handler";
|
||||||
|
import { getEggTierForSpecies } from "../data/egg";
|
||||||
|
import {Button} from "#enums/buttons";
|
||||||
|
import { Gender } from "#app/data/gender";
|
||||||
|
import { getVariantTint } from "#app/data/variant";
|
||||||
|
import { EggTier } from "#app/enums/egg-type";
|
||||||
|
import PokemonHatchInfoContainer from "./pokemon-hatch-info-container";
|
||||||
|
import { EggSummaryPhase } from "#app/phases/egg-summary-phase";
|
||||||
|
import { DexAttr } from "#app/system/game-data";
|
||||||
|
import { EggHatchData } from "#app/data/egg-hatch-data";
|
||||||
|
|
||||||
|
const iconContainerX = 115;
|
||||||
|
const iconContainerY = 9;
|
||||||
|
const numCols = 11;
|
||||||
|
const iconSize = 18;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UI Handler for the egg summary.
|
||||||
|
* Handles navigation and display of each pokemon as a list
|
||||||
|
* Also handles display of the pokemon-hatch-info-container
|
||||||
|
*/
|
||||||
|
export default class EggSummaryUiHandler extends MessageUiHandler {
|
||||||
|
/** holds all elements in the scene */
|
||||||
|
private eggHatchContainer: Phaser.GameObjects.Container;
|
||||||
|
/** holds the icon containers and info container */
|
||||||
|
private summaryContainer: Phaser.GameObjects.Container;
|
||||||
|
/** container for the mini pokemon sprites */
|
||||||
|
private pokemonIconSpritesContainer: Phaser.GameObjects.Container;
|
||||||
|
/** container for the icons displayed alongside the mini icons (e.g. shiny, HA capsule) */
|
||||||
|
private pokemonIconsContainer: Phaser.GameObjects.Container;
|
||||||
|
/** hatch info container that displays the current pokemon / hatch (main element on left hand side) */
|
||||||
|
private infoContainer: PokemonHatchInfoContainer;
|
||||||
|
/** handles jumping animations for the pokemon sprite icons */
|
||||||
|
private iconAnimHandler: PokemonIconAnimHandler;
|
||||||
|
private eggHatchBg: Phaser.GameObjects.Image;
|
||||||
|
private cursorObj: Phaser.GameObjects.Image;
|
||||||
|
private eggHatchData: EggHatchData[];
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows subscribers to listen for events
|
||||||
|
*
|
||||||
|
* Current Events:
|
||||||
|
* - {@linkcode EggEventType.EGG_COUNT_CHANGED} {@linkcode EggCountChangedEvent}
|
||||||
|
*/
|
||||||
|
public readonly eventTarget: EventTarget = new EventTarget();
|
||||||
|
|
||||||
|
constructor(scene: BattleScene) {
|
||||||
|
super(scene, Mode.EGG_HATCH_SUMMARY);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
const ui = this.getUi();
|
||||||
|
|
||||||
|
this.summaryContainer = this.scene.add.container(0, -this.scene.game.canvas.height / 6);
|
||||||
|
this.summaryContainer.setVisible(false);
|
||||||
|
ui.add(this.summaryContainer);
|
||||||
|
|
||||||
|
this.eggHatchContainer = this.scene.add.container(0, -this.scene.game.canvas.height / 6);
|
||||||
|
this.eggHatchContainer.setVisible(false);
|
||||||
|
ui.add(this.eggHatchContainer);
|
||||||
|
|
||||||
|
this.iconAnimHandler = new PokemonIconAnimHandler();
|
||||||
|
this.iconAnimHandler.setup(this.scene);
|
||||||
|
|
||||||
|
this.eggHatchBg = this.scene.add.image(0, 0, "egg_summary_bg");
|
||||||
|
this.eggHatchBg.setOrigin(0, 0);
|
||||||
|
this.eggHatchContainer.add(this.eggHatchBg);
|
||||||
|
|
||||||
|
this.pokemonIconsContainer = this.scene.add.container(iconContainerX, iconContainerY);
|
||||||
|
this.pokemonIconSpritesContainer = this.scene.add.container(iconContainerX, iconContainerY);
|
||||||
|
this.summaryContainer.add(this.pokemonIconsContainer);
|
||||||
|
this.summaryContainer.add(this.pokemonIconSpritesContainer);
|
||||||
|
|
||||||
|
this.cursorObj = this.scene.add.image(0, 0, "select_cursor");
|
||||||
|
this.cursorObj.setOrigin(0, 0);
|
||||||
|
this.summaryContainer.add(this.cursorObj);
|
||||||
|
|
||||||
|
this.infoContainer = new PokemonHatchInfoContainer(this.scene, this.summaryContainer);
|
||||||
|
this.infoContainer.setup();
|
||||||
|
this.infoContainer.changeToEggSummaryLayout();
|
||||||
|
this.infoContainer.setVisible(true);
|
||||||
|
this.summaryContainer.add(this.infoContainer);
|
||||||
|
|
||||||
|
this.cursor = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
super.clear();
|
||||||
|
this.cursor = -1;
|
||||||
|
this.summaryContainer.setVisible(false);
|
||||||
|
this.pokemonIconSpritesContainer.removeAll(true);
|
||||||
|
this.pokemonIconsContainer.removeAll(true);
|
||||||
|
this.eggHatchBg.setVisible(false);
|
||||||
|
this.getUi().hideTooltip();
|
||||||
|
// Note: Questions on garbage collection go to @frutescens
|
||||||
|
const activeKeys = this.scene.getActiveKeys();
|
||||||
|
// Removing unnecessary sprites from animation manager
|
||||||
|
const animKeys = Object.keys(this.scene.anims["anims"]["entries"]);
|
||||||
|
animKeys.forEach(key => {
|
||||||
|
if (key.startsWith("pkmn__") && !activeKeys.includes(key)) {
|
||||||
|
this.scene.anims.remove(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Removing unnecessary cries from audio cache
|
||||||
|
const audioKeys = Object.keys(this.scene.cache.audio.entries.entries);
|
||||||
|
audioKeys.forEach(key => {
|
||||||
|
if (key.startsWith("cry/") && !activeKeys.includes(key)) {
|
||||||
|
delete this.scene.cache.audio.entries.entries[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Clears eggHatchData in EggSummaryUiHandler
|
||||||
|
this.eggHatchData.length = 0;
|
||||||
|
// Removes Pokemon icons in EggSummaryUiHandler
|
||||||
|
this.iconAnimHandler.removeAll();
|
||||||
|
console.log("Egg Summary Handler cleared");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param args EggHatchData[][]
|
||||||
|
* args[0]: list of EggHatchData for each egg/pokemon hatched
|
||||||
|
*/
|
||||||
|
show(args: EggHatchData[][]): boolean {
|
||||||
|
super.show(args);
|
||||||
|
if (args.length >= 1) {
|
||||||
|
// sort the egg hatch data by egg tier then by species number (then by order hatched)
|
||||||
|
this.eggHatchData = args[0].sort(function sortHatchData(a: EggHatchData, b: EggHatchData) {
|
||||||
|
const speciesA = a.pokemon.species;
|
||||||
|
const speciesB = b.pokemon.species;
|
||||||
|
if (getEggTierForSpecies(speciesA) < getEggTierForSpecies(speciesB)) {
|
||||||
|
return -1;
|
||||||
|
} else if (getEggTierForSpecies(speciesA) > getEggTierForSpecies(speciesB)) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
if (speciesA.speciesId < speciesB.speciesId) {
|
||||||
|
return -1;
|
||||||
|
} else if (speciesA.speciesId > speciesB.speciesId) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getUi().bringToTop(this.summaryContainer);
|
||||||
|
this.summaryContainer.setVisible(true);
|
||||||
|
this.eggHatchContainer.setVisible(true);
|
||||||
|
this.pokemonIconsContainer.setVisible(true);
|
||||||
|
this.eggHatchBg.setVisible(true);
|
||||||
|
this.infoContainer.hideDisplayPokemon();
|
||||||
|
|
||||||
|
this.eggHatchData.forEach( (value: EggHatchData, i: number) => {
|
||||||
|
const x = (i % numCols) * iconSize;
|
||||||
|
const y = Math.floor(i / numCols) * iconSize;
|
||||||
|
|
||||||
|
const displayPokemon = value.pokemon;
|
||||||
|
const offset = 2;
|
||||||
|
const rightSideX = 12;
|
||||||
|
|
||||||
|
const bg = this.scene.add.image(x+2, y+5, "passive_bg");
|
||||||
|
bg.setOrigin(0, 0);
|
||||||
|
bg.setScale(0.75);
|
||||||
|
bg.setVisible(true);
|
||||||
|
this.pokemonIconsContainer.add(bg);
|
||||||
|
|
||||||
|
// set tint for passive bg
|
||||||
|
switch (getEggTierForSpecies(displayPokemon.species)) {
|
||||||
|
case EggTier.COMMON:
|
||||||
|
bg.setVisible(false);
|
||||||
|
break;
|
||||||
|
case EggTier.GREAT:
|
||||||
|
bg.setTint(0xabafff);
|
||||||
|
break;
|
||||||
|
case EggTier.ULTRA:
|
||||||
|
bg.setTint(0xffffaa);
|
||||||
|
break;
|
||||||
|
case EggTier.MASTER:
|
||||||
|
bg.setTint(0xdfffaf);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const species = displayPokemon.species;
|
||||||
|
const female = displayPokemon.gender === Gender.FEMALE;
|
||||||
|
const formIndex = displayPokemon.formIndex;
|
||||||
|
const variant = displayPokemon.variant;
|
||||||
|
const isShiny = displayPokemon.shiny;
|
||||||
|
|
||||||
|
// set pokemon icon (and replace with base sprite if there is a mismatch)
|
||||||
|
const icon = this.scene.add.sprite(x - offset, y + offset, species.getIconAtlasKey(formIndex, isShiny, variant));
|
||||||
|
icon.setScale(0.5);
|
||||||
|
icon.setOrigin(0, 0);
|
||||||
|
icon.setFrame(species.getIconId(female, formIndex, isShiny, variant));
|
||||||
|
|
||||||
|
if (icon.frame.name !== species.getIconId(female, formIndex, isShiny, variant)) {
|
||||||
|
console.log(`${species.name}'s variant icon does not exist. Replacing with default.`);
|
||||||
|
icon.setTexture(species.getIconAtlasKey(formIndex, false, variant));
|
||||||
|
icon.setFrame(species.getIconId(female, formIndex, false, variant));
|
||||||
|
}
|
||||||
|
this.pokemonIconSpritesContainer.add(icon);
|
||||||
|
this.iconAnimHandler.addOrUpdate(icon, PokemonIconAnimMode.NONE);
|
||||||
|
|
||||||
|
const shiny = this.scene.add.image(x + rightSideX, y + offset * 2, "shiny_star_small");
|
||||||
|
shiny.setScale(0.5);
|
||||||
|
shiny.setVisible(displayPokemon.shiny);
|
||||||
|
shiny.setTint(getVariantTint(displayPokemon.variant));
|
||||||
|
this.pokemonIconsContainer.add(shiny);
|
||||||
|
|
||||||
|
const ha = this.scene.add.image(x + rightSideX, y + 7, "ha_capsule");
|
||||||
|
ha.setScale(0.5);
|
||||||
|
ha.setVisible((displayPokemon.hasAbility(displayPokemon.species.abilityHidden)));
|
||||||
|
this.pokemonIconsContainer.add(ha);
|
||||||
|
|
||||||
|
const pb = this.scene.add.image(x + rightSideX, y + offset * 7, "icon_owned");
|
||||||
|
pb.setOrigin(0, 0);
|
||||||
|
pb.setScale(0.5);
|
||||||
|
|
||||||
|
// add animation for new unlocks (new catch or new shiny or new form)
|
||||||
|
const dexEntry = value.dexEntryBeforeUpdate;
|
||||||
|
const caughtAttr = dexEntry.caughtAttr;
|
||||||
|
const newShiny = BigInt(1 << (displayPokemon.shiny ? 1 : 0));
|
||||||
|
const newVariant = BigInt(1 << (displayPokemon.variant + 4));
|
||||||
|
const newShinyOrVariant = ((newShiny & caughtAttr) === BigInt(0)) || ((newVariant & caughtAttr) === BigInt(0));
|
||||||
|
const newForm = (BigInt(1 << displayPokemon.formIndex) * DexAttr.DEFAULT_FORM & caughtAttr) === BigInt(0);
|
||||||
|
|
||||||
|
pb.setVisible(!caughtAttr || newForm);
|
||||||
|
if (!caughtAttr || newShinyOrVariant || newForm) {
|
||||||
|
this.iconAnimHandler.addOrUpdate(icon, PokemonIconAnimMode.PASSIVE);
|
||||||
|
}
|
||||||
|
this.pokemonIconsContainer.add(pb);
|
||||||
|
|
||||||
|
const em = this.scene.add.image(x, y + offset, "icon_egg_move");
|
||||||
|
em.setOrigin(0, 0);
|
||||||
|
em.setScale(0.5);
|
||||||
|
em.setVisible(value.eggMoveUnlocked);
|
||||||
|
this.pokemonIconsContainer.add(em);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.setCursor(0);
|
||||||
|
this.scene.playSoundWithoutBgm("evolution_fanfare");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
processInput(button: Button): boolean {
|
||||||
|
const ui = this.getUi();
|
||||||
|
|
||||||
|
let success = false;
|
||||||
|
const error = false;
|
||||||
|
if (button === Button.CANCEL) {
|
||||||
|
const phase = this.scene.getCurrentPhase();
|
||||||
|
if (phase instanceof EggSummaryPhase) {
|
||||||
|
phase.end();
|
||||||
|
}
|
||||||
|
ui.revertMode();
|
||||||
|
success = true;
|
||||||
|
} else {
|
||||||
|
const count = this.eggHatchData.length;
|
||||||
|
const rows = Math.ceil(count / numCols);
|
||||||
|
const row = Math.floor(this.cursor / numCols);
|
||||||
|
switch (button) {
|
||||||
|
case Button.UP:
|
||||||
|
if (row) {
|
||||||
|
success = this.setCursor(this.cursor - numCols);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Button.DOWN:
|
||||||
|
if (row < rows - 2 || (row < rows - 1 && this.cursor % numCols <= (count - 1) % numCols)) {
|
||||||
|
success = this.setCursor(this.cursor + numCols);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Button.LEFT:
|
||||||
|
if (this.cursor % numCols) {
|
||||||
|
success = this.setCursor(this.cursor - 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Button.RIGHT:
|
||||||
|
if (this.cursor % numCols < (row < rows - 1 ? 10 : (count - 1) % numCols)) {
|
||||||
|
success = this.setCursor(this.cursor + 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
ui.playSelect();
|
||||||
|
} else if (error) {
|
||||||
|
ui.playError();
|
||||||
|
}
|
||||||
|
|
||||||
|
return success || error;
|
||||||
|
}
|
||||||
|
|
||||||
|
setCursor(cursor: number): boolean {
|
||||||
|
let changed = false;
|
||||||
|
|
||||||
|
const lastCursor = this.cursor;
|
||||||
|
|
||||||
|
changed = super.setCursor(cursor);
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
this.cursorObj.setPosition(iconContainerX - 1 + iconSize * (cursor % numCols), iconContainerY + 1 + iconSize * Math.floor(cursor / numCols));
|
||||||
|
|
||||||
|
if (lastCursor > -1) {
|
||||||
|
this.iconAnimHandler.addOrUpdate(this.pokemonIconSpritesContainer.getAt(lastCursor) as Phaser.GameObjects.Sprite, PokemonIconAnimMode.NONE);
|
||||||
|
}
|
||||||
|
this.iconAnimHandler.addOrUpdate(this.pokemonIconSpritesContainer.getAt(cursor) as Phaser.GameObjects.Sprite, PokemonIconAnimMode.ACTIVE);
|
||||||
|
|
||||||
|
this.infoContainer.showHatchInfo(this.eggHatchData[cursor]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,189 @@
|
||||||
|
import PokemonInfoContainer from "./pokemon-info-container";
|
||||||
|
import BattleScene from "../battle-scene";
|
||||||
|
import { Gender } from "../data/gender";
|
||||||
|
import { Type } from "../data/type";
|
||||||
|
import * as Utils from "../utils";
|
||||||
|
import { TextStyle, addTextObject } from "./text";
|
||||||
|
import { speciesEggMoves } from "#app/data/egg-moves";
|
||||||
|
import { allMoves } from "#app/data/move";
|
||||||
|
import { Species } from "#app/enums/species";
|
||||||
|
import { getEggTierForSpecies } from "#app/data/egg";
|
||||||
|
import { starterColors } from "../battle-scene";
|
||||||
|
import { argbFromRgba } from "@material/material-color-utilities";
|
||||||
|
import { EggHatchData } from "#app/data/egg-hatch-data";
|
||||||
|
import { PlayerPokemon } from "#app/field/pokemon";
|
||||||
|
import { getPokemonSpeciesForm } from "#app/data/pokemon-species";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class for the hatch info summary of each pokemon
|
||||||
|
* Holds an info container as well as an additional egg sprite, name, egg moves and main sprite
|
||||||
|
*/
|
||||||
|
export default class PokemonHatchInfoContainer extends PokemonInfoContainer {
|
||||||
|
private currentPokemonSprite: Phaser.GameObjects.Sprite;
|
||||||
|
private pokemonNumberText: Phaser.GameObjects.Text;
|
||||||
|
private pokemonNameText: Phaser.GameObjects.Text;
|
||||||
|
private pokemonEggMovesContainer: Phaser.GameObjects.Container;
|
||||||
|
private pokemonEggMoveContainers: Phaser.GameObjects.Container[];
|
||||||
|
private pokemonEggMoveBgs: Phaser.GameObjects.NineSlice[];
|
||||||
|
private pokemonEggMoveLabels: Phaser.GameObjects.Text[];
|
||||||
|
private pokemonHatchedIcon : Phaser.GameObjects.Sprite;
|
||||||
|
private pokemonListContainer: Phaser.GameObjects.Container;
|
||||||
|
private pokemonCandyIcon: Phaser.GameObjects.Sprite;
|
||||||
|
private pokemonCandyOverlayIcon: Phaser.GameObjects.Sprite;
|
||||||
|
private pokemonCandyCountText: Phaser.GameObjects.Text;
|
||||||
|
|
||||||
|
constructor(scene: BattleScene, listContainer : Phaser.GameObjects.Container, x: number = 115, y: number = 9,) {
|
||||||
|
super(scene, x, y);
|
||||||
|
this.pokemonListContainer = listContainer;
|
||||||
|
|
||||||
|
}
|
||||||
|
setup(): void {
|
||||||
|
super.setup();
|
||||||
|
super.changeToEggSummaryLayout();
|
||||||
|
|
||||||
|
this.currentPokemonSprite = this.scene.add.sprite(54, 80, "pkmn__sub");
|
||||||
|
this.currentPokemonSprite.setScale(0.8);
|
||||||
|
this.currentPokemonSprite.setPipeline(this.scene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], ignoreTimeTint: true });
|
||||||
|
this.pokemonListContainer.add(this.currentPokemonSprite);
|
||||||
|
|
||||||
|
// setup name and number
|
||||||
|
this.pokemonNumberText = addTextObject(this.scene, 80, 107.5, "0000", TextStyle.SUMMARY, {fontSize: 74});
|
||||||
|
this.pokemonNumberText.setOrigin(0, 0);
|
||||||
|
this.pokemonListContainer.add(this.pokemonNumberText);
|
||||||
|
|
||||||
|
this.pokemonNameText = addTextObject(this.scene, 7, 107.5, "", TextStyle.SUMMARY, {fontSize: 74});
|
||||||
|
this.pokemonNameText.setOrigin(0, 0);
|
||||||
|
this.pokemonListContainer.add(this.pokemonNameText);
|
||||||
|
|
||||||
|
// setup egg icon and candy count
|
||||||
|
this.pokemonHatchedIcon = this.scene.add.sprite(-5, 90, "egg_icons");
|
||||||
|
this.pokemonHatchedIcon.setOrigin(0, 0.2);
|
||||||
|
this.pokemonHatchedIcon.setScale(0.8);
|
||||||
|
this.pokemonListContainer.add(this.pokemonHatchedIcon);
|
||||||
|
|
||||||
|
this.pokemonCandyIcon = this.scene.add.sprite(4.5, 40, "candy");
|
||||||
|
this.pokemonCandyIcon.setScale(0.5);
|
||||||
|
this.pokemonCandyIcon.setOrigin(0, 0);
|
||||||
|
this.pokemonListContainer.add(this.pokemonCandyIcon);
|
||||||
|
|
||||||
|
this.pokemonCandyOverlayIcon = this.scene.add.sprite(4.5, 40, "candy_overlay");
|
||||||
|
this.pokemonCandyOverlayIcon.setScale(0.5);
|
||||||
|
this.pokemonCandyOverlayIcon.setOrigin(0, 0);
|
||||||
|
this.pokemonListContainer.add(this.pokemonCandyOverlayIcon);
|
||||||
|
|
||||||
|
this.pokemonCandyCountText = addTextObject(this.scene, 14, 40, "x0", TextStyle.SUMMARY, { fontSize: "56px" });
|
||||||
|
this.pokemonCandyCountText.setOrigin(0, 0);
|
||||||
|
this.pokemonListContainer.add(this.pokemonCandyCountText);
|
||||||
|
|
||||||
|
// setup egg moves
|
||||||
|
this.pokemonEggMoveContainers = [];
|
||||||
|
this.pokemonEggMoveBgs = [];
|
||||||
|
this.pokemonEggMoveLabels = [];
|
||||||
|
this.pokemonEggMovesContainer = this.scene.add.container(0, 200);
|
||||||
|
this.pokemonEggMovesContainer.setVisible(false);
|
||||||
|
this.pokemonEggMovesContainer.setScale(0.5);
|
||||||
|
|
||||||
|
for (let m = 0; m < 4; m++) {
|
||||||
|
const eggMoveContainer = this.scene.add.container(0, 0 + 6 * m);
|
||||||
|
|
||||||
|
const eggMoveBg = this.scene.add.nineslice(70, 0, "type_bgs", "unknown", 92, 14, 2, 2, 2, 2);
|
||||||
|
eggMoveBg.setOrigin(1, 0);
|
||||||
|
|
||||||
|
const eggMoveLabel = addTextObject(this.scene, 70 -eggMoveBg.width / 2, 0, "???", TextStyle.PARTY);
|
||||||
|
eggMoveLabel.setOrigin(0.5, 0);
|
||||||
|
|
||||||
|
this.pokemonEggMoveBgs.push(eggMoveBg);
|
||||||
|
this.pokemonEggMoveLabels.push(eggMoveLabel);
|
||||||
|
|
||||||
|
eggMoveContainer.add(eggMoveBg);
|
||||||
|
eggMoveContainer.add(eggMoveLabel);
|
||||||
|
eggMoveContainer.setScale(0.44);
|
||||||
|
|
||||||
|
this.pokemonEggMoveContainers.push(eggMoveContainer);
|
||||||
|
|
||||||
|
this.pokemonEggMovesContainer.add(eggMoveContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
super.add(this.pokemonEggMoveContainers);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable the sprite (and replace with substitute)
|
||||||
|
*/
|
||||||
|
hideDisplayPokemon() {
|
||||||
|
this.currentPokemonSprite.setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display a given pokemon sprite with animations
|
||||||
|
* assumes the specific pokemon sprite has already been loaded
|
||||||
|
*/
|
||||||
|
displayPokemon(pokemon: PlayerPokemon) {
|
||||||
|
const species = pokemon.species;
|
||||||
|
const female = pokemon.gender === Gender.FEMALE;
|
||||||
|
const formIndex = pokemon.formIndex;
|
||||||
|
const shiny = pokemon.shiny;
|
||||||
|
const variant = pokemon.variant;
|
||||||
|
this.currentPokemonSprite.setVisible(false);
|
||||||
|
species.loadAssets(this.scene, female, formIndex, shiny, variant, true).then(() => {
|
||||||
|
|
||||||
|
getPokemonSpeciesForm(species.speciesId, pokemon.formIndex).cry(this.scene);
|
||||||
|
this.currentPokemonSprite.play(species.getSpriteKey(female, formIndex, shiny, variant));
|
||||||
|
this.currentPokemonSprite.setPipelineData("shiny", shiny);
|
||||||
|
this.currentPokemonSprite.setPipelineData("variant", variant);
|
||||||
|
this.currentPokemonSprite.setPipelineData("spriteKey", species.getSpriteKey(female, formIndex, shiny, variant));
|
||||||
|
this.currentPokemonSprite.setVisible(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the info container with the appropriate dex data and starter entry from the hatchInfo
|
||||||
|
* Also updates the displayed name, number, egg moves and main animated sprite for the pokemon
|
||||||
|
* @param hatchInfo The EggHatchData of the pokemon / new hatch to show
|
||||||
|
*/
|
||||||
|
showHatchInfo(hatchInfo: EggHatchData) {
|
||||||
|
this.pokemonEggMovesContainer.setVisible(true);
|
||||||
|
|
||||||
|
const pokemon = hatchInfo.pokemon;
|
||||||
|
const species = pokemon.species;
|
||||||
|
this.displayPokemon(pokemon);
|
||||||
|
|
||||||
|
super.show(pokemon, false, 1, hatchInfo.getDex(), hatchInfo.getStarterEntry(), true);
|
||||||
|
const colorScheme = starterColors[species.speciesId];
|
||||||
|
|
||||||
|
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.pokemonNumberText.setText(Utils.padInt(species.speciesId, 4));
|
||||||
|
this.pokemonNameText.setText(species.name);
|
||||||
|
|
||||||
|
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.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 : "???");
|
||||||
|
if (!(eggMove && hatchInfo.starterDataEntryBeforeUpdate.eggMoves & Math.pow(2, em)) && eggMoveUnlocked) {
|
||||||
|
this.pokemonEggMoveLabels[em].setText("(+) " + eggMove.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// will always have at least one egg move
|
||||||
|
this.pokemonEggMovesContainer.setVisible(true);
|
||||||
|
|
||||||
|
if (species.speciesId === Species.MANAPHY || species.speciesId === Species.PHIONE) {
|
||||||
|
this.pokemonHatchedIcon.setFrame("manaphy");
|
||||||
|
} else {
|
||||||
|
this.pokemonHatchedIcon.setFrame(getEggTierForSpecies(species));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -6,7 +6,7 @@ import { getNatureName } from "../data/nature";
|
||||||
import { Type } from "../data/type";
|
import { Type } from "../data/type";
|
||||||
import Pokemon from "../field/pokemon";
|
import Pokemon from "../field/pokemon";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { DexAttr } from "../system/game-data";
|
import { DexAttr, DexEntry, StarterDataEntry } from "../system/game-data";
|
||||||
import * as Utils from "../utils";
|
import * as Utils from "../utils";
|
||||||
import ConfirmUiHandler from "./confirm-ui-handler";
|
import ConfirmUiHandler from "./confirm-ui-handler";
|
||||||
import { StatsContainer } from "./stats-container";
|
import { StatsContainer } from "./stats-container";
|
||||||
|
@ -24,7 +24,7 @@ const languageSettings: { [key: string]: LanguageSetting } = {
|
||||||
infoContainerTextSize: "64px"
|
infoContainerTextSize: "64px"
|
||||||
},
|
},
|
||||||
"de": {
|
"de": {
|
||||||
infoContainerTextSize: "64px"
|
infoContainerTextSize: "64px",
|
||||||
},
|
},
|
||||||
"es": {
|
"es": {
|
||||||
infoContainerTextSize: "64px"
|
infoContainerTextSize: "64px"
|
||||||
|
@ -63,6 +63,7 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container {
|
||||||
private pokemonMovesContainers: Phaser.GameObjects.Container[];
|
private pokemonMovesContainers: Phaser.GameObjects.Container[];
|
||||||
private pokemonMoveBgs: Phaser.GameObjects.NineSlice[];
|
private pokemonMoveBgs: Phaser.GameObjects.NineSlice[];
|
||||||
private pokemonMoveLabels: Phaser.GameObjects.Text[];
|
private pokemonMoveLabels: Phaser.GameObjects.Text[];
|
||||||
|
private infoBg;
|
||||||
|
|
||||||
private numCharsBeforeCutoff = 16;
|
private numCharsBeforeCutoff = 16;
|
||||||
|
|
||||||
|
@ -83,9 +84,9 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container {
|
||||||
const currentLanguage = i18next.resolvedLanguage!; // TODO: is this bang correct?
|
const currentLanguage = i18next.resolvedLanguage!; // TODO: is this bang correct?
|
||||||
const langSettingKey = Object.keys(languageSettings).find(lang => currentLanguage?.includes(lang))!; // TODO: is this bang correct?
|
const langSettingKey = Object.keys(languageSettings).find(lang => currentLanguage?.includes(lang))!; // TODO: is this bang correct?
|
||||||
const textSettings = languageSettings[langSettingKey];
|
const textSettings = languageSettings[langSettingKey];
|
||||||
const infoBg = addWindow(this.scene, 0, 0, this.infoWindowWidth, 132);
|
this.infoBg = addWindow(this.scene, 0, 0, this.infoWindowWidth, 132);
|
||||||
infoBg.setOrigin(0.5, 0.5);
|
this.infoBg.setOrigin(0.5, 0.5);
|
||||||
infoBg.setName("window-info-bg");
|
this.infoBg.setName("window-info-bg");
|
||||||
|
|
||||||
this.pokemonMovesContainer = this.scene.add.container(6, 14);
|
this.pokemonMovesContainer = this.scene.add.container(6, 14);
|
||||||
this.pokemonMovesContainer.setName("pkmn-moves");
|
this.pokemonMovesContainer.setName("pkmn-moves");
|
||||||
|
@ -133,7 +134,7 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container {
|
||||||
|
|
||||||
this.statsContainer = new StatsContainer(this.scene, -48, -64, true);
|
this.statsContainer = new StatsContainer(this.scene, -48, -64, true);
|
||||||
|
|
||||||
this.add(infoBg);
|
this.add(this.infoBg);
|
||||||
this.add(this.statsContainer);
|
this.add(this.statsContainer);
|
||||||
|
|
||||||
// The position should be set per language
|
// The position should be set per language
|
||||||
|
@ -207,9 +208,16 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container {
|
||||||
this.setVisible(false);
|
this.setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
show(pokemon: Pokemon, showMoves: boolean = false, speedMultiplier: number = 1): Promise<void> {
|
show(pokemon: Pokemon, showMoves: boolean = false, speedMultiplier: number = 1, dexEntry?: DexEntry, starterEntry?: StarterDataEntry, eggInfo = false): Promise<void> {
|
||||||
return new Promise<void>(resolve => {
|
return new Promise<void>(resolve => {
|
||||||
const caughtAttr = BigInt(pokemon.scene.gameData.dexData[pokemon.species.speciesId].caughtAttr);
|
if (!dexEntry) {
|
||||||
|
dexEntry = pokemon.scene.gameData.dexData[pokemon.species.speciesId];
|
||||||
|
}
|
||||||
|
if (!starterEntry) {
|
||||||
|
starterEntry = pokemon.scene.gameData.starterData[pokemon.species.getRootSpeciesId()];
|
||||||
|
}
|
||||||
|
|
||||||
|
const caughtAttr = BigInt(dexEntry.caughtAttr);
|
||||||
if (pokemon.gender > Gender.GENDERLESS) {
|
if (pokemon.gender > Gender.GENDERLESS) {
|
||||||
this.pokemonGenderText.setText(getGenderSymbol(pokemon.gender));
|
this.pokemonGenderText.setText(getGenderSymbol(pokemon.gender));
|
||||||
this.pokemonGenderText.setColor(getGenderColor(pokemon.gender));
|
this.pokemonGenderText.setColor(getGenderColor(pokemon.gender));
|
||||||
|
@ -268,7 +276,7 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container {
|
||||||
const opponentPokemonAbilityIndex = (opponentPokemonOneNormalAbility && pokemon.abilityIndex === 1) ? 2 : pokemon.abilityIndex;
|
const opponentPokemonAbilityIndex = (opponentPokemonOneNormalAbility && pokemon.abilityIndex === 1) ? 2 : pokemon.abilityIndex;
|
||||||
const opponentPokemonAbilityAttr = 1 << opponentPokemonAbilityIndex;
|
const opponentPokemonAbilityAttr = 1 << opponentPokemonAbilityIndex;
|
||||||
|
|
||||||
const rootFormHasHiddenAbility = pokemon.scene.gameData.starterData[pokemon.species.getRootSpeciesId()].abilityAttr & opponentPokemonAbilityAttr;
|
const rootFormHasHiddenAbility = starterEntry.abilityAttr & opponentPokemonAbilityAttr;
|
||||||
|
|
||||||
if (!rootFormHasHiddenAbility) {
|
if (!rootFormHasHiddenAbility) {
|
||||||
this.pokemonAbilityLabelText.setColor(getTextColor(TextStyle.SUMMARY_BLUE, false, this.scene.uiTheme));
|
this.pokemonAbilityLabelText.setColor(getTextColor(TextStyle.SUMMARY_BLUE, false, this.scene.uiTheme));
|
||||||
|
@ -280,7 +288,7 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container {
|
||||||
|
|
||||||
this.pokemonNatureText.setText(getNatureName(pokemon.getNature(), true, false, false, this.scene.uiTheme));
|
this.pokemonNatureText.setText(getNatureName(pokemon.getNature(), true, false, false, this.scene.uiTheme));
|
||||||
|
|
||||||
const dexNatures = pokemon.scene.gameData.dexData[pokemon.species.speciesId].natureAttr;
|
const dexNatures = dexEntry.natureAttr;
|
||||||
const newNature = 1 << (pokemon.nature + 1);
|
const newNature = 1 << (pokemon.nature + 1);
|
||||||
|
|
||||||
if (!(dexNatures & newNature)) {
|
if (!(dexNatures & newNature)) {
|
||||||
|
@ -324,12 +332,11 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container {
|
||||||
}
|
}
|
||||||
|
|
||||||
const starterSpeciesId = pokemon.species.getRootSpeciesId();
|
const starterSpeciesId = pokemon.species.getRootSpeciesId();
|
||||||
const originalIvs: integer[] | null = this.scene.gameData.dexData[starterSpeciesId].caughtAttr
|
const originalIvs: integer[] | null = eggInfo ? (dexEntry.caughtAttr ? dexEntry.ivs : null) : (this.scene.gameData.dexData[starterSpeciesId].caughtAttr
|
||||||
? this.scene.gameData.dexData[starterSpeciesId].ivs
|
? this.scene.gameData.dexData[starterSpeciesId].ivs : null);
|
||||||
: null;
|
|
||||||
|
|
||||||
this.statsContainer.updateIvs(pokemon.ivs, originalIvs!); // TODO: is this bang correct?
|
this.statsContainer.updateIvs(pokemon.ivs, originalIvs!); // TODO: is this bang correct?
|
||||||
|
if (!eggInfo) {
|
||||||
this.scene.tweens.add({
|
this.scene.tweens.add({
|
||||||
targets: this,
|
targets: this,
|
||||||
duration: Utils.fixedInt(Math.floor(750 / speedMultiplier)),
|
duration: Utils.fixedInt(Math.floor(750 / speedMultiplier)),
|
||||||
|
@ -350,6 +357,7 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container {
|
||||||
onComplete: () => resolve()
|
onComplete: () => resolve()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (let m = 0; m < 4; m++) {
|
for (let m = 0; m < 4; m++) {
|
||||||
const move = m < pokemon.moveset.length && pokemon.moveset[m] ? pokemon.moveset[m]!.getMove() : null;
|
const move = m < pokemon.moveset.length && pokemon.moveset[m] ? pokemon.moveset[m]!.getMove() : null;
|
||||||
|
@ -364,6 +372,36 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
changeToEggSummaryLayout() {
|
||||||
|
// The position should be set per language (and shifted for new layout)
|
||||||
|
const currentLanguage = i18next.resolvedLanguage!; // TODO: is this bang correct?
|
||||||
|
const langSettingKey = Object.keys(languageSettings).find(lang => currentLanguage?.includes(lang))!; // TODO: is this bang correct?
|
||||||
|
const textSettings = languageSettings[langSettingKey];
|
||||||
|
|
||||||
|
const eggLabelTextOffset = 43;
|
||||||
|
const infoContainerLabelXPos = (textSettings?.infoContainerLabelXPos || -18) + eggLabelTextOffset;
|
||||||
|
const infoContainerTextXPos = (textSettings?.infoContainerTextXPos || -14) + eggLabelTextOffset;
|
||||||
|
|
||||||
|
this.x = this.initialX - this.infoWindowWidth;
|
||||||
|
|
||||||
|
this.pokemonGenderText.setPosition(89, -2);
|
||||||
|
this.pokemonGenderNewText.setPosition(79, -2);
|
||||||
|
this.pokemonShinyIcon.setPosition(82, 87);
|
||||||
|
this.pokemonShinyNewIcon.setPosition(72, 87);
|
||||||
|
|
||||||
|
this.pokemonFormLabelText.setPosition(infoContainerLabelXPos, 152);
|
||||||
|
this.pokemonFormText.setPosition(infoContainerTextXPos, 152);
|
||||||
|
this.pokemonAbilityLabelText.setPosition(infoContainerLabelXPos, 110);
|
||||||
|
this.pokemonAbilityText.setPosition(infoContainerTextXPos, 110);
|
||||||
|
this.pokemonNatureLabelText.setPosition(infoContainerLabelXPos, 125);
|
||||||
|
this.pokemonNatureText.setPosition(infoContainerTextXPos, 125);
|
||||||
|
|
||||||
|
this.statsContainer.setScale(0.7);
|
||||||
|
this.statsContainer.setPosition(30, -3);
|
||||||
|
this.infoBg.setVisible(false);
|
||||||
|
this.pokemonMovesContainer.setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
makeRoomForConfirmUi(speedMultiplier: number = 1, fromCatch: boolean = false): Promise<void> {
|
makeRoomForConfirmUi(speedMultiplier: number = 1, fromCatch: boolean = false): Promise<void> {
|
||||||
const xPosition = fromCatch ? this.initialX - this.infoWindowWidth - 65 : this.initialX - this.infoWindowWidth - ConfirmUiHandler.windowWidth;
|
const xPosition = fromCatch ? this.initialX - this.infoWindowWidth - 65 : this.initialX - this.infoWindowWidth - ConfirmUiHandler.windowWidth;
|
||||||
return new Promise<void>(resolve => {
|
return new Promise<void>(resolve => {
|
||||||
|
|
|
@ -1853,10 +1853,14 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
||||||
switch (button) {
|
switch (button) {
|
||||||
case Button.CYCLE_SHINY:
|
case Button.CYCLE_SHINY:
|
||||||
if (this.canCycleShiny) {
|
if (this.canCycleShiny) {
|
||||||
const newVariant = starterAttributes.variant ? starterAttributes.variant as Variant : props.variant;
|
starterAttributes.shiny = starterAttributes.shiny !== undefined ? !starterAttributes.shiny : false;
|
||||||
starterAttributes.shiny = starterAttributes.shiny ? !starterAttributes.shiny : true;
|
|
||||||
this.setSpeciesDetails(this.lastSpecies, !props.shiny, undefined, undefined, props.shiny ? 0 : newVariant, undefined, undefined);
|
|
||||||
if (starterAttributes.shiny) {
|
if (starterAttributes.shiny) {
|
||||||
|
// Change to shiny, we need to get the proper default variant
|
||||||
|
const newProps = this.scene.gameData.getSpeciesDexAttrProps(this.lastSpecies, this.getCurrentDexProps(this.lastSpecies.speciesId));
|
||||||
|
const newVariant = starterAttributes.variant ? starterAttributes.variant as Variant : newProps.variant;
|
||||||
|
this.setSpeciesDetails(this.lastSpecies, true, undefined, undefined, newVariant, undefined, undefined);
|
||||||
|
|
||||||
this.scene.playSound("se/sparkle");
|
this.scene.playSound("se/sparkle");
|
||||||
// Set the variant label to the shiny tint
|
// Set the variant label to the shiny tint
|
||||||
const tint = getVariantTint(newVariant);
|
const tint = getVariantTint(newVariant);
|
||||||
|
@ -1864,6 +1868,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
||||||
this.pokemonShinyIcon.setTint(tint);
|
this.pokemonShinyIcon.setTint(tint);
|
||||||
this.pokemonShinyIcon.setVisible(true);
|
this.pokemonShinyIcon.setVisible(true);
|
||||||
} else {
|
} else {
|
||||||
|
this.setSpeciesDetails(this.lastSpecies, false, undefined, undefined, 0, undefined, undefined);
|
||||||
this.pokemonShinyIcon.setVisible(false);
|
this.pokemonShinyIcon.setVisible(false);
|
||||||
success = true;
|
success = true;
|
||||||
}
|
}
|
||||||
|
@ -3487,23 +3492,22 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
||||||
props += DexAttr.MALE;
|
props += DexAttr.MALE;
|
||||||
}
|
}
|
||||||
/* This part is very similar to above, but instead of for gender, it checks for shiny within starter preferences.
|
/* This part is very similar to above, but instead of for gender, it checks for shiny within starter preferences.
|
||||||
* If they're not there, it checks the caughtAttr for shiny only (i.e. SHINY === true && NON_SHINY === false)
|
* If they're not there, it enables shiny state by default if any shiny was caught
|
||||||
*/
|
*/
|
||||||
if (this.starterPreferences[speciesId]?.shiny || ((caughtAttr & DexAttr.SHINY) > 0n && (caughtAttr & DexAttr.NON_SHINY) === 0n)) {
|
if (this.starterPreferences[speciesId]?.shiny || ((caughtAttr & DexAttr.SHINY) > 0n && this.starterPreferences[speciesId]?.shiny !== false)) {
|
||||||
props += DexAttr.SHINY;
|
props += DexAttr.SHINY;
|
||||||
if (this.starterPreferences[speciesId]?.variant) {
|
if (this.starterPreferences[speciesId]?.variant !== undefined) {
|
||||||
props += BigInt(Math.pow(2, this.starterPreferences[speciesId]?.variant)) * DexAttr.DEFAULT_VARIANT;
|
props += BigInt(Math.pow(2, this.starterPreferences[speciesId]?.variant)) * DexAttr.DEFAULT_VARIANT;
|
||||||
} else {
|
} else {
|
||||||
/* This calculates the correct variant if there's no starter preferences for it.
|
/* This calculates the correct variant if there's no starter preferences for it.
|
||||||
* This gets the lowest tier variant that you've caught (in line with other mechanics) and adds it to the temp props
|
* This gets the highest tier variant that you've caught and adds it to the temp props
|
||||||
*/
|
*/
|
||||||
if ((caughtAttr & DexAttr.DEFAULT_VARIANT) > 0) {
|
if ((caughtAttr & DexAttr.VARIANT_3) > 0) {
|
||||||
props += DexAttr.DEFAULT_VARIANT;
|
|
||||||
}
|
|
||||||
if ((caughtAttr & DexAttr.VARIANT_2) > 0) {
|
|
||||||
props += DexAttr.VARIANT_2;
|
|
||||||
} else if ((caughtAttr & DexAttr.VARIANT_3) > 0) {
|
|
||||||
props += DexAttr.VARIANT_3;
|
props += DexAttr.VARIANT_3;
|
||||||
|
} else if ((caughtAttr & DexAttr.VARIANT_2) > 0) {
|
||||||
|
props += DexAttr.VARIANT_2;
|
||||||
|
} else {
|
||||||
|
props += DexAttr.DEFAULT_VARIANT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -49,6 +49,7 @@ import RenameFormUiHandler from "./rename-form-ui-handler";
|
||||||
import AdminUiHandler from "./admin-ui-handler";
|
import AdminUiHandler from "./admin-ui-handler";
|
||||||
import RunHistoryUiHandler from "./run-history-ui-handler";
|
import RunHistoryUiHandler from "./run-history-ui-handler";
|
||||||
import RunInfoUiHandler from "./run-info-ui-handler";
|
import RunInfoUiHandler from "./run-info-ui-handler";
|
||||||
|
import EggSummaryUiHandler from "./egg-summary-ui-handler";
|
||||||
import TestDialogueUiHandler from "#app/ui/test-dialogue-ui-handler";
|
import TestDialogueUiHandler from "#app/ui/test-dialogue-ui-handler";
|
||||||
import AutoCompleteUiHandler from "./autocomplete-ui-handler";
|
import AutoCompleteUiHandler from "./autocomplete-ui-handler";
|
||||||
|
|
||||||
|
@ -66,6 +67,7 @@ export enum Mode {
|
||||||
STARTER_SELECT,
|
STARTER_SELECT,
|
||||||
EVOLUTION_SCENE,
|
EVOLUTION_SCENE,
|
||||||
EGG_HATCH_SCENE,
|
EGG_HATCH_SCENE,
|
||||||
|
EGG_HATCH_SUMMARY,
|
||||||
CONFIRM,
|
CONFIRM,
|
||||||
OPTION_SELECT,
|
OPTION_SELECT,
|
||||||
MENU,
|
MENU,
|
||||||
|
@ -171,6 +173,7 @@ export default class UI extends Phaser.GameObjects.Container {
|
||||||
new StarterSelectUiHandler(scene),
|
new StarterSelectUiHandler(scene),
|
||||||
new EvolutionSceneHandler(scene),
|
new EvolutionSceneHandler(scene),
|
||||||
new EggHatchSceneHandler(scene),
|
new EggHatchSceneHandler(scene),
|
||||||
|
new EggSummaryUiHandler(scene),
|
||||||
new ConfirmUiHandler(scene),
|
new ConfirmUiHandler(scene),
|
||||||
new OptionSelectUiHandler(scene),
|
new OptionSelectUiHandler(scene),
|
||||||
new MenuUiHandler(scene),
|
new MenuUiHandler(scene),
|
||||||
|
|
Loading…
Reference in New Issue