2023-12-19 23:51:48 -05:00
|
|
|
import BattleScene from "../battle-scene";
|
2024-06-09 10:45:21 -06:00
|
|
|
import PokemonSpecies, { getPokemonSpecies, speciesStarters } from "./pokemon-species";
|
2024-06-22 02:19:56 +02:00
|
|
|
import { VariantTier } from "../enums/variant-tiers";
|
|
|
|
import * as Utils from "../utils";
|
|
|
|
import * as Overrides from "../overrides";
|
|
|
|
import { pokemonPrevolutions } from "./pokemon-evolutions";
|
|
|
|
import { PlayerPokemon } from "#app/field/pokemon";
|
2024-06-17 17:05:33 -04:00
|
|
|
import i18next from "i18next";
|
2024-06-13 18:44:23 -04:00
|
|
|
import { EggTier } from "#enums/egg-type";
|
|
|
|
import { Species } from "#enums/species";
|
2024-06-22 02:19:56 +02:00
|
|
|
import { EggSourceType } from "#app/enums/egg-source-types.js";
|
2023-12-15 23:07:32 -05:00
|
|
|
|
|
|
|
export const EGG_SEED = 1073741824;
|
|
|
|
|
2024-06-22 02:19:56 +02:00
|
|
|
// Rates for specific random properties in 1/x
|
|
|
|
const DEFAULT_SHINY_RATE = 128;
|
|
|
|
const GACHA_SHINY_UP_SHINY_RATE = 64;
|
|
|
|
const SAME_SPECIES_EGG_SHINY_RATE = 32;
|
|
|
|
const SAME_SPECIES_EGG_HA_RATE = 16;
|
|
|
|
const MANAPHY_EGG_MANAPHY_RATE = 8;
|
|
|
|
|
|
|
|
// 1/x for legendary eggs, 1/x*2 for epic eggs, 1/x*4 for rare eggs, and 1/x*8 for common eggs
|
|
|
|
const DEFAULT_RARE_EGGMOVE_RATE = 6;
|
|
|
|
const SAME_SPECIES_EGG_RARE_EGGMOVE_RATE = 3;
|
|
|
|
const GACHA_MOVE_UP_RARE_EGGMOVE_RATE = 3;
|
|
|
|
|
|
|
|
/** Egg options to override egg properties */
|
|
|
|
export interface IEggOptions {
|
|
|
|
/** Id. Used to check if egg type will be manaphy (id % 204 === 0) */
|
|
|
|
id?: number;
|
|
|
|
/** Timestamp when this egg got created */
|
|
|
|
timestamp?: number;
|
|
|
|
/** Defines if the egg got pulled from a gacha or not. If true, egg pity and pull statistics will be applyed.
|
|
|
|
* Egg will be automaticly added to the game data.
|
|
|
|
* NEEDS scene eggOption to work.
|
|
|
|
*/
|
|
|
|
pulled?: boolean;
|
|
|
|
/** Defines where the egg comes from. Applies specific modifiers.
|
|
|
|
* Will also define the text displayed in the egg list.
|
|
|
|
*/
|
|
|
|
sourceType?: EggSourceType;
|
|
|
|
/** Needs to be defined if eggOption pulled is defined or if no species or isShiny is degined since this will be needed to generate them. */
|
|
|
|
scene?: BattleScene;
|
|
|
|
/** Sets the tier of the egg. Only species of this tier can be hatched from this egg.
|
|
|
|
* Tier will be overriden if species eggOption is set.
|
|
|
|
*/
|
|
|
|
tier?: EggTier;
|
|
|
|
/** Sets how many waves it will take till this egg hatches. */
|
|
|
|
hatchWaves?: number;
|
|
|
|
/** Sets the exact species that will hatch from this egg.
|
|
|
|
* Needs scene eggOption if not provided.
|
|
|
|
*/
|
|
|
|
species?: Species;
|
|
|
|
/** Defines if the hatched pokemon will be a shiny. */
|
|
|
|
isShiny?: boolean;
|
|
|
|
/** Defines the variant of the pokemon that will hatch from this egg. If no variantTier is given the normal variant rates will apply. */
|
|
|
|
variantTier?: VariantTier;
|
|
|
|
/** Defines which egg move will be unlocked. 3 = rare egg move. */
|
|
|
|
eggMoveIndex?: number;
|
|
|
|
/** Defines if the egg will hatch with the hidden ability of this species.
|
|
|
|
* If no hidden ability exist, a random one will get choosen.
|
|
|
|
*/
|
|
|
|
overrideHiddenAbility?: boolean
|
2023-12-15 23:07:32 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
export class Egg {
|
|
|
|
|
2024-06-22 02:19:56 +02:00
|
|
|
////
|
|
|
|
// #region Privat properties
|
|
|
|
////
|
|
|
|
|
|
|
|
private _id: number;
|
|
|
|
private _tier: EggTier;
|
|
|
|
private _sourceType: EggSourceType | undefined;
|
|
|
|
private _hatchWaves: number;
|
|
|
|
private _timestamp: number;
|
|
|
|
|
|
|
|
private _species: Species;
|
|
|
|
private _isShiny: boolean;
|
|
|
|
private _variantTier: VariantTier;
|
|
|
|
private _eggMoveIndex: number;
|
|
|
|
|
|
|
|
private _overrideHiddenAbility: boolean;
|
|
|
|
|
|
|
|
////
|
|
|
|
// #endregion
|
|
|
|
////
|
|
|
|
|
|
|
|
////
|
|
|
|
// #region Public facing properties
|
|
|
|
////
|
|
|
|
get id(): number {
|
|
|
|
return this._id;
|
|
|
|
}
|
|
|
|
|
|
|
|
get tier(): EggTier {
|
|
|
|
return this._tier;
|
|
|
|
}
|
|
|
|
|
|
|
|
get sourceType(): EggSourceType | undefined {
|
|
|
|
return this._sourceType;
|
|
|
|
}
|
|
|
|
|
|
|
|
get hatchWaves(): number {
|
|
|
|
return this._hatchWaves;
|
|
|
|
}
|
|
|
|
|
|
|
|
set hatchWaves(value: number) {
|
|
|
|
this._hatchWaves = value;
|
2023-12-15 23:07:32 -05:00
|
|
|
}
|
2023-12-19 23:51:48 -05:00
|
|
|
|
2024-06-22 02:19:56 +02:00
|
|
|
get timestamp(): number {
|
|
|
|
return this._timestamp;
|
2023-12-19 23:51:48 -05:00
|
|
|
}
|
|
|
|
|
2024-06-22 02:19:56 +02:00
|
|
|
get species(): Species {
|
|
|
|
return this._species;
|
|
|
|
}
|
|
|
|
|
|
|
|
get isShiny(): boolean {
|
|
|
|
return this._isShiny;
|
|
|
|
}
|
|
|
|
|
|
|
|
get variantTier(): VariantTier {
|
|
|
|
return this._variantTier;
|
|
|
|
}
|
|
|
|
|
|
|
|
get eggMoveIndex(): number {
|
|
|
|
return this._eggMoveIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
get overrideHiddenAbility(): boolean {
|
|
|
|
return this._overrideHiddenAbility;
|
|
|
|
}
|
|
|
|
|
|
|
|
////
|
|
|
|
// #endregion
|
|
|
|
////
|
|
|
|
|
|
|
|
constructor(eggOptions?: IEggOptions) {
|
|
|
|
//if (eggOptions.tier && eggOptions.species) throw Error("Error egg can't have species and tier as option. only choose one of them.")
|
|
|
|
|
2024-06-23 18:40:37 +02:00
|
|
|
this._sourceType = eggOptions.sourceType ?? undefined;
|
2024-06-25 23:14:28 +09:00
|
|
|
// Ensure _sourceType is defined before invoking rollEggTier(), as it is referenced
|
|
|
|
this._tier = eggOptions.tier ?? (Overrides.EGG_TIER_OVERRIDE ?? this.rollEggTier());
|
2024-06-22 12:40:10 +02:00
|
|
|
// If egg was pulled, check if egg pity needs to override the egg tier
|
2024-06-22 02:19:56 +02:00
|
|
|
if (eggOptions.pulled) {
|
2024-06-23 18:40:37 +02:00
|
|
|
// Needs this._tier and this._sourceType to work
|
2024-06-22 02:19:56 +02:00
|
|
|
this.checkForPityTierOverrides(eggOptions.scene);
|
|
|
|
}
|
|
|
|
|
|
|
|
this._id = eggOptions.id ?? Utils.randInt(EGG_SEED, EGG_SEED * this._tier);
|
2024-06-22 12:40:10 +02:00
|
|
|
|
2024-06-22 02:19:56 +02:00
|
|
|
this._sourceType = eggOptions.sourceType ?? undefined;
|
|
|
|
this._hatchWaves = eggOptions.hatchWaves ?? this.getEggTierDefaultHatchWaves();
|
|
|
|
this._timestamp = eggOptions.timestamp ?? new Date().getTime();
|
|
|
|
|
|
|
|
// First roll shiny and variant so we can filter if species with an variant exist
|
|
|
|
this._isShiny = eggOptions.isShiny ?? (Overrides.EGG_SHINY_OVERRIDE || this.rollShiny());
|
|
|
|
this._variantTier = eggOptions.variantTier ?? (Overrides.EGG_VARIANT_OVERRIDE ?? this.rollVariant());
|
|
|
|
this._species = eggOptions.species ?? this.rollSpecies(eggOptions.scene);
|
|
|
|
|
|
|
|
this._overrideHiddenAbility = eggOptions.overrideHiddenAbility ?? false;
|
|
|
|
|
|
|
|
// Override egg tier and hatchwaves if species was given
|
|
|
|
if (eggOptions.species) {
|
|
|
|
this._tier = this.getEggTierFromSpeciesStarterValue();
|
|
|
|
this._hatchWaves = eggOptions.hatchWaves ?? this.getEggTierDefaultHatchWaves();
|
2024-06-22 12:40:10 +02:00
|
|
|
// If species has no variant, set variantTier to common. This needs to
|
|
|
|
// be done because species with no variants get filtered at rollSpecies but since the
|
|
|
|
// species is set the check never happens
|
|
|
|
if (!getPokemonSpecies(this.species).hasVariants()) {
|
|
|
|
this._variantTier = VariantTier.COMMON;
|
|
|
|
}
|
2024-06-22 02:19:56 +02:00
|
|
|
}
|
2024-06-22 14:28:10 +02:00
|
|
|
// Needs this._tier so it needs to be generated afer the tier override if bought from same species
|
|
|
|
this._eggMoveIndex = eggOptions.eggMoveIndex ?? this.rollEggMoveIndex();
|
2024-06-22 02:19:56 +02:00
|
|
|
if (eggOptions.pulled) {
|
2024-06-23 18:40:37 +02:00
|
|
|
this.increasePullStatistic(eggOptions.scene);
|
2024-06-22 02:19:56 +02:00
|
|
|
this.addEggToGameData(eggOptions.scene);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
////
|
|
|
|
// #region Public methodes
|
|
|
|
////
|
|
|
|
|
|
|
|
public isManaphyEgg(): boolean {
|
|
|
|
return (this._species === Species.PHIONE || this._species === Species.MANAPHY) ||
|
2024-06-22 12:40:10 +02:00
|
|
|
this._tier === EggTier.COMMON && !(this._id % 204) && !this._species;
|
2024-06-22 02:19:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public getKey(): string {
|
2024-05-23 17:03:10 +02:00
|
|
|
if (this.isManaphyEgg()) {
|
|
|
|
return "manaphy";
|
|
|
|
}
|
2024-06-22 02:19:56 +02:00
|
|
|
return this._tier.toString();
|
2023-12-19 23:51:48 -05:00
|
|
|
}
|
|
|
|
|
2024-06-22 02:19:56 +02:00
|
|
|
// Generates a PlayerPokemon from an egg
|
|
|
|
public generatePlayerPokemon(scene: BattleScene): PlayerPokemon {
|
|
|
|
// Legacy egg wants to hatch. Generate missing properties
|
|
|
|
if (!this._species) {
|
|
|
|
this._isShiny = this.rollShiny();
|
|
|
|
this._species = this.rollSpecies(scene);
|
|
|
|
}
|
|
|
|
|
|
|
|
const pokemonSpecies = getPokemonSpecies(this._species);
|
|
|
|
|
|
|
|
// Sets the hidden ability if a hidden ability exists and the override is set
|
|
|
|
// or if the same species egg hits the chance
|
|
|
|
let abilityIndex = undefined;
|
|
|
|
if (pokemonSpecies.abilityHidden && (this._overrideHiddenAbility
|
|
|
|
|| (this._sourceType === EggSourceType.SAME_SPECIES_EGG && !Utils.randSeedInt(SAME_SPECIES_EGG_HA_RATE)))) {
|
|
|
|
abilityIndex = pokemonSpecies.ability2 ? 2 : 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This function has way to many optional parameters
|
|
|
|
const ret: PlayerPokemon = scene.addPlayerPokemon(pokemonSpecies, 1, abilityIndex, undefined, undefined, false);
|
|
|
|
ret.shiny = this._isShiny;
|
|
|
|
ret.variant = this._variantTier;
|
|
|
|
|
|
|
|
const secondaryIvs = Utils.getIvsFromId(Utils.randSeedInt(4294967295));
|
|
|
|
|
|
|
|
for (let s = 0; s < ret.ivs.length; s++) {
|
|
|
|
ret.ivs[s] = Math.max(ret.ivs[s], secondaryIvs[s]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2023-12-19 23:51:48 -05:00
|
|
|
}
|
|
|
|
|
2024-06-22 02:19:56 +02:00
|
|
|
// Doesn't need to be called if the egg got pulled by a gacha machiene
|
|
|
|
public addEggToGameData(scene: BattleScene): void {
|
|
|
|
scene.gameData.eggs.push(this);
|
2024-05-23 17:03:10 +02:00
|
|
|
}
|
2024-06-22 02:19:56 +02:00
|
|
|
|
|
|
|
public getEggDescriptor(): string {
|
|
|
|
if (this.isManaphyEgg()) {
|
|
|
|
return "Manaphy";
|
|
|
|
}
|
|
|
|
switch (this.tier) {
|
|
|
|
case EggTier.GREAT:
|
|
|
|
return i18next.t("egg:greatTier");
|
|
|
|
case EggTier.ULTRA:
|
|
|
|
return i18next.t("egg:ultraTier");
|
|
|
|
case EggTier.MASTER:
|
|
|
|
return i18next.t("egg:masterTier");
|
|
|
|
default:
|
|
|
|
return i18next.t("egg:defaultTier");
|
|
|
|
}
|
2023-12-19 23:51:48 -05:00
|
|
|
}
|
|
|
|
|
2024-06-22 02:19:56 +02:00
|
|
|
public getEggHatchWavesMessage(): string {
|
|
|
|
if (this.hatchWaves <= 5) {
|
|
|
|
return i18next.t("egg:hatchWavesMessageSoon");
|
|
|
|
}
|
|
|
|
if (this.hatchWaves <= 15) {
|
|
|
|
return i18next.t("egg:hatchWavesMessageClose");
|
|
|
|
}
|
|
|
|
if (this.hatchWaves <= 50) {
|
|
|
|
return i18next.t("egg:hatchWavesMessageNotClose");
|
|
|
|
}
|
|
|
|
return i18next.t("egg:hatchWavesMessageLongTime");
|
2024-05-23 17:03:10 +02:00
|
|
|
}
|
2024-06-22 02:19:56 +02:00
|
|
|
|
|
|
|
public getEggTypeDescriptor(scene: BattleScene): string {
|
|
|
|
switch (this.sourceType) {
|
2024-06-21 21:26:42 -04:00
|
|
|
case EggSourceType.SAME_SPECIES_EGG:
|
|
|
|
return i18next.t("egg:sameSpeciesEgg", { species: getPokemonSpecies(this._species).getName()});
|
2024-06-22 02:19:56 +02:00
|
|
|
case EggSourceType.GACHA_LEGENDARY:
|
|
|
|
return `${i18next.t("egg:gachaTypeLegendary")} (${getPokemonSpecies(getLegendaryGachaSpeciesForTimestamp(scene, this.timestamp)).getName()})`;
|
|
|
|
case EggSourceType.GACHA_SHINY:
|
|
|
|
return i18next.t("egg:gachaTypeShiny");
|
2024-06-21 21:26:42 -04:00
|
|
|
case EggSourceType.GACHA_MOVE:
|
|
|
|
return i18next.t("egg:gachaTypeMove");
|
2024-06-22 02:19:56 +02:00
|
|
|
}
|
2024-05-23 17:03:10 +02:00
|
|
|
}
|
2024-06-22 02:19:56 +02:00
|
|
|
|
|
|
|
////
|
|
|
|
// #endregion
|
|
|
|
////
|
|
|
|
|
|
|
|
////
|
|
|
|
// #region Private methodes
|
|
|
|
////
|
|
|
|
|
|
|
|
private rollEggMoveIndex() {
|
|
|
|
let baseChance = DEFAULT_RARE_EGGMOVE_RATE;
|
|
|
|
switch (this._sourceType) {
|
|
|
|
case EggSourceType.SAME_SPECIES_EGG:
|
|
|
|
baseChance = SAME_SPECIES_EGG_RARE_EGGMOVE_RATE;
|
|
|
|
break;
|
2024-06-21 21:26:42 -04:00
|
|
|
case EggSourceType.GACHA_MOVE:
|
|
|
|
baseChance = GACHA_MOVE_UP_RARE_EGGMOVE_RATE;
|
|
|
|
break;
|
2024-06-22 02:19:56 +02:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Utils.randSeedInt(baseChance * Math.pow(2, 3 - this.tier)) ? Utils.randSeedInt(3) : 3;
|
2024-05-23 17:03:10 +02:00
|
|
|
}
|
2023-12-19 23:51:48 -05:00
|
|
|
|
2024-06-22 02:19:56 +02:00
|
|
|
private getEggTierDefaultHatchWaves(eggTier?: EggTier): number {
|
|
|
|
if (this._species === Species.PHIONE || this._species === Species.MANAPHY) {
|
|
|
|
return 50;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (eggTier ?? this._tier) {
|
|
|
|
case EggTier.COMMON:
|
|
|
|
return 10;
|
|
|
|
case EggTier.GREAT:
|
|
|
|
return 25;
|
|
|
|
case EggTier.ULTRA:
|
|
|
|
return 50;
|
|
|
|
}
|
|
|
|
return 100;
|
|
|
|
}
|
|
|
|
|
|
|
|
private rollEggTier(): EggTier {
|
|
|
|
const tierValueOffset = this._sourceType === EggSourceType.GACHA_LEGENDARY ? 1 : 0;
|
|
|
|
const tierValue = Utils.randInt(256);
|
|
|
|
return tierValue >= 52 + tierValueOffset ? EggTier.COMMON : tierValue >= 8 + tierValueOffset ? EggTier.GREAT : tierValue >= 1 + tierValueOffset ? EggTier.ULTRA : EggTier.MASTER;
|
|
|
|
}
|
|
|
|
|
|
|
|
private rollSpecies(scene: BattleScene): Species {
|
|
|
|
if (!scene) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
if (this.isManaphyEgg()) {
|
|
|
|
const rand = Utils.randSeedInt(MANAPHY_EGG_MANAPHY_RATE);
|
|
|
|
return rand ? Species.PHIONE : Species.MANAPHY;
|
|
|
|
} else if (this.tier === EggTier.MASTER
|
|
|
|
&& this._sourceType === EggSourceType.GACHA_LEGENDARY) {
|
|
|
|
if (!Utils.randSeedInt(2)) {
|
|
|
|
return getLegendaryGachaSpeciesForTimestamp(scene, this.timestamp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let minStarterValue: integer;
|
|
|
|
let maxStarterValue: integer;
|
|
|
|
|
|
|
|
switch (this.tier) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
const ignoredSpecies = [Species.PHIONE, Species.MANAPHY, Species.ETERNATUS];
|
|
|
|
|
|
|
|
let speciesPool = Object.keys(speciesStarters)
|
|
|
|
.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);
|
|
|
|
|
|
|
|
// If this is the 10th egg without unlocking something new, attempt to force it.
|
|
|
|
if (scene.gameData.unlockPity[this.tier] >= 9) {
|
|
|
|
const lockedPool = speciesPool.filter(s => !scene.gameData.dexData[s].caughtAttr);
|
|
|
|
if (lockedPool.length) { // Skip this if everything is unlocked
|
|
|
|
speciesPool = lockedPool;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If egg variant is set to RARE or EPIC, filter species pool to only include ones with variants.
|
|
|
|
if (this.variantTier && (this.variantTier === VariantTier.RARE || this.variantTier === VariantTier.EPIC)) {
|
|
|
|
speciesPool = speciesPool.filter(s => getPokemonSpecies(s).hasVariants());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
let totalWeight = 0;
|
|
|
|
const speciesWeights = [];
|
|
|
|
for (const speciesId of speciesPool) {
|
|
|
|
let weight = Math.floor((((maxStarterValue - speciesStarters[speciesId]) / ((maxStarterValue - minStarterValue) + 1)) * 1.5 + 1) * 100);
|
|
|
|
const species = getPokemonSpecies(speciesId);
|
|
|
|
if (species.isRegional()) {
|
|
|
|
weight = Math.floor(weight / (species.isRareRegional() ? 8 : 2));
|
|
|
|
}
|
|
|
|
speciesWeights.push(totalWeight + weight);
|
|
|
|
totalWeight += weight;
|
|
|
|
}
|
|
|
|
|
|
|
|
let species: Species;
|
|
|
|
|
|
|
|
const rand = Utils.randSeedInt(totalWeight);
|
|
|
|
for (let s = 0; s < speciesWeights.length; s++) {
|
|
|
|
if (rand < speciesWeights[s]) {
|
|
|
|
species = speciesPool[s];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!!scene.gameData.dexData[species].caughtAttr) {
|
|
|
|
scene.gameData.unlockPity[this.tier] = Math.min(scene.gameData.unlockPity[this.tier] + 1, 10);
|
|
|
|
} else {
|
|
|
|
scene.gameData.unlockPity[this.tier] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return species;
|
2023-12-19 23:51:48 -05:00
|
|
|
}
|
2024-06-22 02:19:56 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Rolls whether the egg is shiny or not.
|
|
|
|
* @returns True if the egg is shiny
|
|
|
|
**/
|
|
|
|
private rollShiny(): boolean {
|
|
|
|
let shinyChance = DEFAULT_SHINY_RATE;
|
|
|
|
switch (this._sourceType) {
|
|
|
|
case EggSourceType.GACHA_SHINY:
|
|
|
|
shinyChance = GACHA_SHINY_UP_SHINY_RATE;
|
|
|
|
break;
|
|
|
|
case EggSourceType.SAME_SPECIES_EGG:
|
|
|
|
shinyChance = SAME_SPECIES_EGG_SHINY_RATE;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return !Utils.randSeedInt(shinyChance);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Uses the same logic as pokemon.generateVariant(). I would like to only have this logic in one
|
|
|
|
// place but I don't want to touch the pokemon class.
|
|
|
|
private rollVariant(): VariantTier {
|
|
|
|
if (!this.isShiny) {
|
|
|
|
return VariantTier.COMMON;
|
|
|
|
}
|
|
|
|
|
|
|
|
const rand = Utils.randSeedInt(10);
|
|
|
|
if (rand >= 4) {
|
|
|
|
return VariantTier.COMMON; // 6/10
|
|
|
|
} else if (rand >= 1) {
|
|
|
|
return VariantTier.RARE; // 3/10
|
|
|
|
} else {
|
|
|
|
return VariantTier.EPIC; // 1/10
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private checkForPityTierOverrides(scene: BattleScene): void {
|
2024-06-22 18:53:02 +02:00
|
|
|
const tierValueOffset = this._sourceType === EggSourceType.GACHA_LEGENDARY ? 1 : 0;
|
2024-06-22 02:19:56 +02:00
|
|
|
scene.gameData.eggPity[EggTier.GREAT] += 1;
|
|
|
|
scene.gameData.eggPity[EggTier.ULTRA] += 1;
|
2024-06-22 18:53:02 +02:00
|
|
|
scene.gameData.eggPity[EggTier.MASTER] += 1 + tierValueOffset;
|
2024-06-22 02:19:56 +02:00
|
|
|
// These numbers are roughly the 80% mark. That is, 80% of the time you'll get an egg before this gets triggered.
|
|
|
|
if (scene.gameData.eggPity[EggTier.MASTER] >= 412 && this._tier === EggTier.COMMON) {
|
|
|
|
this._tier = EggTier.MASTER;
|
|
|
|
} else if (scene.gameData.eggPity[EggTier.ULTRA] >= 59 && this._tier === EggTier.COMMON) {
|
|
|
|
this._tier = EggTier.ULTRA;
|
|
|
|
} else if (scene.gameData.eggPity[EggTier.GREAT] >= 9 && this._tier === EggTier.COMMON) {
|
|
|
|
this._tier = EggTier.GREAT;
|
|
|
|
}
|
|
|
|
scene.gameData.eggPity[this._tier] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
private increasePullStatistic(scene: BattleScene): void {
|
|
|
|
scene.gameData.gameStats.eggsPulled++;
|
|
|
|
if (this.isManaphyEgg()) {
|
|
|
|
scene.gameData.gameStats.manaphyEggsPulled++;
|
|
|
|
this._hatchWaves = this.getEggTierDefaultHatchWaves(EggTier.ULTRA);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
switch (this.tier) {
|
|
|
|
case EggTier.GREAT:
|
|
|
|
scene.gameData.gameStats.rareEggsPulled++;
|
|
|
|
break;
|
|
|
|
case EggTier.ULTRA:
|
|
|
|
scene.gameData.gameStats.epicEggsPulled++;
|
|
|
|
break;
|
|
|
|
case EggTier.MASTER:
|
|
|
|
scene.gameData.gameStats.legendaryEggsPulled++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private getEggTierFromSpeciesStarterValue(): EggTier {
|
|
|
|
const speciesStartValue = speciesStarters[this.species];
|
|
|
|
if (speciesStartValue >= 1 && speciesStartValue <= 3) {
|
|
|
|
return EggTier.COMMON;
|
|
|
|
}
|
|
|
|
if (speciesStartValue >= 4 && speciesStartValue <= 5) {
|
|
|
|
return EggTier.GREAT;
|
|
|
|
}
|
|
|
|
if (speciesStartValue >= 6 && speciesStartValue <= 7) {
|
|
|
|
return EggTier.ULTRA;
|
|
|
|
}
|
|
|
|
if (speciesStartValue >= 8) {
|
|
|
|
return EggTier.MASTER;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
////
|
|
|
|
// #endregion
|
|
|
|
////
|
2023-12-19 23:51:48 -05:00
|
|
|
}
|
|
|
|
|
2024-06-22 02:19:56 +02:00
|
|
|
export function getLegendaryGachaSpeciesForTimestamp(scene: BattleScene, timestamp: number): Species {
|
2023-12-19 23:51:48 -05:00
|
|
|
const legendarySpecies = Object.entries(speciesStarters)
|
|
|
|
.filter(s => s[1] >= 8 && s[1] <= 9)
|
2023-12-30 15:58:41 -05:00
|
|
|
.map(s => parseInt(s[0]))
|
|
|
|
.filter(s => getPokemonSpecies(s).isObtainable());
|
2023-12-19 23:51:48 -05:00
|
|
|
|
|
|
|
let ret: Species;
|
|
|
|
|
2024-05-15 01:34:40 +10:00
|
|
|
// 86400000 is the number of miliseconds in one day
|
|
|
|
const timeDate = new Date(timestamp);
|
|
|
|
const dayTimestamp = timeDate.getTime(); // Timestamp of current week
|
|
|
|
const offset = Math.floor(Math.floor(dayTimestamp / 86400000) / legendarySpecies.length); // Cycle number
|
2024-05-23 17:03:10 +02:00
|
|
|
const index = Math.floor(dayTimestamp / 86400000) % legendarySpecies.length; // Index within cycle
|
2024-05-15 01:34:40 +10:00
|
|
|
|
2023-12-19 23:51:48 -05:00
|
|
|
scene.executeWithSeedOffset(() => {
|
2024-05-15 01:34:40 +10:00
|
|
|
ret = Phaser.Math.RND.shuffle(legendarySpecies)[index];
|
|
|
|
}, offset, EGG_SEED.toString());
|
2023-12-19 23:51:48 -05:00
|
|
|
|
|
|
|
return ret;
|
2024-05-23 17:03:10 +02:00
|
|
|
}
|
2024-06-09 10:45:21 -06:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Check for a given species EggTier Value
|
|
|
|
* @param species - Species for wich we will check the egg tier it belongs to
|
|
|
|
* @returns The egg tier of a given pokemon species
|
|
|
|
*/
|
|
|
|
export function getEggTierForSpecies(pokemonSpecies :PokemonSpecies): EggTier {
|
|
|
|
const speciesBaseValue = speciesStarters[pokemonSpecies.getRootSpeciesId()];
|
|
|
|
if (speciesBaseValue <= 3) {
|
|
|
|
return EggTier.COMMON;
|
|
|
|
} else if (speciesBaseValue <= 5) {
|
|
|
|
return EggTier.GREAT;
|
|
|
|
} else if (speciesBaseValue <= 7) {
|
|
|
|
return EggTier.ULTRA;
|
|
|
|
}
|
|
|
|
return EggTier.MASTER;
|
|
|
|
}
|