2023-12-16 04:07:32 +00:00
|
|
|
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
|
2024-02-22 04:57:49 +00:00
|
|
|
import { Phase } from "./phase";
|
2023-12-16 04:07:32 +00:00
|
|
|
import BattleScene, { AnySound } from "./battle-scene";
|
|
|
|
import * as Utils from "./utils";
|
|
|
|
import { Mode } from "./ui/ui";
|
2024-03-04 02:30:11 +00:00
|
|
|
import { EGG_SEED, Egg, GachaType, getLegendaryGachaSpeciesForTimestamp } from "./data/egg";
|
2023-12-16 04:07:32 +00:00
|
|
|
import EggHatchSceneHandler from "./ui/egg-hatch-scene-handler";
|
2024-03-01 01:08:50 +00:00
|
|
|
import { PlayerPokemon } from "./field/pokemon";
|
2023-12-16 04:07:32 +00:00
|
|
|
import { getPokemonSpecies, speciesStarters } from "./data/pokemon-species";
|
2023-12-20 04:51:48 +00:00
|
|
|
import { achvs } from "./system/achv";
|
2024-02-25 17:45:41 +00:00
|
|
|
import { pokemonPrevolutions } from "./data/pokemon-evolutions";
|
2024-03-08 03:43:15 +00:00
|
|
|
import PokemonInfoContainer from "./ui/pokemon-info-container";
|
2024-06-11 02:20:00 +01:00
|
|
|
import EggCounterContainer from "./ui/egg-counter-container";
|
|
|
|
import { EggCountChangedEvent } from "./events/egg";
|
2024-06-13 23:44:23 +01:00
|
|
|
import { EggTier } from "#enums/egg-type";
|
|
|
|
import { Species } from "#enums/species";
|
2023-12-16 04:07:32 +00:00
|
|
|
|
2024-05-31 22:34:02 +01:00
|
|
|
/**
|
|
|
|
* Class that represents egg hatching
|
|
|
|
*/
|
2024-02-22 04:57:49 +00:00
|
|
|
export class EggHatchPhase extends Phase {
|
2024-05-31 22:34:02 +01:00
|
|
|
/** The egg that is hatching */
|
2023-12-16 04:07:32 +00:00
|
|
|
private egg: Egg;
|
|
|
|
|
2024-05-31 22:34:02 +01:00
|
|
|
/** The number of eggs that are hatching */
|
2024-05-29 23:17:41 +01:00
|
|
|
private eggsToHatchCount: integer;
|
2024-05-31 22:34:02 +01:00
|
|
|
/** The container that lists how many eggs are hatching */
|
2024-06-11 02:20:00 +01:00
|
|
|
private eggCounterContainer: EggCounterContainer;
|
2024-05-29 23:17:41 +01:00
|
|
|
|
2024-05-31 22:34:02 +01:00
|
|
|
/** The scene handler for egg hatching */
|
2024-04-05 19:04:01 +01:00
|
|
|
private eggHatchHandler: EggHatchSceneHandler;
|
2024-05-31 22:34:02 +01:00
|
|
|
/** The phaser gameobject container that holds everything */
|
2023-12-16 04:07:32 +00:00
|
|
|
private eggHatchContainer: Phaser.GameObjects.Container;
|
2024-05-31 22:34:02 +01:00
|
|
|
/** The phaser image that is the background */
|
2023-12-16 04:07:32 +00:00
|
|
|
private eggHatchBg: Phaser.GameObjects.Image;
|
2024-05-31 22:34:02 +01:00
|
|
|
/** The phaser rectangle that overlays during the scene */
|
2023-12-16 04:07:32 +00:00
|
|
|
private eggHatchOverlay: Phaser.GameObjects.Rectangle;
|
2024-05-31 22:34:02 +01:00
|
|
|
/** The phaser container that holds the egg */
|
2023-12-16 04:07:32 +00:00
|
|
|
private eggContainer: Phaser.GameObjects.Container;
|
2024-05-31 22:34:02 +01:00
|
|
|
/** The phaser sprite of the egg */
|
2023-12-16 04:07:32 +00:00
|
|
|
private eggSprite: Phaser.GameObjects.Sprite;
|
2024-05-31 22:34:02 +01:00
|
|
|
/** The phaser sprite of the cracks in an egg */
|
2023-12-16 04:07:32 +00:00
|
|
|
private eggCrackSprite: Phaser.GameObjects.Sprite;
|
2024-05-31 22:34:02 +01:00
|
|
|
/** The phaser sprite that represents the overlaid light rays */
|
2023-12-16 04:07:32 +00:00
|
|
|
private eggLightraysOverlay: Phaser.GameObjects.Sprite;
|
2024-05-31 22:34:02 +01:00
|
|
|
/** The phaser sprite of the hatched Pokemon */
|
2023-12-16 04:07:32 +00:00
|
|
|
private pokemonSprite: Phaser.GameObjects.Sprite;
|
2024-05-31 22:34:02 +01:00
|
|
|
/** The phaser sprite for shiny sparkles */
|
2023-12-26 17:29:18 +00:00
|
|
|
private pokemonShinySparkle: Phaser.GameObjects.Sprite;
|
2023-12-16 04:07:32 +00:00
|
|
|
|
2024-05-31 22:34:02 +01:00
|
|
|
/** The {@link PokemonInfoContainer} of the newly hatched Pokemon */
|
2024-03-08 03:43:15 +00:00
|
|
|
private infoContainer: PokemonInfoContainer;
|
2023-12-20 04:51:48 +00:00
|
|
|
|
2024-05-31 22:34:02 +01:00
|
|
|
/** The newly hatched {@link PlayerPokemon} */
|
2024-02-20 05:24:39 +00:00
|
|
|
private pokemon: PlayerPokemon;
|
2024-05-31 22:34:02 +01:00
|
|
|
/** The index of which egg move is unlocked. 0-2 is common, 3 is rare */
|
2024-02-25 17:45:41 +00:00
|
|
|
private eggMoveIndex: integer;
|
2024-05-31 22:34:02 +01:00
|
|
|
/** Internal booleans representing if the egg is hatched, able to be skipped, or skipped */
|
2024-04-04 23:54:50 +01:00
|
|
|
private hatched: boolean;
|
2024-02-20 05:24:39 +00:00
|
|
|
private canSkip: boolean;
|
|
|
|
private skipped: boolean;
|
2024-05-31 22:34:02 +01:00
|
|
|
/** The sound effect being played when the egg is hatched */
|
2024-02-20 05:24:39 +00:00
|
|
|
private evolutionBgm: AnySound;
|
|
|
|
|
2024-05-29 23:17:41 +01:00
|
|
|
constructor(scene: BattleScene, egg: Egg, eggsToHatchCount: integer) {
|
2023-12-16 04:07:32 +00:00
|
|
|
super(scene);
|
|
|
|
|
|
|
|
this.egg = egg;
|
2024-05-29 23:17:41 +01:00
|
|
|
this.eggsToHatchCount = eggsToHatchCount;
|
2023-12-16 04:07:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
start() {
|
|
|
|
super.start();
|
|
|
|
|
|
|
|
this.scene.ui.setModeForceTransition(Mode.EGG_HATCH_SCENE).then(() => {
|
|
|
|
|
2024-05-23 16:03:10 +01:00
|
|
|
if (!this.egg) {
|
2023-12-16 04:07:32 +00:00
|
|
|
return this.end();
|
2024-05-23 16:03:10 +01:00
|
|
|
}
|
2023-12-16 04:07:32 +00:00
|
|
|
|
2023-12-20 04:51:48 +00:00
|
|
|
const eggIndex = this.scene.gameData.eggs.findIndex(e => e.id === this.egg.id);
|
|
|
|
|
2024-05-23 16:03:10 +01:00
|
|
|
if (eggIndex === -1) {
|
2023-12-20 04:51:48 +00:00
|
|
|
return this.end();
|
2024-05-23 16:03:10 +01:00
|
|
|
}
|
2023-12-20 04:51:48 +00:00
|
|
|
|
|
|
|
this.scene.gameData.eggs.splice(eggIndex, 1);
|
|
|
|
|
2023-12-16 04:07:32 +00:00
|
|
|
this.scene.fadeOutBgm(null, false);
|
|
|
|
|
2024-04-05 19:04:01 +01:00
|
|
|
this.eggHatchHandler = this.scene.ui.getHandler() as EggHatchSceneHandler;
|
2023-12-16 04:07:32 +00:00
|
|
|
|
2024-04-05 19:04:01 +01:00
|
|
|
this.eggHatchContainer = this.eggHatchHandler.eggHatchContainer;
|
2023-12-16 04:07:32 +00:00
|
|
|
|
2024-05-23 16:03:10 +01:00
|
|
|
this.eggHatchBg = this.scene.add.image(0, 0, "default_bg");
|
2023-12-16 04:07:32 +00:00
|
|
|
this.eggHatchBg.setOrigin(0, 0);
|
|
|
|
this.eggHatchContainer.add(this.eggHatchBg);
|
|
|
|
|
|
|
|
this.eggContainer = this.scene.add.container(this.eggHatchBg.displayWidth / 2, this.eggHatchBg.displayHeight / 2);
|
|
|
|
|
2024-05-23 16:03:10 +01:00
|
|
|
this.eggSprite = this.scene.add.sprite(0, 0, "egg", `egg_${this.egg.getKey()}`);
|
|
|
|
this.eggCrackSprite = this.scene.add.sprite(0, 0, "egg_crack", "0");
|
2023-12-16 04:07:32 +00:00
|
|
|
this.eggCrackSprite.setVisible(false);
|
|
|
|
|
2024-05-23 16:03:10 +01:00
|
|
|
this.eggLightraysOverlay = this.scene.add.sprite((-this.eggHatchBg.displayWidth / 2) + 4, -this.eggHatchBg.displayHeight / 2, "egg_lightrays", "3");
|
2023-12-16 04:07:32 +00:00
|
|
|
this.eggLightraysOverlay.setOrigin(0, 0);
|
|
|
|
this.eggLightraysOverlay.setVisible(false);
|
|
|
|
|
|
|
|
this.eggContainer.add(this.eggSprite);
|
|
|
|
this.eggContainer.add(this.eggCrackSprite);
|
|
|
|
this.eggContainer.add(this.eggLightraysOverlay);
|
|
|
|
this.eggHatchContainer.add(this.eggContainer);
|
|
|
|
|
2024-06-11 02:20:00 +01:00
|
|
|
this.eggCounterContainer = new EggCounterContainer(this.scene, this.eggsToHatchCount);
|
|
|
|
this.eggHatchContainer.add(this.eggCounterContainer);
|
2024-05-29 23:17:41 +01:00
|
|
|
|
2024-04-21 23:22:33 +01:00
|
|
|
const getPokemonSprite = () => {
|
2024-05-23 16:03:10 +01:00
|
|
|
const ret = this.scene.add.sprite(this.eggHatchBg.displayWidth / 2, this.eggHatchBg.displayHeight / 2, "pkmn__sub");
|
2024-04-21 23:22:33 +01:00
|
|
|
ret.setPipeline(this.scene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], ignoreTimeTint: true });
|
|
|
|
return ret;
|
|
|
|
};
|
2023-12-16 04:07:32 +00:00
|
|
|
|
|
|
|
this.eggHatchContainer.add((this.pokemonSprite = getPokemonSprite()));
|
|
|
|
|
2024-05-23 16:03:10 +01:00
|
|
|
this.pokemonShinySparkle = this.scene.add.sprite(this.pokemonSprite.x, this.pokemonSprite.y, "shiny");
|
2023-12-26 17:29:18 +00:00
|
|
|
this.pokemonShinySparkle.setVisible(false);
|
|
|
|
|
|
|
|
this.eggHatchContainer.add(this.pokemonShinySparkle);
|
|
|
|
|
2023-12-16 04:07:32 +00:00
|
|
|
this.eggHatchOverlay = this.scene.add.rectangle(0, -this.scene.game.canvas.height / 6, this.scene.game.canvas.width / 6, this.scene.game.canvas.height / 6, 0xFFFFFF);
|
|
|
|
this.eggHatchOverlay.setOrigin(0, 0);
|
|
|
|
this.eggHatchOverlay.setAlpha(0);
|
|
|
|
this.scene.fieldUI.add(this.eggHatchOverlay);
|
|
|
|
|
2024-03-08 03:43:15 +00:00
|
|
|
this.infoContainer = new PokemonInfoContainer(this.scene);
|
|
|
|
this.infoContainer.setup();
|
2024-01-06 03:24:05 +00:00
|
|
|
|
2023-12-20 04:51:48 +00:00
|
|
|
this.eggHatchContainer.add(this.infoContainer);
|
|
|
|
|
2024-05-31 22:34:02 +01:00
|
|
|
// The game will try to unfuse any Pokemon even though eggs should not generate fused Pokemon in the first place
|
2023-12-16 04:07:32 +00:00
|
|
|
const pokemon = this.generatePokemon();
|
2024-05-23 16:03:10 +01:00
|
|
|
if (pokemon.fusionSpecies) {
|
2023-12-31 18:20:28 +00:00
|
|
|
pokemon.clearFusionSpecies();
|
2024-05-23 16:03:10 +01:00
|
|
|
}
|
2023-12-16 04:07:32 +00:00
|
|
|
|
|
|
|
this.pokemonSprite.setVisible(false);
|
|
|
|
|
2024-02-20 05:24:39 +00:00
|
|
|
this.pokemon = pokemon;
|
2023-12-16 04:07:32 +00:00
|
|
|
|
|
|
|
pokemon.loadAssets().then(() => {
|
2024-04-04 23:54:50 +01:00
|
|
|
this.canSkip = true;
|
|
|
|
|
2024-02-20 05:24:39 +00:00
|
|
|
this.scene.time.delayedCall(1000, () => {
|
2024-05-23 16:03:10 +01:00
|
|
|
if (!this.hatched) {
|
|
|
|
this.evolutionBgm = this.scene.playSoundWithoutBgm("evolution");
|
|
|
|
}
|
2024-02-20 05:24:39 +00:00
|
|
|
});
|
2023-12-16 04:07:32 +00:00
|
|
|
|
|
|
|
this.scene.time.delayedCall(2000, () => {
|
2024-05-23 16:03:10 +01:00
|
|
|
if (this.hatched) {
|
2024-02-20 05:24:39 +00:00
|
|
|
return;
|
2024-05-23 16:03:10 +01:00
|
|
|
}
|
2023-12-16 04:07:32 +00:00
|
|
|
this.eggCrackSprite.setVisible(true);
|
|
|
|
this.doSpray(1, this.eggSprite.displayHeight / -2);
|
|
|
|
this.doEggShake(2).then(() => {
|
2024-05-23 16:03:10 +01:00
|
|
|
if (this.hatched) {
|
2024-04-07 02:48:48 +01:00
|
|
|
return;
|
2024-05-23 16:03:10 +01:00
|
|
|
}
|
2023-12-16 04:07:32 +00:00
|
|
|
this.scene.time.delayedCall(1000, () => {
|
2024-05-23 16:03:10 +01:00
|
|
|
if (this.hatched) {
|
2024-02-20 05:24:39 +00:00
|
|
|
return;
|
2024-05-23 16:03:10 +01:00
|
|
|
}
|
2023-12-16 04:07:32 +00:00
|
|
|
this.doSpray(2, this.eggSprite.displayHeight / -4);
|
2024-05-23 16:03:10 +01:00
|
|
|
this.eggCrackSprite.setFrame("1");
|
|
|
|
this.scene.time.delayedCall(125, () => this.eggCrackSprite.setFrame("2"));
|
2023-12-16 04:07:32 +00:00
|
|
|
this.doEggShake(4).then(() => {
|
2024-05-23 16:03:10 +01:00
|
|
|
if (this.hatched) {
|
2024-02-20 05:24:39 +00:00
|
|
|
return;
|
2024-05-23 16:03:10 +01:00
|
|
|
}
|
2023-12-16 04:07:32 +00:00
|
|
|
this.scene.time.delayedCall(1000, () => {
|
2024-05-23 16:03:10 +01:00
|
|
|
if (this.hatched) {
|
2024-02-20 05:24:39 +00:00
|
|
|
return;
|
2024-05-23 16:03:10 +01:00
|
|
|
}
|
|
|
|
this.scene.playSound("egg_crack");
|
2023-12-16 04:07:32 +00:00
|
|
|
this.doSpray(4);
|
2024-05-23 16:03:10 +01:00
|
|
|
this.eggCrackSprite.setFrame("3");
|
|
|
|
this.scene.time.delayedCall(125, () => this.eggCrackSprite.setFrame("4"));
|
2023-12-16 04:07:32 +00:00
|
|
|
this.doEggShake(8, 2).then(() => {
|
2024-05-23 16:03:10 +01:00
|
|
|
if (!this.hatched) {
|
2024-02-20 05:24:39 +00:00
|
|
|
this.doHatch();
|
2024-05-23 16:03:10 +01:00
|
|
|
}
|
2023-12-16 04:07:32 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2024-05-23 16:03:10 +01:00
|
|
|
});
|
2023-12-16 04:07:32 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
2024-04-07 02:48:48 +01:00
|
|
|
|
2024-04-05 19:04:01 +01:00
|
|
|
end() {
|
2024-05-23 16:03:10 +01:00
|
|
|
if (this.scene.findPhase((p) => p instanceof EggHatchPhase)) {
|
2024-04-05 22:50:01 +01:00
|
|
|
this.eggHatchHandler.clear();
|
2024-05-23 16:03:10 +01:00
|
|
|
} else {
|
2024-04-07 02:48:48 +01:00
|
|
|
this.scene.time.delayedCall(250, () => this.scene.setModifiersVisible(true));
|
2024-05-23 16:03:10 +01:00
|
|
|
}
|
2024-04-05 22:50:01 +01:00
|
|
|
super.end();
|
2024-04-05 19:04:01 +01:00
|
|
|
}
|
2023-12-16 04:07:32 +00:00
|
|
|
|
2024-05-31 22:34:02 +01:00
|
|
|
/**
|
|
|
|
* Function that animates egg shaking
|
|
|
|
* @param intensity of horizontal shaking. Doubled on the first call (where count is 0)
|
|
|
|
* @param repeatCount the number of times this function should be called (asynchronous recursion?!?)
|
|
|
|
* @param count the current number of times this function has been called.
|
|
|
|
* @returns nothing since it's a Promise<void>
|
|
|
|
*/
|
2023-12-16 04:07:32 +00:00
|
|
|
doEggShake(intensity: number, repeatCount?: integer, count?: integer): Promise<void> {
|
|
|
|
return new Promise(resolve => {
|
2024-05-23 16:03:10 +01:00
|
|
|
if (repeatCount === undefined) {
|
2023-12-16 04:07:32 +00:00
|
|
|
repeatCount = 0;
|
2024-05-23 16:03:10 +01:00
|
|
|
}
|
|
|
|
if (count === undefined) {
|
2023-12-16 04:07:32 +00:00
|
|
|
count = 0;
|
2024-05-23 16:03:10 +01:00
|
|
|
}
|
|
|
|
this.scene.playSound("pb_move");
|
2023-12-16 04:07:32 +00:00
|
|
|
this.scene.tweens.add({
|
|
|
|
targets: this.eggContainer,
|
|
|
|
x: `-=${intensity / (count ? 1 : 2)}`,
|
2024-05-23 16:03:10 +01:00
|
|
|
ease: "Sine.easeInOut",
|
2023-12-16 04:07:32 +00:00
|
|
|
duration: 125,
|
|
|
|
onComplete: () => {
|
|
|
|
this.scene.tweens.add({
|
|
|
|
targets: this.eggContainer,
|
|
|
|
x: `+=${intensity}`,
|
2024-05-23 16:03:10 +01:00
|
|
|
ease: "Sine.easeInOut",
|
2023-12-16 04:07:32 +00:00
|
|
|
duration: 250,
|
|
|
|
onComplete: () => {
|
|
|
|
count++;
|
2024-05-23 16:03:10 +01:00
|
|
|
if (count < repeatCount) {
|
2023-12-16 04:07:32 +00:00
|
|
|
return this.doEggShake(intensity, repeatCount, count).then(() => resolve());
|
2024-05-23 16:03:10 +01:00
|
|
|
}
|
2023-12-16 04:07:32 +00:00
|
|
|
this.scene.tweens.add({
|
|
|
|
targets: this.eggContainer,
|
|
|
|
x: `-=${intensity / 2}`,
|
2024-05-23 16:03:10 +01:00
|
|
|
ease: "Sine.easeInOut",
|
2023-12-16 04:07:32 +00:00
|
|
|
duration: 125,
|
|
|
|
onComplete: () => resolve()
|
|
|
|
});
|
|
|
|
}
|
2024-05-23 16:03:10 +01:00
|
|
|
});
|
2023-12-16 04:07:32 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-05-31 22:34:02 +01:00
|
|
|
/**
|
|
|
|
* Tries to skip the hatching animation
|
|
|
|
* @returns false if cannot be skipped or already skipped. True otherwise
|
|
|
|
*/
|
2024-02-20 05:24:39 +00:00
|
|
|
trySkip(): boolean {
|
2024-05-23 16:03:10 +01:00
|
|
|
if (!this.canSkip || this.skipped) {
|
2024-02-20 05:24:39 +00:00
|
|
|
return false;
|
2024-05-23 16:03:10 +01:00
|
|
|
}
|
2024-06-16 05:48:09 +01:00
|
|
|
if (this.eggCounterContainer.eggCountText?.data === undefined) {
|
|
|
|
return false;
|
|
|
|
}
|
2024-02-20 05:24:39 +00:00
|
|
|
this.skipped = true;
|
2024-05-23 16:03:10 +01:00
|
|
|
if (!this.hatched) {
|
2024-04-04 23:54:50 +01:00
|
|
|
this.doHatch();
|
2024-05-23 16:03:10 +01:00
|
|
|
} else {
|
2024-04-04 23:54:50 +01:00
|
|
|
this.doReveal();
|
2024-05-23 16:03:10 +01:00
|
|
|
}
|
2024-02-20 05:24:39 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-05-31 22:34:02 +01:00
|
|
|
/**
|
|
|
|
* Plays the animation of an egg hatch
|
|
|
|
*/
|
2024-02-20 05:24:39 +00:00
|
|
|
doHatch(): void {
|
|
|
|
this.canSkip = false;
|
2024-04-04 23:54:50 +01:00
|
|
|
this.hatched = true;
|
2024-05-23 16:03:10 +01:00
|
|
|
if (this.evolutionBgm) {
|
2024-04-04 23:54:50 +01:00
|
|
|
SoundFade.fadeOut(this.scene, this.evolutionBgm, Utils.fixedInt(100));
|
2024-05-23 16:03:10 +01:00
|
|
|
}
|
|
|
|
for (let e = 0; e < 5; e++) {
|
|
|
|
this.scene.time.delayedCall(Utils.fixedInt(375 * e), () => this.scene.playSound("egg_hatch", { volume: 1 - (e * 0.2) }));
|
|
|
|
}
|
2024-02-20 05:24:39 +00:00
|
|
|
this.eggLightraysOverlay.setVisible(true);
|
2024-05-23 16:03:10 +01:00
|
|
|
this.eggLightraysOverlay.play("egg_lightrays");
|
2024-02-20 05:24:39 +00:00
|
|
|
this.scene.tweens.add({
|
|
|
|
duration: Utils.fixedInt(125),
|
|
|
|
targets: this.eggHatchOverlay,
|
|
|
|
alpha: 1,
|
2024-05-23 16:03:10 +01:00
|
|
|
ease: "Cubic.easeIn",
|
2024-04-04 23:54:50 +01:00
|
|
|
onComplete: () => {
|
|
|
|
this.skipped = false;
|
|
|
|
this.canSkip = true;
|
|
|
|
}
|
2024-02-20 05:24:39 +00:00
|
|
|
});
|
|
|
|
this.scene.time.delayedCall(Utils.fixedInt(1500), () => {
|
2024-04-04 23:54:50 +01:00
|
|
|
this.canSkip = false;
|
2024-05-23 16:03:10 +01:00
|
|
|
if (!this.skipped) {
|
2024-04-04 23:54:50 +01:00
|
|
|
this.doReveal();
|
2024-05-23 16:03:10 +01:00
|
|
|
}
|
2024-04-04 23:54:50 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-05-31 22:34:02 +01:00
|
|
|
/**
|
|
|
|
* Function to do the logic and animation of completing a hatch and revealing the Pokemon
|
|
|
|
*/
|
2024-04-04 23:54:50 +01:00
|
|
|
doReveal(): void {
|
|
|
|
const isShiny = this.pokemon.isShiny();
|
2024-05-23 16:03:10 +01:00
|
|
|
if (this.pokemon.species.subLegendary) {
|
2024-05-05 16:05:22 +01:00
|
|
|
this.scene.validateAchv(achvs.HATCH_SUB_LEGENDARY);
|
2024-05-23 16:03:10 +01:00
|
|
|
}
|
|
|
|
if (this.pokemon.species.legendary) {
|
2024-04-04 23:54:50 +01:00
|
|
|
this.scene.validateAchv(achvs.HATCH_LEGENDARY);
|
2024-05-23 16:03:10 +01:00
|
|
|
}
|
|
|
|
if (this.pokemon.species.mythical) {
|
2024-05-05 16:05:22 +01:00
|
|
|
this.scene.validateAchv(achvs.HATCH_MYTHICAL);
|
2024-05-23 16:03:10 +01:00
|
|
|
}
|
|
|
|
if (isShiny) {
|
2024-04-04 23:54:50 +01:00
|
|
|
this.scene.validateAchv(achvs.HATCH_SHINY);
|
2024-05-23 16:03:10 +01:00
|
|
|
}
|
2024-04-04 23:54:50 +01:00
|
|
|
this.eggContainer.setVisible(false);
|
|
|
|
this.pokemonSprite.play(this.pokemon.getSpriteKey(true));
|
2024-05-23 16:03:10 +01:00
|
|
|
this.pokemonSprite.setPipelineData("ignoreTimeTint", true);
|
|
|
|
this.pokemonSprite.setPipelineData("spriteKey", this.pokemon.getSpriteKey());
|
|
|
|
this.pokemonSprite.setPipelineData("shiny", this.pokemon.shiny);
|
|
|
|
this.pokemonSprite.setPipelineData("variant", this.pokemon.variant);
|
2024-04-04 23:54:50 +01:00
|
|
|
this.pokemonSprite.setVisible(true);
|
|
|
|
this.scene.time.delayedCall(Utils.fixedInt(250), () => {
|
2024-06-11 02:20:00 +01:00
|
|
|
this.eggsToHatchCount--;
|
|
|
|
this.eggHatchHandler.eventTarget.dispatchEvent(new EggCountChangedEvent(this.eggsToHatchCount));
|
2024-04-04 23:54:50 +01:00
|
|
|
this.pokemon.cry();
|
|
|
|
if (isShiny) {
|
|
|
|
this.scene.time.delayedCall(Utils.fixedInt(500), () => {
|
2024-05-23 16:03:10 +01:00
|
|
|
this.pokemonShinySparkle.play(`sparkle${this.pokemon.variant ? `_${this.pokemon.variant + 1}` : ""}`);
|
|
|
|
this.scene.playSound("sparkle");
|
2024-02-20 05:24:39 +00:00
|
|
|
});
|
2024-04-04 23:54:50 +01:00
|
|
|
}
|
|
|
|
this.scene.time.delayedCall(Utils.fixedInt(!this.skipped ? !isShiny ? 1250 : 1750 : !isShiny ? 250 : 750), () => {
|
|
|
|
this.infoContainer.show(this.pokemon, false, this.skipped ? 2 : 1);
|
|
|
|
|
2024-05-23 16:03:10 +01:00
|
|
|
this.scene.playSoundWithoutBgm("evolution_fanfare");
|
2024-05-24 00:45:04 +01:00
|
|
|
|
2024-04-04 23:54:50 +01:00
|
|
|
this.scene.ui.showText(`${this.pokemon.name} hatched from the egg!`, null, () => {
|
|
|
|
this.scene.gameData.updateSpeciesDexIvs(this.pokemon.species.speciesId, this.pokemon.ivs);
|
|
|
|
this.scene.gameData.setPokemonCaught(this.pokemon, true, true).then(() => {
|
|
|
|
this.scene.gameData.setEggMoveUnlocked(this.pokemon.species, this.eggMoveIndex).then(() => {
|
|
|
|
this.scene.ui.showText(null, 0);
|
|
|
|
this.end();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}, null, true, 3000);
|
|
|
|
//this.scene.time.delayedCall(Utils.fixedInt(4250), () => this.scene.playBgm());
|
2024-02-20 05:24:39 +00:00
|
|
|
});
|
2024-04-04 23:54:50 +01:00
|
|
|
});
|
|
|
|
this.scene.tweens.add({
|
|
|
|
duration: Utils.fixedInt(this.skipped ? 500 : 3000),
|
|
|
|
targets: this.eggHatchOverlay,
|
|
|
|
alpha: 0,
|
2024-05-23 16:03:10 +01:00
|
|
|
ease: "Cubic.easeOut"
|
2024-02-20 05:24:39 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-05-31 22:34:02 +01:00
|
|
|
/**
|
|
|
|
* Helper function to generate sine. (Why is this not a Utils?!?)
|
|
|
|
* @param index random number from 0-7 being passed in to scale pi/128
|
|
|
|
* @param amplitude Scaling
|
|
|
|
* @returns a number
|
|
|
|
*/
|
2023-12-16 04:07:32 +00:00
|
|
|
sin(index: integer, amplitude: integer): number {
|
|
|
|
return amplitude * Math.sin(index * (Math.PI / 128));
|
|
|
|
}
|
|
|
|
|
2024-05-31 22:34:02 +01:00
|
|
|
/**
|
|
|
|
* Animates spraying
|
|
|
|
* @param intensity number of times this is repeated (this is a badly named variable)
|
|
|
|
* @param offsetY how much to offset the Y coordinates
|
|
|
|
*/
|
2023-12-16 04:07:32 +00:00
|
|
|
doSpray(intensity: integer, offsetY?: number) {
|
|
|
|
this.scene.tweens.addCounter({
|
|
|
|
repeat: intensity,
|
|
|
|
duration: Utils.getFrameMs(1),
|
|
|
|
onRepeat: () => {
|
|
|
|
this.doSprayParticle(Utils.randInt(8), offsetY || 0);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-05-31 22:34:02 +01:00
|
|
|
/**
|
|
|
|
* Animates a particle used in the spray animation
|
|
|
|
* @param trigIndex Used to modify the particle's vertical speed, is a random number from 0-7
|
|
|
|
* @param offsetY how much to offset the Y coordinate
|
|
|
|
*/
|
2023-12-16 04:07:32 +00:00
|
|
|
doSprayParticle(trigIndex: integer, offsetY: number) {
|
|
|
|
const initialX = this.eggHatchBg.displayWidth / 2;
|
|
|
|
const initialY = this.eggHatchBg.displayHeight / 2 + offsetY;
|
2024-05-23 16:03:10 +01:00
|
|
|
const shardKey = !this.egg.isManaphyEgg() ? this.egg.tier.toString() : "1";
|
|
|
|
const particle = this.scene.add.image(initialX, initialY, "egg_shard", `${shardKey}_${Math.floor(trigIndex / 2)}`);
|
2023-12-16 04:07:32 +00:00
|
|
|
this.eggHatchContainer.add(particle);
|
|
|
|
|
|
|
|
let f = 0;
|
|
|
|
let yOffset = 0;
|
2024-05-23 16:03:10 +01:00
|
|
|
const speed = 3 - Utils.randInt(8);
|
|
|
|
const amp = 24 + Utils.randInt(32);
|
2023-12-16 04:07:32 +00:00
|
|
|
|
|
|
|
const particleTimer = this.scene.tweens.addCounter({
|
|
|
|
repeat: -1,
|
|
|
|
duration: Utils.getFrameMs(1),
|
|
|
|
onRepeat: () => {
|
|
|
|
updateParticle();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const updateParticle = () => {
|
2024-04-04 23:54:50 +01:00
|
|
|
const speedMultiplier = this.skipped ? 6 : 1;
|
|
|
|
yOffset += speedMultiplier;
|
2023-12-16 04:07:32 +00:00
|
|
|
if (trigIndex < 160) {
|
|
|
|
particle.setPosition(initialX + (speed * f) / 3, initialY + yOffset);
|
|
|
|
particle.y += -this.sin(trigIndex, amp);
|
2024-05-23 16:03:10 +01:00
|
|
|
if (f > 108) {
|
2023-12-16 04:07:32 +00:00
|
|
|
particle.setScale((1 - (f - 108) / 20));
|
2024-05-23 16:03:10 +01:00
|
|
|
}
|
2024-04-04 23:54:50 +01:00
|
|
|
trigIndex += 2 * speedMultiplier;
|
|
|
|
f += speedMultiplier;
|
2023-12-16 04:07:32 +00:00
|
|
|
} else {
|
|
|
|
particle.destroy();
|
|
|
|
particleTimer.remove();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
updateParticle();
|
|
|
|
}
|
|
|
|
|
2024-05-31 22:34:02 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Generates a Pokemon to be hatched by the egg
|
|
|
|
* @returns the hatched PlayerPokemon
|
|
|
|
*/
|
2023-12-31 18:20:28 +00:00
|
|
|
generatePokemon(): PlayerPokemon {
|
|
|
|
let ret: PlayerPokemon;
|
2024-05-31 22:34:02 +01:00
|
|
|
let speciesOverride: Species; // SpeciesOverride should probably be a passed in parameter for future species-eggs
|
2023-12-20 04:51:48 +00:00
|
|
|
|
2024-03-02 03:18:39 +00:00
|
|
|
this.scene.executeWithSeedOffset(() => {
|
|
|
|
|
2024-05-31 22:34:02 +01:00
|
|
|
/**
|
|
|
|
* Manaphy eggs have a 1/8 chance of being Manaphy and 7/8 chance of being Phione
|
|
|
|
* Legendary eggs pulled from the legendary gacha have a 50% of being converted into
|
|
|
|
* the species that was the legendary focus at the time
|
|
|
|
*/
|
2024-03-02 03:18:39 +00:00
|
|
|
if (this.egg.isManaphyEgg()) {
|
2023-12-20 04:51:48 +00:00
|
|
|
const rand = Utils.randSeedInt(8);
|
2024-05-24 00:45:04 +01:00
|
|
|
|
2023-12-20 04:51:48 +00:00
|
|
|
speciesOverride = rand ? Species.PHIONE : Species.MANAPHY;
|
2024-03-02 03:18:39 +00:00
|
|
|
} else if (this.egg.tier === EggTier.MASTER
|
|
|
|
&& this.egg.gachaType === GachaType.LEGENDARY) {
|
2024-05-23 16:03:10 +01:00
|
|
|
if (!Utils.randSeedInt(2)) {
|
2023-12-20 04:51:48 +00:00
|
|
|
speciesOverride = getLegendaryGachaSpeciesForTimestamp(this.scene, this.egg.timestamp);
|
2024-05-23 16:03:10 +01:00
|
|
|
}
|
2024-03-02 03:18:39 +00:00
|
|
|
}
|
2023-12-16 04:07:32 +00:00
|
|
|
|
2024-03-02 03:18:39 +00:00
|
|
|
if (speciesOverride) {
|
2023-12-20 05:35:41 +00:00
|
|
|
const pokemonSpecies = getPokemonSpecies(speciesOverride);
|
2024-04-11 01:31:52 +01:00
|
|
|
ret = this.scene.addPlayerPokemon(pokemonSpecies, 1, undefined, undefined, undefined, false);
|
2024-03-02 03:18:39 +00:00
|
|
|
} else {
|
|
|
|
let minStarterValue: integer;
|
|
|
|
let maxStarterValue: integer;
|
2023-12-16 04:07:32 +00:00
|
|
|
|
2024-03-02 03:18:39 +00:00
|
|
|
switch (this.egg.tier) {
|
2024-05-23 16:03:10 +01:00
|
|
|
case EggTier.GREAT:
|
|
|
|
minStarterValue = 4;
|
|
|
|
maxStarterValue = 5;
|
|
|
|
break;
|
|
|
|
case EggTier.ULTRA:
|
|
|
|
minStarterValue = 6;
|
|
|
|
maxStarterValue = 7;
|
|
|
|
break;
|
|
|
|
case EggTier.MASTER:
|
|
|
|
minStarterValue = 8;
|
|
|
|
maxStarterValue = 9;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
minStarterValue = 1;
|
|
|
|
maxStarterValue = 3;
|
|
|
|
break;
|
2024-03-02 03:18:39 +00:00
|
|
|
}
|
2023-12-16 04:07:32 +00:00
|
|
|
|
2024-03-02 03:18:39 +00:00
|
|
|
const ignoredSpecies = [ Species.PHIONE, Species.MANAPHY, Species.ETERNATUS ];
|
2023-12-20 04:51:48 +00:00
|
|
|
|
2024-06-03 20:43:52 +01:00
|
|
|
let speciesPool = Object.keys(speciesStarters)
|
2024-03-02 03:18:39 +00:00
|
|
|
.filter(s => speciesStarters[s] >= minStarterValue && speciesStarters[s] <= maxStarterValue)
|
|
|
|
.map(s => parseInt(s) as Species)
|
|
|
|
.filter(s => !pokemonPrevolutions.hasOwnProperty(s) && getPokemonSpecies(s).isObtainable() && ignoredSpecies.indexOf(s) === -1);
|
2023-12-20 04:51:48 +00:00
|
|
|
|
2024-06-03 20:43:52 +01:00
|
|
|
// If this is the 10th egg without unlocking something new, attempt to force it.
|
|
|
|
if (this.scene.gameData.unlockPity[this.egg.tier] >= 9) {
|
|
|
|
const lockedPool = speciesPool.filter(s => !this.scene.gameData.dexData[s].caughtAttr);
|
|
|
|
if (lockedPool.length) { // Skip this if everything is unlocked
|
|
|
|
speciesPool = lockedPool;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-31 22:34:02 +01:00
|
|
|
/**
|
|
|
|
* Pokemon that are cheaper in their tier get a weight boost. Regionals get a weight penalty
|
|
|
|
* 1 cost mons get 2x
|
|
|
|
* 2 cost mons get 1.5x
|
|
|
|
* 4, 6, 8 cost mons get 1.75x
|
|
|
|
* 3, 5, 7, 9 cost mons get 1x
|
|
|
|
* Alolan, Galarian, and Paldean mons get 0.5x
|
|
|
|
* Hisui mons get 0.125x
|
|
|
|
*
|
|
|
|
* The total weight is also being calculated EACH time there is an egg hatch instead of being generated once
|
|
|
|
* and being the same each time
|
|
|
|
*/
|
2024-03-02 03:18:39 +00:00
|
|
|
let totalWeight = 0;
|
|
|
|
const speciesWeights = [];
|
2024-05-23 16:03:10 +01:00
|
|
|
for (const speciesId of speciesPool) {
|
2024-03-02 03:18:39 +00:00
|
|
|
let weight = Math.floor((((maxStarterValue - speciesStarters[speciesId]) / ((maxStarterValue - minStarterValue) + 1)) * 1.5 + 1) * 100);
|
|
|
|
const species = getPokemonSpecies(speciesId);
|
2024-05-23 16:03:10 +01:00
|
|
|
if (species.isRegional()) {
|
2024-03-02 03:18:39 +00:00
|
|
|
weight = Math.floor(weight / (species.isRareRegional() ? 8 : 2));
|
2024-05-23 16:03:10 +01:00
|
|
|
}
|
2024-03-02 03:18:39 +00:00
|
|
|
speciesWeights.push(totalWeight + weight);
|
|
|
|
totalWeight += weight;
|
|
|
|
}
|
2023-12-16 04:07:32 +00:00
|
|
|
|
2024-03-02 03:18:39 +00:00
|
|
|
let species: Species;
|
2023-12-20 04:51:48 +00:00
|
|
|
|
|
|
|
const rand = Utils.randSeedInt(totalWeight);
|
|
|
|
for (let s = 0; s < speciesWeights.length; s++) {
|
|
|
|
if (rand < speciesWeights[s]) {
|
|
|
|
species = speciesPool[s];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-03 20:43:52 +01:00
|
|
|
if (!!this.scene.gameData.dexData[species].caughtAttr) {
|
|
|
|
this.scene.gameData.unlockPity[this.egg.tier] = Math.min(this.scene.gameData.unlockPity[this.egg.tier] + 1, 10);
|
|
|
|
} else {
|
|
|
|
this.scene.gameData.unlockPity[this.egg.tier] = 0;
|
|
|
|
}
|
|
|
|
|
2023-12-20 05:35:41 +00:00
|
|
|
const pokemonSpecies = getPokemonSpecies(species);
|
|
|
|
|
2024-04-11 01:31:52 +01:00
|
|
|
ret = this.scene.addPlayerPokemon(pokemonSpecies, 1, undefined, undefined, undefined, false);
|
2024-03-02 03:18:39 +00:00
|
|
|
}
|
2023-12-16 04:07:32 +00:00
|
|
|
|
2024-05-31 22:34:02 +01:00
|
|
|
/**
|
|
|
|
* Non Shiny gacha Pokemon have a 1/128 chance of being shiny
|
|
|
|
* Shiny gacha Pokemon have a 1/64 chance of being shiny
|
|
|
|
* IVs are rolled twice and the higher of each stat's IV is taken
|
|
|
|
* The egg move gacha doubles the rate of rare egg moves but the base rates are
|
|
|
|
* Common: 1/48
|
|
|
|
* Rare: 1/24
|
|
|
|
* Epic: 1/12
|
|
|
|
* Legendary: 1/6
|
|
|
|
*/
|
2024-03-02 03:18:39 +00:00
|
|
|
ret.trySetShiny(this.egg.gachaType === GachaType.SHINY ? 1024 : 512);
|
2024-04-26 20:38:06 +01:00
|
|
|
ret.variant = ret.shiny ? ret.generateVariant() : 0;
|
2023-12-20 18:05:11 +00:00
|
|
|
|
2024-03-29 04:03:54 +00:00
|
|
|
const secondaryIvs = Utils.getIvsFromId(Utils.randSeedInt(4294967295));
|
2024-03-02 03:18:39 +00:00
|
|
|
|
2024-05-23 16:03:10 +01:00
|
|
|
for (let s = 0; s < ret.ivs.length; s++) {
|
2023-12-20 18:05:11 +00:00
|
|
|
ret.ivs[s] = Math.max(ret.ivs[s], secondaryIvs[s]);
|
2024-05-23 16:03:10 +01:00
|
|
|
}
|
2024-05-24 00:45:04 +01:00
|
|
|
|
2024-03-04 02:30:11 +00:00
|
|
|
const baseChance = this.egg.gachaType === GachaType.MOVE ? 3 : 6;
|
|
|
|
this.eggMoveIndex = Utils.randSeedInt(baseChance * Math.pow(2, 3 - this.egg.tier))
|
2024-03-03 04:24:28 +00:00
|
|
|
? Utils.randSeedInt(3)
|
|
|
|
: 3;
|
2024-03-04 02:30:11 +00:00
|
|
|
|
2024-02-25 17:45:41 +00:00
|
|
|
}, this.egg.id, EGG_SEED.toString());
|
2024-05-24 00:45:04 +01:00
|
|
|
|
2023-12-20 04:51:48 +00:00
|
|
|
return ret;
|
2023-12-16 04:07:32 +00:00
|
|
|
}
|
2024-05-23 16:03:10 +01:00
|
|
|
}
|