Add egg gacha system and various minor changes

Add egg gacha system; remove certain mythical Pokemon from the wild pool as egg exclusive; add egg vouchers with UI; rework Shiny Charm odds; fix trainer Pokemon shiny odds not properly ignoring Shiny Charm modifier
This commit is contained in:
Flashfyre 2023-12-19 23:51:48 -05:00
parent 50d4858caa
commit aa94e044ab
71 changed files with 4303 additions and 2241 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,6 +1,6 @@
{
"id": 383,
"graphic": "'!",
"graphic": "!",
"frames": [
[
{

Binary file not shown.

Before

Width:  |  Height:  |  Size: 951 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 337 B

View File

@ -4,7 +4,7 @@
"image": "egg.png",
"format": "RGBA8888",
"size": {
"w": 112,
"w": 138,
"h": 30
},
"scale": 1,
@ -12,14 +12,14 @@
{
"filename": "egg_0",
"rotated": false,
"trimmed": true,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 32
"w": 28,
"h": 30
},
"spriteSourceSize": {
"x": 2,
"y": 1,
"x": 0,
"y": 0,
"w": 28,
"h": 30
},
@ -33,14 +33,14 @@
{
"filename": "egg_1",
"rotated": false,
"trimmed": true,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 32
"w": 28,
"h": 30
},
"spriteSourceSize": {
"x": 2,
"y": 1,
"x": 0,
"y": 0,
"w": 28,
"h": 30
},
@ -54,14 +54,14 @@
{
"filename": "egg_2",
"rotated": false,
"trimmed": true,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 32
"w": 28,
"h": 30
},
"spriteSourceSize": {
"x": 2,
"y": 1,
"x": 0,
"y": 0,
"w": 28,
"h": 30
},
@ -75,14 +75,14 @@
{
"filename": "egg_3",
"rotated": false,
"trimmed": true,
"trimmed": false,
"sourceSize": {
"w": 32,
"h": 32
"w": 28,
"h": 30
},
"spriteSourceSize": {
"x": 2,
"y": 1,
"x": 0,
"y": 0,
"w": 28,
"h": 30
},
@ -92,6 +92,27 @@
"w": 28,
"h": 30
}
},
{
"filename": "egg_manaphy",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 26,
"h": 30
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 26,
"h": 30
},
"frame": {
"x": 112,
"y": 0,
"w": 26,
"h": 30
}
}
]
}
@ -99,6 +120,6 @@
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:a2a114a3e275355f11c124f7ddc3a158:87e6ddecd2221fa223d5a07b1b3bb040:f2ac48b1c7b5b0a41ac50c4888a029cf$"
"smartupdate": "$TexturePacker:SmartUpdate:ba653f2a1c1d028d27d209346c8f5cda:d1a96146bbb57b7f2dbd12209e9e846d:f2ac48b1c7b5b0a41ac50c4888a029cf$"
}
}

BIN
public/images/egg/egg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 304 B

After

Width:  |  Height:  |  Size: 304 B

View File

@ -0,0 +1,125 @@
{
"textures": [
{
"image": "egg_icons.png",
"format": "RGBA8888",
"size": {
"w": 64,
"h": 15
},
"scale": 1,
"frames": [
{
"filename": "0",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 40,
"h": 30
},
"spriteSourceSize": {
"x": 14,
"y": 14,
"w": 13,
"h": 14
},
"frame": {
"x": 0,
"y": 0,
"w": 13,
"h": 14
}
},
{
"filename": "1",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 40,
"h": 30
},
"spriteSourceSize": {
"x": 14,
"y": 14,
"w": 13,
"h": 14
},
"frame": {
"x": 13,
"y": 0,
"w": 13,
"h": 14
}
},
{
"filename": "2",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 40,
"h": 30
},
"spriteSourceSize": {
"x": 14,
"y": 14,
"w": 13,
"h": 14
},
"frame": {
"x": 26,
"y": 0,
"w": 13,
"h": 14
}
},
{
"filename": "3",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 40,
"h": 30
},
"spriteSourceSize": {
"x": 14,
"y": 14,
"w": 13,
"h": 14
},
"frame": {
"x": 39,
"y": 0,
"w": 13,
"h": 14
}
},
{
"filename": "manaphy",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 40,
"h": 30
},
"spriteSourceSize": {
"x": 14,
"y": 13,
"w": 12,
"h": 15
},
"frame": {
"x": 52,
"y": 0,
"w": 12,
"h": 15
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:5bdeccfea3b20316b6f997ded4917832:9d7fd6a01652d18587263aea89256028:9ca79c86befa783c0f0fdd62dae4f950$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 559 B

View File

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

View File

Before

Width:  |  Height:  |  Size: 402 B

After

Width:  |  Height:  |  Size: 402 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 562 B

View File

@ -0,0 +1,104 @@
{
"textures": [
{
"image": "gacha_hatch.png",
"format": "RGBA8888",
"size": {
"w": 53,
"h": 53
},
"scale": 1,
"frames": [
{
"filename": "1.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 25,
"h": 32
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 25,
"h": 32
},
"frame": {
"x": 0,
"y": 0,
"w": 25,
"h": 32
}
},
{
"filename": "2.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 25,
"h": 32
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 25,
"h": 31
},
"frame": {
"x": 25,
"y": 0,
"w": 25,
"h": 31
}
},
{
"filename": "3.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 25,
"h": 32
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 25,
"h": 22
},
"frame": {
"x": 25,
"y": 31,
"w": 25,
"h": 22
}
},
{
"filename": "4.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 25,
"h": 32
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 25,
"h": 12
},
"frame": {
"x": 0,
"y": 32,
"w": 25,
"h": 12
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:dba061ae0c944cad946da89a3db61ef3:6bcb3cebd91542179a70caca502090cd:03d3c0cdbe3979a45ab948bf5fc6c5fe$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 433 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 279 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,62 @@
{
"textures": [
{
"image": "gacha_underlay_legendary.png",
"format": "RGBA8888",
"size": {
"w": 25,
"h": 104
},
"scale": 1,
"frames": [
{
"filename": "default",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 25,
"h": 52
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 25,
"h": 52
},
"frame": {
"x": 0,
"y": 0,
"w": 25,
"h": 52
}
},
{
"filename": "open_hatch",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 25,
"h": 52
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 25,
"h": 52
},
"frame": {
"x": 0,
"y": 52,
"w": 25,
"h": 52
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:2714c584c99d3f151df83633a3e573e2:9a5a390cb558062cd2b0d8b9e577816a:dc9ad86988e4cd7eb47a54564121d8fa$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 B

View File

@ -0,0 +1,62 @@
{
"textures": [
{
"image": "gacha_underlay_shiny.png",
"format": "RGBA8888",
"size": {
"w": 25,
"h": 104
},
"scale": 1,
"frames": [
{
"filename": "default",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 25,
"h": 52
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 25,
"h": 52
},
"frame": {
"x": 0,
"y": 0,
"w": 25,
"h": 52
}
},
{
"filename": "open_hatch",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 25,
"h": 52
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 25,
"h": 52
},
"frame": {
"x": 0,
"y": 52,
"w": 25,
"h": 52
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:2714c584c99d3f151df83633a3e573e2:9a5a390cb558062cd2b0d8b9e577816a:dc9ad86988e4cd7eb47a54564121d8fa$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 420 B

View File

@ -0,0 +1,62 @@
{
"textures": [
{
"image": "gacha_underlay_type.png",
"format": "RGBA8888",
"size": {
"w": 25,
"h": 104
},
"scale": 1,
"frames": [
{
"filename": "default",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 25,
"h": 52
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 25,
"h": 52
},
"frame": {
"x": 0,
"y": 0,
"w": 25,
"h": 52
}
},
{
"filename": "open_hatch",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 25,
"h": 52
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 25,
"h": 52
},
"frame": {
"x": 0,
"y": 52,
"w": 25,
"h": 52
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:2714c584c99d3f151df83633a3e573e2:9a5a390cb558062cd2b0d8b9e577816a:dc9ad86988e4cd7eb47a54564121d8fa$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 B

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 261 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 333 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 333 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 358 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -34,7 +34,10 @@ import { Species } from "./data/species";
import { HealAchv, LevelAchv, MoneyAchv, achvs } from "./system/achv";
import { DexEntry } from "./system/game-data";
import { pokemonPrevolutions } from "./data/pokemon-evolutions";
import { trainerConfigs } from "./data/trainer-type";
import { TrainerType, trainerConfigs } from "./data/trainer-type";
import { EggHatchPhase } from "./egg-hatch-phase";
import { Egg } from "./data/egg";
import { vouchers } from "./system/voucher";
export class CheckLoadPhase extends BattlePhase {
private loaded: boolean;
@ -92,6 +95,11 @@ export class CheckLoadPhase extends BattlePhase {
}
}
for (let achv of Object.keys(this.scene.gameData.achvUnlocks)) {
if (vouchers.hasOwnProperty(achv))
this.scene.validateVoucher(vouchers[achv]);
}
super.end();
}
}
@ -2338,6 +2346,7 @@ export class VictoryPhase extends PokemonPhase {
this.scene.pushPhase(new BattleEndPhase(this.scene));
if (this.scene.currentBattle.battleType === BattleType.TRAINER)
this.scene.pushPhase(new TrainerVictoryPhase(this.scene));
this.scene.pushPhase(new EggLapsePhase(this.scene));
if (this.scene.gameMode !== GameMode.CLASSIC || this.scene.currentBattle.waveIndex < this.scene.finalWave) {
if (this.scene.currentBattle.waveIndex % 10)
this.scene.pushPhase(new SelectModifierPhase(this.scene));
@ -2347,8 +2356,10 @@ export class VictoryPhase extends PokemonPhase {
if (this.scene.currentBattle.waveIndex <= 150 && !(this.scene.currentBattle.waveIndex % 50))
this.scene.pushPhase(new ModifierRewardPhase(this.scene, modifierTypes.GOLDEN_POKEBALL));
}
if (this.scene.gameMode !== GameMode.CLASSIC && !(this.scene.currentBattle.waveIndex % 50))
if (this.scene.gameMode !== GameMode.CLASSIC && !(this.scene.currentBattle.waveIndex % 50)) {
this.scene.pushPhase(new ModifierRewardPhase(this.scene, modifierTypes.VOUCHER));
this.scene.pushPhase(new AddEnemyBuffModifierPhase(this.scene));
}
}
this.scene.pushPhase(new NewBattlePhase(this.scene));
} else
@ -2367,6 +2378,10 @@ export class TrainerVictoryPhase extends BattlePhase {
start() {
this.scene.playBgm(this.scene.currentBattle.trainer.config.victoryBgm);
const trainerType = this.scene.currentBattle.trainer.config.trainerType;
if (vouchers.hasOwnProperty(TrainerType[trainerType]))
this.scene.validateVoucher(vouchers[TrainerType[trainerType]]);
this.scene.unshiftPhase(new MoneyRewardPhase(this.scene, this.scene.currentBattle.trainer.config.moneyMultiplier));
const modifierRewardFuncs = this.scene.currentBattle.trainer.config.modifierRewardFuncs;
@ -2964,18 +2979,7 @@ export class AttemptCapturePhase extends PokemonPhase {
if (pokemon.species.mythical)
this.scene.validateAchv(achvs.CATCH_MYTHICAL);
let dexEntry: DexEntry;
let speciesId = pokemon.species.speciesId;
do {
dexEntry = this.scene.gameData.dexData[speciesId];
const dexIvs = dexEntry.ivs;
for (let i = 0; i < dexIvs.length; i++) {
if (dexIvs[i] < pokemon.ivs[i])
dexIvs[i] = pokemon.ivs[i];
}
if (dexIvs.filter(iv => iv === 31).length === 6)
this.scene.validateAchv(achvs.PERFECT_IVS);
} while (pokemonPrevolutions.hasOwnProperty(speciesId) && (speciesId = pokemonPrevolutions[speciesId]));
this.scene.gameData.updateSpeciesDexIvs(pokemon.species.speciesId, pokemon.ivs);
this.scene.ui.showText(`${pokemon.name} was caught!`, null, () => {
const end = () => {
@ -3229,6 +3233,31 @@ export class SelectModifierPhase extends BattlePhase {
}
}
export class EggLapsePhase extends BattlePhase {
constructor(scene: BattleScene) {
super(scene);
}
start() {
super.start();
const eggsToHatch: Egg[] = [];
for (let egg of this.scene.gameData.eggs) {
if (--egg.hatchWaves < 1)
eggsToHatch.push(egg);
}
if (eggsToHatch.length)
this.scene.queueMessage('Oh?');
for (let egg of eggsToHatch)
this.scene.unshiftPhase(new EggHatchPhase(this.scene, egg));
this.end();
}
}
export class AddEnemyBuffModifierPhase extends BattlePhase {
constructor(scene: BattleScene) {
super(scene);

View File

@ -35,6 +35,8 @@ import MessageUiHandler from './ui/message-ui-handler';
import { Species } from './data/species';
import InvertPostFX from './pipelines/invert';
import { Achv, ModifierAchv, achvs } from './system/achv';
import { GachaType } from './data/egg';
import { Voucher, vouchers } from './system/voucher';
const enableAuto = true;
const quickStart = false;
@ -301,10 +303,22 @@ export default class BattleScene extends Phaser.Scene {
this.loadAtlas('types', '');
this.loadAtlas('statuses', '');
this.loadAtlas('categories', '');
this.loadAtlas('egg', '');
this.loadAtlas('egg_crack', '');
this.loadAtlas('egg_shard', '');
this.loadAtlas('egg_lightrays', '');
this.loadAtlas('egg', 'egg');
this.loadAtlas('egg_crack', 'egg');
this.loadAtlas('egg_icons', 'egg');
this.loadAtlas('egg_shard', 'egg');
this.loadAtlas('egg_lightrays', 'egg');
Utils.getEnumKeys(GachaType).forEach(gt => {
const key = gt.toLowerCase();
this.loadImage(`gacha_${key}`, 'egg');
this.loadAtlas(`gacha_underlay_${key}`, 'egg');
});
this.loadImage('gacha_glass', 'egg');
this.loadAtlas('gacha_hatch', 'egg');
this.loadImage('gacha_knob', 'egg');
this.loadImage('egg_list_bg', 'ui');
for (let i = 0; i < 10; i++)
this.loadAtlas(`pokemon_icons_${i}`, 'ui');
@ -345,6 +359,9 @@ export default class BattleScene extends Phaser.Scene {
this.loadSe('egg_crack');
this.loadSe('egg_hatch');
this.loadSe('gacha_dial');
this.loadSe('gacha_running');
this.loadSe('gacha_dispense');
this.loadSe('PRSFX- Transform', 'battle_anims');
@ -713,10 +730,7 @@ export default class BattleScene extends Phaser.Scene {
this.currentBattle.incrementTurn(this);
//this.pushPhase(new TrainerMessageTestPhase(this));
//for (let t = 0; t < 4; t++)
//this.pushPhase(new EggHatchPhase(this, new Egg(2423432 + EGG_SEED * t, GachaType.LEGENDARY, new Date().getTime())));
if (!waveIndex) {
const isNewBiome = !lastBattle || !(lastBattle.waveIndex % 10);
const resetArenaState = isNewBiome || this.currentBattle.battleType === BattleType.TRAINER;
@ -1447,7 +1461,6 @@ export default class BattleScene extends Phaser.Scene {
validateAchvs(achvType: { new(...args: any[]): Achv }, ...args: any[]): void {
const filteredAchvs = Object.values(achvs).filter(a => a instanceof achvType);
let newAchv = false;
for (let achv of filteredAchvs)
this.validateAchv(achv, args);
}
@ -1456,6 +1469,19 @@ export default class BattleScene extends Phaser.Scene {
if (!this.gameData.achvUnlocks.hasOwnProperty(achv.id) && achv.validate(this, args)) {
this.gameData.achvUnlocks[achv.id] = new Date().getTime();
this.ui.achvBar.showAchv(achv);
if (vouchers.hasOwnProperty(achv.id))
this.validateVoucher(vouchers[achv.id]);
return true;
}
return false;
}
validateVoucher(voucher: Voucher, args?: any[]): boolean {
if (!this.gameData.voucherUnlocks.hasOwnProperty(voucher.id) && voucher.validate(this, args)) {
this.gameData.voucherUnlocks[voucher.id] = new Date().getTime();
this.ui.achvBar.showAchv(voucher);
this.gameData.voucherCounts[voucher.voucherType]++;
return true;
}

View File

@ -181,24 +181,20 @@ export const biomePokemonPools: BiomePokemonPools = {
Species.VENONAT,
Species.MEOWTH,
Species.BELLSPROUT,
Species.PICHU,
Species.IGGLYBUFF,
Species.LOTAD,
Species.SEEDOT,
Species.SHROOMISH,
Species.NINCADA,
Species.WHISMUR,
Species.AZURILL,
Species.SKITTY,
Species.KRICKETOT,
Species.BUDEW,
Species.COMBEE,
Species.CHERUBI,
Species.VENIPEDE,
Species.MINCCINO
],
[BiomePoolTier.RARE]: [ Species.ABRA, Species.CLEFFA, Species.SURSKIT ],
[BiomePoolTier.SUPER_RARE]: [ Species.EEVEE, Species.TOGEPI, Species.TYROGUE, Species.SMOOCHUM, Species.ELEKID, Species.MAGBY, Species.RALTS, Species.WYNAUT, Species.BONSLY, Species.MIME_JR, Species.HAPPINY, Species.MUNCHLAX, Species.RIOLU ],
[BiomePoolTier.RARE]: [ Species.ABRA, Species.SURSKIT ],
[BiomePoolTier.SUPER_RARE]: [ Species.EEVEE, Species.RALTS ],
[BiomePoolTier.ULTRA_RARE]: [ Species.DITTO ],
[BiomePoolTier.BOSS]: [],
[BiomePoolTier.BOSS_RARE]: [],
@ -250,7 +246,7 @@ export const biomePokemonPools: BiomePokemonPools = {
[BiomePoolTier.BOSS]: [ Species.JUMPLUFF, Species.SUNFLORA, Species.WHIMSICOTT ],
[BiomePoolTier.BOSS_RARE]: [ Species.VENUSAUR, Species.SUDOWOODO, Species.TORTERRA ],
[BiomePoolTier.BOSS_SUPER_RARE]: [],
[BiomePoolTier.BOSS_ULTRA_RARE]: [ Species.SHAYMIN ]
[BiomePoolTier.BOSS_ULTRA_RARE]: []
},
[Biome.TALL_GRASS]: {
[BiomePoolTier.COMMON]: [
@ -329,7 +325,7 @@ export const biomePokemonPools: BiomePokemonPools = {
{ 1: [ Species.ROWLET ], 17: [ Species.DARTRIX ], 36: [ Species.DECIDUEYE ] }
],
[BiomePoolTier.SUPER_RARE]: [ Species.DURANT ],
[BiomePoolTier.ULTRA_RARE]: [ Species.CELEBI, Species.KARTANA ],
[BiomePoolTier.ULTRA_RARE]: [ Species.KARTANA ],
[BiomePoolTier.BOSS]: [
Species.VENOMOTH,
Species.VICTREEBEL,
@ -351,7 +347,7 @@ export const biomePokemonPools: BiomePokemonPools = {
],
[BiomePoolTier.BOSS_RARE]: [ Species.HERACROSS, Species.STANTLER, Species.SCEPTILE, Species.ESCAVALIER, Species.ACCELGOR, Species.DURANT, Species.CHESNAUGHT, Species.DECIDUEYE, Species.LYCANROC ],
[BiomePoolTier.BOSS_SUPER_RARE]: [ Species.KARTANA ],
[BiomePoolTier.BOSS_ULTRA_RARE]: [ Species.CELEBI, Species.CALYREX ]
[BiomePoolTier.BOSS_ULTRA_RARE]: [ Species.CALYREX ]
},
[Biome.SEA]: {
[BiomePoolTier.COMMON]: [
@ -474,7 +470,7 @@ export const biomePokemonPools: BiomePokemonPools = {
{ 1: [ Species.SKRELP ], 48: [ Species.DRAGALGE ] },
Species.PINCURCHIN
],
[BiomePoolTier.RARE]: [ Species.QWILFISH, Species.CORSOLA, Species.OCTILLERY, { 1: [ Species.MANTYKE ], 20: [ Species.MANTINE ] }, Species.PHIONE, Species.ALOMOMOLA, { 1: [ Species.TYNAMO ], 39: [ Species.EELEKTRIK ] }, Species.DHELMISE ],
[BiomePoolTier.RARE]: [ Species.QWILFISH, Species.CORSOLA, Species.OCTILLERY, { 1: [ Species.MANTYKE ], 20: [ Species.MANTINE ] }, Species.ALOMOMOLA, { 1: [ Species.TYNAMO ], 39: [ Species.EELEKTRIK ] }, Species.DHELMISE ],
[BiomePoolTier.SUPER_RARE]: [
{ 1: [ Species.OMANYTE ], 40: [ Species.OMASTAR ] },
{ 1: [ Species.KABUTO ], 40: [ Species.KABUTOPS ] },
@ -484,10 +480,10 @@ export const biomePokemonPools: BiomePokemonPools = {
Species.ARCTOVISH,
Species.HISUI_QWILFISH
],
[BiomePoolTier.ULTRA_RARE]: [ Species.FEEBAS, Species.MANAPHY, Species.NIHILEGO ],
[BiomePoolTier.ULTRA_RARE]: [ Species.FEEBAS, Species.NIHILEGO ],
[BiomePoolTier.BOSS]: [ Species.LANTURN, Species.QWILFISH, Species.CORSOLA, Species.OCTILLERY, Species.MANTINE, Species.WAILORD, Species.HUNTAIL, Species.GOREBYSS, Species.LUVDISC, Species.JELLICENT, Species.ALOMOMOLA, Species.DRAGALGE, Species.BARRASKEWDA ],
[BiomePoolTier.BOSS_RARE]: [ Species.OMASTAR, Species.KABUTOPS, Species.RELICANTH, Species.PHIONE, Species.EELEKTROSS, Species.PYUKUMUKU, Species.DHELMISE, Species.ARCTOVISH, Species.BASCULEGION ],
[BiomePoolTier.BOSS_SUPER_RARE]: [ Species.MILOTIC, Species.MANAPHY, Species.NIHILEGO, Species.CURSOLA, Species.OVERQWIL ],
[BiomePoolTier.BOSS_RARE]: [ Species.OMASTAR, Species.KABUTOPS, Species.RELICANTH, Species.EELEKTROSS, Species.PYUKUMUKU, Species.DHELMISE, Species.ARCTOVISH, Species.BASCULEGION ],
[BiomePoolTier.BOSS_SUPER_RARE]: [ Species.MILOTIC, Species.NIHILEGO, Species.CURSOLA, Species.OVERQWIL ],
[BiomePoolTier.BOSS_ULTRA_RARE]: [ Species.KYOGRE ]
},
[Biome.MOUNTAIN]: {
@ -573,10 +569,10 @@ export const biomePokemonPools: BiomePokemonPools = {
],
[BiomePoolTier.RARE]: [ Species.ONIX, { 1: [ Species.FERROSEED ], 40: [ Species.FERROTHORN ] }, Species.CARBINK ],
[BiomePoolTier.SUPER_RARE]: [ Species.SHUCKLE ],
[BiomePoolTier.ULTRA_RARE]: [ Species.REGISTEEL, Species.UXIE ],
[BiomePoolTier.ULTRA_RARE]: [ Species.UXIE ],
[BiomePoolTier.BOSS]: [ Species.PARASECT, Species.ONIX, Species.CROBAT, Species.URSARING, Species.EXPLOUD, Species.PROBOPASS, Species.GIGALITH, Species.SWOOBAT, Species.DIGGERSBY, Species.NOIVERN, Species.GOLISOPOD ],
[BiomePoolTier.BOSS_RARE]: [ Species.SHUCKLE, Species.FERROTHORN, Species.LYCANROC ],
[BiomePoolTier.BOSS_SUPER_RARE]: [ Species.REGISTEEL, Species.UXIE ],
[BiomePoolTier.BOSS_SUPER_RARE]: [ Species.UXIE ],
[BiomePoolTier.BOSS_ULTRA_RARE]: []
},
[Biome.DESERT]: {
@ -726,7 +722,7 @@ export const biomePokemonPools: BiomePokemonPools = {
{ 1: [ Species.CLOBBOPUS ], 20: [ Species.GRAPPLOCT ] }
],
[BiomePoolTier.UNCOMMON]: [ { 1: [ Species.CROAGUNK ], 37: [ Species.TOXICROAK ] }, { 1: [ Species.SCRAGGY ], 39: [ Species.SCRAFTY ] }, { 1: [ Species.MIENFOO ], 50: [ Species.MIENSHAO ] } ],
[BiomePoolTier.RARE]: [ { 1: [ Species.TYROGUE ], 20: [ Species.HITMONLEE ] }, Species.HITMONCHAN, Species.LUCARIO, Species.THROH, Species.SAWK ],
[BiomePoolTier.RARE]: [ Species.HITMONLEE, Species.HITMONCHAN, Species.LUCARIO, Species.THROH, Species.SAWK ],
[BiomePoolTier.SUPER_RARE]: [ Species.HITMONTOP, Species.GALLADE, Species.GALAR_FARFETCHD ],
[BiomePoolTier.ULTRA_RARE]: [ Species.TERRAKION, Species.KUBFU, Species.GALAR_ZAPDOS ],
[BiomePoolTier.BOSS]: [ Species.PRIMEAPE, Species.HITMONLEE, Species.HITMONCHAN, Species.HARIYAMA, Species.MEDICHAM, Species.LUCARIO, Species.TOXICROAK, Species.THROH, Species.SAWK, Species.SCRAFTY, Species.MIENSHAO, Species.BEWEAR, Species.GRAPPLOCT ],
@ -745,11 +741,11 @@ export const biomePokemonPools: BiomePokemonPools = {
[BiomePoolTier.UNCOMMON]: [ { 1: [ Species.BRONZOR ], 33: [ Species.BRONZONG ] }, Species.KLEFKI ],
[BiomePoolTier.RARE]: [],
[BiomePoolTier.SUPER_RARE]: [ { 1: [ Species.PORYGON ], 20: [ Species.PORYGON2 ] }, { 1: [ Species.BELDUM ], 20: [ Species.METANG ], 45: [ Species.METAGROSS ] } ],
[BiomePoolTier.ULTRA_RARE]: [ Species.GENESECT, Species.MAGEARNA, Species.MELTAN ],
[BiomePoolTier.ULTRA_RARE]: [ Species.GENESECT, Species.MAGEARNA ],
[BiomePoolTier.BOSS]: [ Species.KLINKLANG, Species.KLEFKI ],
[BiomePoolTier.BOSS_RARE]: [],
[BiomePoolTier.BOSS_SUPER_RARE]: [ Species.GENESECT, Species.MAGEARNA ],
[BiomePoolTier.BOSS_ULTRA_RARE]: [ Species.MELMETAL ]
[BiomePoolTier.BOSS_ULTRA_RARE]: []
},
[Biome.RUINS]: {
[BiomePoolTier.COMMON]: [
@ -763,11 +759,11 @@ export const biomePokemonPools: BiomePokemonPools = {
[BiomePoolTier.UNCOMMON]: [ { 1: [ Species.ABRA ], 16: [ Species.KADABRA ] }, Species.SIGILYPH ],
[BiomePoolTier.RARE]: [ Species.MR_MIME, Species.WOBBUFFET, { 1: [ Species.GOTHITA ], 32: [ Species.GOTHORITA ], 41: [ Species.GOTHITELLE ] }, Species.STONJOURNER ],
[BiomePoolTier.SUPER_RARE]: [ Species.ESPEON, { 1: [ Species.ARCHEN ], 37: [ Species.ARCHEOPS ] }, { 1: [ Species.GALAR_YAMASK ], 34: [ Species.RUNERIGUS ] } ],
[BiomePoolTier.ULTRA_RARE]: [ Species.MEW, Species.VICTINI ],
[BiomePoolTier.ULTRA_RARE]: [ Species.REGISTEEL ],
[BiomePoolTier.BOSS]: [ Species.ALAKAZAM, Species.HYPNO, Species.XATU, Species.GRUMPIG, Species.CLAYDOL, Species.SIGILYPH, Species.GOTHITELLE, Species.BEHEEYEM ],
[BiomePoolTier.BOSS_RARE]: [ Species.MR_MIME, Species.ESPEON, Species.WOBBUFFET, Species.ARCHEOPS ],
[BiomePoolTier.BOSS_SUPER_RARE]: [ Species.VICTINI, Species.RUNERIGUS ],
[BiomePoolTier.BOSS_ULTRA_RARE]: [ Species.MEW ]
[BiomePoolTier.BOSS_SUPER_RARE]: [ Species.REGISTEEL, Species.RUNERIGUS ],
[BiomePoolTier.BOSS_ULTRA_RARE]: []
},
[Biome.WASTELAND]: {
[BiomePoolTier.COMMON]: [
@ -812,11 +808,11 @@ export const biomePokemonPools: BiomePokemonPools = {
[BiomePoolTier.UNCOMMON]: [ { 1: [ Species.BALTOY ], 36: [ Species.CLAYDOL ] }, { 1: [ Species.ELGYEM ], 42: [ Species.BEHEEYEM ] } ],
[BiomePoolTier.RARE]: [ { 1: [ Species.BELDUM ], 20: [ Species.METANG ], 45: [ Species.METAGROSS ] }, Species.SIGILYPH, { 1: [ Species.SOLOSIS ], 32: [ Species.DUOSION ], 41: [ Species.REUNICLUS ] } ],
[BiomePoolTier.SUPER_RARE]: [ { 1: [ Species.PORYGON ], 20: [ Species.PORYGON2 ] } ],
[BiomePoolTier.ULTRA_RARE]: [ Species.JIRACHI, Species.DEOXYS, Species.CRESSELIA, { 1: [ Species.COSMOG ], 43: [ Species.COSMOEM ] }, Species.CELESTEELA ],
[BiomePoolTier.ULTRA_RARE]: [ Species.DEOXYS, Species.CRESSELIA, { 1: [ Species.COSMOG ], 43: [ Species.COSMOEM ] }, Species.CELESTEELA ],
[BiomePoolTier.BOSS]: [ Species.CLEFABLE, Species.LUNATONE, Species.SOLROCK, Species.BRONZONG, Species.MUSHARNA, Species.REUNICLUS, Species.MINIOR ],
[BiomePoolTier.BOSS_RARE]: [ Species.METAGROSS, Species.PORYGON_Z ],
[BiomePoolTier.BOSS_SUPER_RARE]: [ Species.JIRACHI, Species.DEOXYS, Species.CRESSELIA, Species.CELESTEELA ],
[BiomePoolTier.BOSS_ULTRA_RARE]: [ Species.RAYQUAZA, Species.ARCEUS, Species.SOLGALEO, Species.LUNALA, Species.NECROZMA ]
[BiomePoolTier.BOSS_SUPER_RARE]: [ Species.DEOXYS, Species.CRESSELIA, Species.CELESTEELA ],
[BiomePoolTier.BOSS_ULTRA_RARE]: [ Species.RAYQUAZA, Species.SOLGALEO, Species.LUNALA, Species.NECROZMA ]
},
[Biome.CONSTRUCTION_SITE]: {
[BiomePoolTier.COMMON]: [
@ -831,7 +827,7 @@ export const biomePokemonPools: BiomePokemonPools = {
{ 1: [ Species.RHYHORN ], 42: [ Species.RHYDON ] },
{ 1: [ Species.SCRAGGY ], 39: [ Species.SCRAFTY ] }
],
[BiomePoolTier.RARE]: [ Species.ONIX, { 1: [ Species.TYROGUE ], 20: [ Species.HITMONLEE ] }, Species.HITMONCHAN, Species.DURALUDON ],
[BiomePoolTier.RARE]: [ Species.ONIX, Species.HITMONLEE, Species.HITMONCHAN, Species.DURALUDON ],
[BiomePoolTier.SUPER_RARE]: [ Species.DITTO, Species.HITMONTOP, { 1: [ Species.GALAR_MEOWTH ], 28: [ Species.PERRSERKER ] } ],
[BiomePoolTier.ULTRA_RARE]: [ Species.COBALION ],
[BiomePoolTier.BOSS]: [ Species.MACHAMP, Species.CONKELDURR ],
@ -1075,7 +1071,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
[BiomePoolTier.RARE]: [ TrainerType.BLACK_BELT ],
[BiomePoolTier.SUPER_RARE]: [],
[BiomePoolTier.ULTRA_RARE]: [],
[BiomePoolTier.BOSS]: [ TrainerType.NORMAN, TrainerType.CHEREN, TrainerType.LENORA ],
[BiomePoolTier.BOSS]: [ TrainerType.CILAN, TrainerType.CHILI, TrainerType.CRESS, TrainerType.CHEREN, TrainerType.LENORA ],
[BiomePoolTier.BOSS_RARE]: [],
[BiomePoolTier.BOSS_SUPER_RARE]: [],
[BiomePoolTier.BOSS_ULTRA_RARE]: []
@ -1108,7 +1104,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
[BiomePoolTier.RARE]: [ TrainerType.ARTIST ],
[BiomePoolTier.SUPER_RARE]: [],
[BiomePoolTier.ULTRA_RARE]: [],
[BiomePoolTier.BOSS]: [ TrainerType.CHEREN ],
[BiomePoolTier.BOSS]: [ TrainerType.NORMAN, TrainerType.CHEREN ],
[BiomePoolTier.BOSS_RARE]: [],
[BiomePoolTier.BOSS_SUPER_RARE]: [],
[BiomePoolTier.BOSS_ULTRA_RARE]: []
@ -2206,10 +2202,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
[ Biome.END, BiomePoolTier.ULTRA_RARE ]
]
],
[ Species.MEW, Type.PSYCHIC, -1, [
[ Biome.RUINS, BiomePoolTier.ULTRA_RARE ],
[ Biome.RUINS, BiomePoolTier.BOSS_ULTRA_RARE ]
]
[ Species.MEW, Type.PSYCHIC, -1, [ ]
],
[ Species.CHIKORITA, Type.GRASS, -1, [
[ Biome.TALL_GRASS, BiomePoolTier.RARE ]
@ -2306,21 +2299,13 @@ export const biomeTrainerPools: BiomeTrainerPools = {
[ Biome.SEABED, BiomePoolTier.BOSS ]
]
],
[ Species.PICHU, Type.ELECTRIC, -1, [
[ Biome.TOWN, BiomePoolTier.UNCOMMON ]
]
[ Species.PICHU, Type.ELECTRIC, -1, [ ]
],
[ Species.CLEFFA, Type.FAIRY, -1, [
[ Biome.TOWN, BiomePoolTier.RARE ]
]
[ Species.CLEFFA, Type.FAIRY, -1, [ ]
],
[ Species.IGGLYBUFF, Type.NORMAL, Type.FAIRY, [
[ Biome.TOWN, BiomePoolTier.UNCOMMON ]
]
[ Species.IGGLYBUFF, Type.NORMAL, Type.FAIRY, [ ]
],
[ Species.TOGEPI, Type.FAIRY, -1, [
[ Biome.TOWN, BiomePoolTier.SUPER_RARE ]
]
[ Species.TOGEPI, Type.FAIRY, -1, [ ]
],
[ Species.TOGETIC, Type.FAIRY, Type.FLYING, [
[ Biome.FAIRY_CAVE, BiomePoolTier.UNCOMMON ]
@ -2615,11 +2600,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
[ Biome.METROPOLIS, BiomePoolTier.SUPER_RARE ]
]
],
[ Species.TYROGUE, Type.FIGHTING, -1, [
[ Biome.TOWN, BiomePoolTier.SUPER_RARE ],
[ Biome.DOJO, BiomePoolTier.RARE ],
[ Biome.CONSTRUCTION_SITE, BiomePoolTier.RARE ]
]
[ Species.TYROGUE, Type.FIGHTING, -1, [ ]
],
[ Species.HITMONTOP, Type.FIGHTING, -1, [
[ Biome.DOJO, BiomePoolTier.SUPER_RARE ],
@ -2627,17 +2608,11 @@ export const biomeTrainerPools: BiomeTrainerPools = {
[ Biome.CONSTRUCTION_SITE, BiomePoolTier.SUPER_RARE ]
]
],
[ Species.SMOOCHUM, Type.ICE, Type.PSYCHIC, [
[ Biome.TOWN, BiomePoolTier.SUPER_RARE ]
]
[ Species.SMOOCHUM, Type.ICE, Type.PSYCHIC, [ ]
],
[ Species.ELEKID, Type.ELECTRIC, -1, [
[ Biome.TOWN, BiomePoolTier.SUPER_RARE ]
]
[ Species.ELEKID, Type.ELECTRIC, -1, [ ]
],
[ Species.MAGBY, Type.FIRE, -1, [
[ Biome.TOWN, BiomePoolTier.SUPER_RARE ]
]
[ Species.MAGBY, Type.FIRE, -1, [ ]
],
[ Species.MILTANK, Type.NORMAL, -1, [
[ Biome.MEADOW, BiomePoolTier.RARE ],
@ -2688,10 +2663,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
[ Biome.MOUNTAIN, BiomePoolTier.BOSS_ULTRA_RARE ]
]
],
[ Species.CELEBI, Type.PSYCHIC, Type.GRASS, [
[ Biome.FOREST, BiomePoolTier.ULTRA_RARE ],
[ Biome.FOREST, BiomePoolTier.BOSS_ULTRA_RARE ]
]
[ Species.CELEBI, Type.PSYCHIC, Type.GRASS, [ ]
],
[ Species.TREECKO, Type.GRASS, -1, [
[ Biome.FOREST, BiomePoolTier.RARE ]
@ -2918,9 +2890,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
[ Biome.DOJO, BiomePoolTier.BOSS ]
]
],
[ Species.AZURILL, Type.NORMAL, Type.FAIRY, [
[ Biome.TOWN, BiomePoolTier.UNCOMMON ]
]
[ Species.AZURILL, Type.NORMAL, Type.FAIRY, [ ]
],
[ Species.NOSEPASS, Type.ROCK, -1, [
[ Biome.CAVE, BiomePoolTier.UNCOMMON ]
@ -3216,9 +3186,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
[ Biome.ABYSS, BiomePoolTier.BOSS ]
]
],
[ Species.WYNAUT, Type.PSYCHIC, -1, [
[ Biome.TOWN, BiomePoolTier.SUPER_RARE ]
]
[ Species.WYNAUT, Type.PSYCHIC, -1, [ ]
],
[ Species.SNORUNT, Type.ICE, -1, [
[ Biome.ICE_CAVE, BiomePoolTier.UNCOMMON ]
@ -3306,8 +3274,8 @@ export const biomeTrainerPools: BiomeTrainerPools = {
]
],
[ Species.REGISTEEL, Type.STEEL, -1, [
[ Biome.CAVE, BiomePoolTier.ULTRA_RARE ],
[ Biome.CAVE, BiomePoolTier.BOSS_SUPER_RARE ]
[ Biome.RUINS, BiomePoolTier.ULTRA_RARE ],
[ Biome.RUINS, BiomePoolTier.BOSS_SUPER_RARE ]
]
],
[ Species.LATIAS, Type.DRAGON, Type.PSYCHIC, [
@ -3333,10 +3301,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
[ Biome.END, BiomePoolTier.ULTRA_RARE ]
]
],
[ Species.JIRACHI, Type.STEEL, Type.PSYCHIC, [
[ Biome.SPACE, BiomePoolTier.ULTRA_RARE ],
[ Biome.SPACE, BiomePoolTier.BOSS_SUPER_RARE ]
]
[ Species.JIRACHI, Type.STEEL, Type.PSYCHIC, [ ]
],
[ Species.DEOXYS, Type.PSYCHIC, -1, [
[ Biome.SPACE, BiomePoolTier.ULTRA_RARE ],
@ -3435,9 +3400,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
[ Biome.POWER_PLANT, BiomePoolTier.BOSS ]
]
],
[ Species.BUDEW, Type.GRASS, Type.POISON, [
[ Biome.TOWN, BiomePoolTier.UNCOMMON ]
]
[ Species.BUDEW, Type.GRASS, Type.POISON, [ ]
],
[ Species.ROSERADE, Type.GRASS, Type.POISON, [
[ Biome.MEADOW, BiomePoolTier.BOSS ]
@ -3599,17 +3562,11 @@ export const biomeTrainerPools: BiomeTrainerPools = {
[ Biome.LABORATORY, BiomePoolTier.BOSS ]
]
],
[ Species.BONSLY, Type.ROCK, -1, [
[ Biome.TOWN, BiomePoolTier.SUPER_RARE ]
]
[ Species.BONSLY, Type.ROCK, -1, [ ]
],
[ Species.MIME_JR, Type.PSYCHIC, Type.FAIRY, [
[ Biome.TOWN, BiomePoolTier.SUPER_RARE ]
]
[ Species.MIME_JR, Type.PSYCHIC, Type.FAIRY, [ ]
],
[ Species.HAPPINY, Type.NORMAL, -1, [
[ Biome.TOWN, BiomePoolTier.SUPER_RARE ]
]
[ Species.HAPPINY, Type.NORMAL, -1, []
],
[ Species.CHATOT, Type.NORMAL, Type.FLYING, [
[ Biome.JUNGLE, BiomePoolTier.SUPER_RARE ]
@ -3638,13 +3595,9 @@ export const biomeTrainerPools: BiomeTrainerPools = {
[ Biome.END, BiomePoolTier.COMMON ]
]
],
[ Species.MUNCHLAX, Type.NORMAL, -1, [
[ Biome.TOWN, BiomePoolTier.SUPER_RARE ]
]
[ Species.MUNCHLAX, Type.NORMAL, -1, [ ]
],
[ Species.RIOLU, Type.FIGHTING, -1, [
[ Biome.TOWN, BiomePoolTier.SUPER_RARE ]
]
[ Species.RIOLU, Type.FIGHTING, -1, [ ]
],
[ Species.LUCARIO, Type.FIGHTING, Type.STEEL, [
[ Biome.DOJO, BiomePoolTier.RARE ],
@ -3844,34 +3797,22 @@ export const biomeTrainerPools: BiomeTrainerPools = {
[ Biome.SPACE, BiomePoolTier.BOSS_SUPER_RARE ]
]
],
[ Species.PHIONE, Type.WATER, -1, [
[ Biome.SEABED, BiomePoolTier.RARE ],
[ Biome.SEABED, BiomePoolTier.BOSS_RARE ]
]
[ Species.PHIONE, Type.WATER, -1, [ ]
],
[ Species.MANAPHY, Type.WATER, -1, [
[ Biome.SEABED, BiomePoolTier.ULTRA_RARE ],
[ Biome.SEABED, BiomePoolTier.BOSS_SUPER_RARE ]
]
[ Species.MANAPHY, Type.WATER, -1, [ ]
],
[ Species.DARKRAI, Type.DARK, -1, [
[ Biome.ABYSS, BiomePoolTier.ULTRA_RARE ],
[ Biome.ABYSS, BiomePoolTier.BOSS_SUPER_RARE ]
]
],
[ Species.SHAYMIN, Type.GRASS, -1, [
[ Biome.GRASS, BiomePoolTier.BOSS_ULTRA_RARE ]
]
[ Species.SHAYMIN, Type.GRASS, -1, [ ]
],
[ Species.ARCEUS, Type.NORMAL, -1, [
[ Biome.SPACE, BiomePoolTier.BOSS_ULTRA_RARE ],
[ Biome.END, BiomePoolTier.ULTRA_RARE ]
]
],
[ Species.VICTINI, Type.PSYCHIC, Type.FIRE, [
[ Biome.RUINS, BiomePoolTier.ULTRA_RARE ],
[ Biome.RUINS, BiomePoolTier.BOSS_SUPER_RARE ]
]
[ Species.VICTINI, Type.PSYCHIC, Type.FIRE, [ ]
],
[ Species.SNIVY, Type.GRASS, -1, [
[ Biome.JUNGLE, BiomePoolTier.RARE ]
@ -5345,13 +5286,9 @@ export const biomeTrainerPools: BiomeTrainerPools = {
[ Biome.POWER_PLANT, BiomePoolTier.BOSS_SUPER_RARE ]
]
],
[ Species.MELTAN, Type.STEEL, -1, [
[ Biome.FACTORY, BiomePoolTier.ULTRA_RARE ]
]
[ Species.MELTAN, Type.STEEL, -1, [ ]
],
[ Species.MELMETAL, Type.STEEL, -1, [
[ Biome.FACTORY, BiomePoolTier.BOSS_ULTRA_RARE ]
]
[ Species.MELMETAL, Type.STEEL, -1, [ ]
],
[ Species.GROOKEY, Type.GRASS, -1, [
[ Biome.JUNGLE, BiomePoolTier.RARE ]
@ -6619,7 +6556,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
]
],
[ TrainerType.NORMAN, [
[ Biome.PLAINS, BiomePoolTier.BOSS ]
[ Biome.METROPOLIS, BiomePoolTier.BOSS ]
]
],
[ TrainerType.WINONA, [
@ -6673,9 +6610,15 @@ export const biomeTrainerPools: BiomeTrainerPools = {
[ Biome.POWER_PLANT, BiomePoolTier.BOSS ]
]
],
[ TrainerType.CILAN, [] ],
[ TrainerType.CHILI, [] ],
[ TrainerType.CRESS, [] ],
[ TrainerType.CILAN, [
[ Biome.PLAINS, BiomePoolTier.BOSS ]
] ],
[ TrainerType.CHILI, [
[ Biome.PLAINS, BiomePoolTier.BOSS ]
] ],
[ TrainerType.CRESS, [
[ Biome.PLAINS, BiomePoolTier.BOSS ]
] ],
[ TrainerType.CHEREN, [
[ Biome.PLAINS, BiomePoolTier.BOSS ],
[ Biome.METROPOLIS, BiomePoolTier.BOSS ]

View File

@ -1,4 +1,9 @@
import { ModifierTier } from "../modifier/modifier-type";
import { Type } from "./type";
import * as Utils from "../utils";
import BattleScene from "../battle-scene";
import { Species } from "./species";
import { getPokemonSpecies, speciesStarters } from "./pokemon-species";
export const EGG_SEED = 1073741824;
@ -12,12 +17,99 @@ export class Egg {
public id: integer;
public tier: ModifierTier;
public gachaType: GachaType;
public hatchWaves: integer;
public timestamp: integer;
constructor(id: integer, gachaType: GachaType, timestamp: integer) {
constructor(id: integer, gachaType: GachaType, hatchWaves: integer, timestamp: integer) {
this.id = id;
this.tier = Math.floor(id / EGG_SEED);
this.gachaType = gachaType;
this.hatchWaves = hatchWaves;
this.timestamp = timestamp;
}
isManaphyEgg(): boolean {
return this.tier === ModifierTier.COMMON && !(this.id % 255);
}
getKey(): string {
if (this.isManaphyEgg())
return 'manaphy';
return this.tier.toString();
}
}
export function getEggTierDefaultHatchWaves(tier: ModifierTier): integer {
switch (tier) {
case ModifierTier.COMMON:
return 10;
case ModifierTier.GREAT:
return 25;
case ModifierTier.ULTRA:
return 50;
}
return 100;
}
export function getEggDescriptor(egg: Egg): string {
if (egg.isManaphyEgg())
return 'Manaphy';
switch (egg.tier) {
case ModifierTier.GREAT:
return 'Rare';
case ModifierTier.ULTRA:
return 'Epic';
case ModifierTier.MASTER:
return 'Legendary';
default:
return 'Common';
}
}
export function getEggHatchWavesMessage(hatchWaves: integer): string {
if (hatchWaves <= 5)
return 'Sounds can be heard coming from inside! It will hatch soon!';
if (hatchWaves <= 15)
return 'It appears to move occasionally. It may be close to hatching.';
if (hatchWaves <= 50)
return 'What will hatch from this? It doesn\'t seem close to hatching.';
return 'It looks like this Egg will take a long time to hatch.';
}
export function getEggGachaTypeDescriptor(scene: BattleScene, egg: Egg): string {
if (egg.isManaphyEgg())
return '';
switch (egg.gachaType) {
case GachaType.LEGENDARY:
return `Legendary Rate Up (${getPokemonSpecies(getLegendaryGachaSpeciesForTimestamp(scene, egg.timestamp)).getName()})`;
case GachaType.TYPE:
return `Type Rate Up (${Utils.toReadableString(Type[getTypeGachaTypeForTimestamp(scene, egg.timestamp)])})`;
case GachaType.SHINY:
return 'Shiny Rate Up';
}
}
export function getLegendaryGachaSpeciesForTimestamp(scene: BattleScene, timestamp: integer): Species {
const legendarySpecies = Object.entries(speciesStarters)
.filter(s => s[1] >= 8 && s[1] <= 9)
.map(s => parseInt(s[0]));
let ret: Species;
scene.executeWithSeedOffset(() => {
ret = Phaser.Math.RND.pick(legendarySpecies);
}, Utils.getSunday(new Date(timestamp)).getTime(), EGG_SEED.toString());
return ret;
}
export function getTypeGachaTypeForTimestamp(scene: BattleScene, timestamp: integer): Type {
const types = Utils.getEnumValues(Type);
let ret: Type;
scene.executeWithSeedOffset(() => {
ret = Phaser.Math.RND.pick(types);
}, Utils.getSunday(new Date(timestamp)).getTime(), EGG_SEED.toString());
return ret;
}

View File

@ -230,7 +230,7 @@ export abstract class PokemonSpeciesForm {
const originalWarn = console.warn;
// Ignore warnings for missing frames, because there will be a lot
console.warn = () => {};
const frameNames = scene.anims.generateFrameNames(this.getSpriteKey(female, formIndex, shiny), { zeroPad: 4, suffix: ".png", start: 1, end: 256 });
const frameNames = scene.anims.generateFrameNames(this.getSpriteKey(female, formIndex, shiny), { zeroPad: 4, suffix: ".png", start: 1, end: 400 });
console.warn = originalWarn;
scene.anims.create({
key: this.getSpriteKey(female, formIndex, shiny),

View File

@ -3,12 +3,16 @@ import { BattlePhase } from "./battle-phase";
import BattleScene, { AnySound } from "./battle-scene";
import * as Utils from "./utils";
import { Mode } from "./ui/ui";
import { Egg } from "./data/egg";
import { EGG_SEED, Egg, GachaType, getLegendaryGachaSpeciesForTimestamp, getTypeGachaTypeForTimestamp } from "./data/egg";
import EggHatchSceneHandler from "./ui/egg-hatch-scene-handler";
import { ModifierTier } from "./modifier/modifier-type";
import { Species } from "./data/species";
import Pokemon, { PlayerPokemon } from "./pokemon";
import { getPokemonSpecies, speciesStarters } from "./data/pokemon-species";
import { StatsContainer } from "./ui/stats-container";
import { TextStyle, addTextObject } from "./ui/text";
import { Gender, getGenderColor, getGenderSymbol } from "./data/gender";
import { achvs } from "./system/achv";
export class EggHatchPhase extends BattlePhase {
private egg: Egg;
@ -22,6 +26,9 @@ export class EggHatchPhase extends BattlePhase {
private eggLightraysOverlay: Phaser.GameObjects.Sprite;
private pokemonSprite: Phaser.GameObjects.Sprite;
private infoContainer: Phaser.GameObjects.Container;
private statsContainer: StatsContainer;
constructor(scene: BattleScene, egg: Egg) {
super(scene);
@ -36,6 +43,13 @@ export class EggHatchPhase extends BattlePhase {
if (!this.egg)
return this.end();
const eggIndex = this.scene.gameData.eggs.findIndex(e => e.id === this.egg.id);
if (eggIndex === -1)
return this.end();
this.scene.gameData.eggs.splice(eggIndex, 1);
this.scene.fadeOutBgm(null, false);
const eggHatchHandler = this.scene.ui.getHandler() as EggHatchSceneHandler;
@ -48,7 +62,7 @@ export class EggHatchPhase extends BattlePhase {
this.eggContainer = this.scene.add.container(this.eggHatchBg.displayWidth / 2, this.eggHatchBg.displayHeight / 2);
this.eggSprite = this.scene.add.sprite(0, 0, 'egg', `egg_${this.egg.tier}`);
this.eggSprite = this.scene.add.sprite(0, 0, 'egg', `egg_${this.egg.getKey()}`);
this.eggCrackSprite = this.scene.add.sprite(0, 0, 'egg_crack', '0');
this.eggCrackSprite.setVisible(false);
@ -70,9 +84,58 @@ export class EggHatchPhase extends BattlePhase {
this.eggHatchOverlay.setAlpha(0);
this.scene.fieldUI.add(this.eggHatchOverlay);
const infoBg = this.scene.add.nineslice(0, 0, 'window', null, 96, 116, 6, 6, 6, 6);
this.infoContainer = this.scene.add.container(this.eggHatchBg.displayWidth + infoBg.width / 2, this.eggHatchBg.displayHeight / 2);
this.statsContainer = new StatsContainer(this.scene, -48, -54, true);
this.infoContainer.add(infoBg);
this.infoContainer.add(this.statsContainer);
const pokemonGenderLabelText = addTextObject(this.scene, -16, 32, 'Gender:', TextStyle.WINDOW, { fontSize: '64px' });
pokemonGenderLabelText.setOrigin(1, 0);
pokemonGenderLabelText.setVisible(false);
this.infoContainer.add(pokemonGenderLabelText);
const pokemonGenderText = addTextObject(this.scene, -12, 32, '', TextStyle.WINDOW, { fontSize: '64px' });
pokemonGenderText.setOrigin(0, 0);
pokemonGenderText.setVisible(false);
this.infoContainer.add(pokemonGenderText);
const pokemonAbilityLabelText = addTextObject(this.scene, -16, 32, 'Ability:', TextStyle.WINDOW, { fontSize: '64px' });
pokemonAbilityLabelText.setOrigin(1, 0);
this.infoContainer.add(pokemonAbilityLabelText);
const pokemonAbilityText = addTextObject(this.scene, -12, 32, '', TextStyle.WINDOW, { fontSize: '64px' });
pokemonAbilityText.setOrigin(0, 0);
this.infoContainer.add(pokemonAbilityText);
this.eggHatchContainer.add(this.infoContainer);
const pokemon = this.generatePokemon();
console.log(pokemon.name, pokemon);
let abilityYOffset = 5;
if (pokemon.gender > Gender.GENDERLESS) {
pokemonGenderText.setText(getGenderSymbol(pokemon.gender));
pokemonGenderText.setColor(getGenderColor(pokemon.gender));
pokemonGenderText.setShadowColor(getGenderColor(pokemon.gender, true));
pokemonGenderLabelText.setVisible(true);
pokemonGenderText.setVisible(true);
abilityYOffset = 10;
}
[ pokemonAbilityLabelText, pokemonAbilityText ].map(t => t.y += abilityYOffset);
pokemonAbilityText.setText(pokemon.getAbility().name);
const originalIvs: integer[] = this.scene.gameData.dexData[pokemon.species.speciesId].caughtAttr
? this.scene.gameData.dexData[pokemon.species.speciesId].ivs
: null;
this.statsContainer.updateIvs(pokemon.ivs, originalIvs);
this.pokemonSprite.setVisible(false);
@ -108,19 +171,35 @@ export class EggHatchPhase extends BattlePhase {
ease: 'Cubic.easeIn'
});
this.scene.time.delayedCall(Utils.fixedInt(1500), () => {
if (pokemon.species.mythical)
this.scene.validateAchv(achvs.HATCH_MYTHICAL);
if (pokemon.species.legendary)
this.scene.validateAchv(achvs.HATCH_LEGENDARY);
if (pokemon.isShiny())
this.scene.validateAchv(achvs.HATCH_SHINY);
this.eggContainer.setVisible(false);
this.pokemonSprite.play(pokemon.getSpriteKey(true));
this.pokemonSprite.setVisible(true);
this.scene.time.delayedCall(Utils.fixedInt(1000), () => {
pokemon.cry();
this.scene.time.delayedCall(Utils.fixedInt(1250), () => {
this.scene.tweens.add({
targets: this.infoContainer,
duration: Utils.fixedInt(750),
ease: 'Cubic.easeInOut',
x: this.eggHatchBg.displayWidth - 48
});
this.scene.playSoundWithoutBgm('evolution_fanfare');
this.scene.ui.showText(`${pokemon.name} hatched from the egg!`, null, () => {
this.scene.ui.showText(null, 0);
this.end();
this.scene.gameData.updateSpeciesDexIvs(pokemon.species.speciesId, pokemon.ivs);
this.scene.gameData.setPokemonCaught(pokemon).then(() => {
this.scene.ui.showText(null, 0);
this.end();
});
}, null, true, 3000);
this.scene.time.delayedCall(Utils.fixedInt(4250), () => this.scene.playBgm());
//this.scene.time.delayedCall(Utils.fixedInt(4250), () => this.scene.playBgm());
});
});
this.scene.tweens.add({
@ -193,7 +272,8 @@ export class EggHatchPhase extends BattlePhase {
doSprayParticle(trigIndex: integer, offsetY: number) {
const initialX = this.eggHatchBg.displayWidth / 2;
const initialY = this.eggHatchBg.displayHeight / 2 + offsetY;
const particle = this.scene.add.image(initialX, initialY, 'egg_shard', `${this.egg.tier}_${Math.floor(trigIndex / 2)}`);
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)}`);
this.eggHatchContainer.add(particle);
let f = 0;
@ -228,58 +308,99 @@ export class EggHatchPhase extends BattlePhase {
}
generatePokemon(): Pokemon {
let minStarterValue: integer;
let maxStarterValue: integer;
let ret: Pokemon;
let speciesOverride: Species;
switch (this.egg.tier) {
case ModifierTier.GREAT:
minStarterValue = 3;
maxStarterValue = 5;
break;
case ModifierTier.ULTRA:
minStarterValue = 6;
maxStarterValue = 7;
break;
case ModifierTier.MASTER:
minStarterValue = 8;
maxStarterValue = 9;
break;
default:
minStarterValue = 1;
maxStarterValue = 2;
break;
if (this.egg.isManaphyEgg()) {
this.scene.executeWithSeedOffset(() => {
const rand = Utils.randSeedInt(8);
speciesOverride = rand ? Species.PHIONE : Species.MANAPHY;
}, this.egg.id, EGG_SEED.toString());
} else if (this.egg.tier === ModifierTier.MASTER
&& this.egg.gachaType === GachaType.LEGENDARY) {
this.scene.executeWithSeedOffset(() => {
if (!Utils.randSeedInt(2))
speciesOverride = getLegendaryGachaSpeciesForTimestamp(this.scene, this.egg.timestamp);
}, this.egg.id, EGG_SEED.toString());
}
const speciesPool = Object.keys(speciesStarters)
.filter(s => speciesStarters[s] >= minStarterValue && speciesStarters[s] <= maxStarterValue)
.map(s => parseInt(s) as Species)
.filter(s => getPokemonSpecies(s).isObtainable());
if (speciesOverride) {
this.scene.executeWithSeedOffset(() => {
ret = new PlayerPokemon(this.scene, getPokemonSpecies(speciesOverride), 5, null, null, undefined, false);
}, this.egg.id, EGG_SEED.toString());
} else {
let minStarterValue: integer;
let maxStarterValue: integer;
let totalWeight = 0;
const speciesWeights = [];
for (let speciesId of speciesPool) {
const weight = Math.floor((((maxStarterValue - speciesStarters[speciesId]) / ((maxStarterValue - minStarterValue) + 1)) * 1.5 + 1) * 100);
speciesWeights.push(totalWeight + weight);
totalWeight += weight;
}
let species: Species;
this.scene.executeWithSeedOffset(() => {
const rand = Utils.randSeedInt(totalWeight);
for (let s = 0; s < speciesWeights.length; s++) {
if (rand < speciesWeights[s]) {
species = speciesPool[s];
switch (this.egg.tier) {
case ModifierTier.GREAT:
minStarterValue = 4;
maxStarterValue = 5;
break;
case ModifierTier.ULTRA:
minStarterValue = 6;
maxStarterValue = 7;
break;
case ModifierTier.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 => getPokemonSpecies(s).isObtainable() && ignoredSpecies.indexOf(s) === -1);
if (this.egg.gachaType === GachaType.TYPE) {
let tryOverrideType: boolean;
this.scene.executeWithSeedOffset(() => {
tryOverrideType = !Utils.randSeedInt(2);
}, this.egg.id, EGG_SEED.toString());
if (tryOverrideType) {
const type = getTypeGachaTypeForTimestamp(this.scene, this.egg.timestamp);
const typeFilteredSpeciesPool = speciesPool
.filter(s => getPokemonSpecies(s).isOfType(type));
if (typeFilteredSpeciesPool.length)
speciesPool = typeFilteredSpeciesPool;
}
}
}, this.egg.id);
console.log(species, totalWeight);
let totalWeight = 0;
const speciesWeights = [];
for (let speciesId of speciesPool) {
const weight = Math.floor((((maxStarterValue - speciesStarters[speciesId]) / ((maxStarterValue - minStarterValue) + 1)) * 1.5 + 1) * 100);
speciesWeights.push(totalWeight + weight);
totalWeight += weight;
}
const pokemon = new PlayerPokemon(this.scene, getPokemonSpecies(species), 5, null, null);
let species: Species;
return pokemon;
this.scene.executeWithSeedOffset(() => {
const rand = Utils.randSeedInt(totalWeight);
for (let s = 0; s < speciesWeights.length; s++) {
if (rand < speciesWeights[s]) {
species = speciesPool[s];
break;
}
}
ret = new PlayerPokemon(this.scene, getPokemonSpecies(species), 5, null, null, undefined, false);
}, this.egg.id, EGG_SEED.toString());
}
ret.trySetShiny(this.egg.gachaType === GachaType.SHINY ? 1024 : 512);
return ret;
}
}

View File

@ -2,6 +2,7 @@ import Phaser from 'phaser';
import BattleScene from './battle-scene';
import InvertPostFX from './pipelines/invert';
import { version } from '../package.json';
import BBCodeTextPlugin from 'phaser3-rex-plugins/plugins/bbcodetext-plugin';
const config: Phaser.Types.Core.GameConfig = {
type: Phaser.WEBGL,
@ -11,6 +12,13 @@ const config: Phaser.Types.Core.GameConfig = {
height: 1080,
mode: Phaser.Scale.FIT
},
plugins: {
global: [{
key: 'rexBBCodeTextPlugin',
plugin: BBCodeTextPlugin,
start: true
}]
},
pixelArt: true,
pipeline: [ InvertPostFX ] as unknown as Phaser.Types.Core.PipelineConfig,
scene: [ BattleScene ],

View File

@ -15,6 +15,7 @@ import { GameMode } from '../game-mode';
import { StatusEffect, getStatusEffectDescriptor } from '../data/status-effect';
import { SpeciesFormKey } from '../data/pokemon-species';
import BattleScene from '../battle-scene';
import { VoucherType, getVoucherTypeIcon, getVoucherTypeName } from '../system/voucher';
type Modifier = Modifiers.Modifier;
@ -96,6 +97,13 @@ class AddPokeballModifierType extends ModifierType {
}
}
class AddVoucherModifierType extends ModifierType {
constructor(voucherType: VoucherType, count: integer) {
super(`${count}x ${getVoucherTypeName(voucherType)}`, `Receive ${getVoucherTypeName(voucherType)} x${count}`,
(_type, _args) => new Modifiers.AddVoucherModifier(this, voucherType, count), getVoucherTypeIcon(voucherType), 'voucher');
}
}
export class PokemonModifierType extends ModifierType {
public selectFilter: PokemonSelectFilter;
@ -702,6 +710,10 @@ export const modifierTypes = {
MINI_BLACK_HOLE: () => new TurnHeldItemTransferModifierType('Mini Black Hole'),
VOUCHER: () => new AddVoucherModifierType(VoucherType.REGULAR, 1),
VOUCHER_PLUS: () => new AddVoucherModifierType(VoucherType.PLUS, 1),
VOUCHER_PREMIUM: () => new AddVoucherModifierType(VoucherType.PREMIUM, 1),
GOLDEN_POKEBALL: () => new ModifierType(`Golden ${getPokeballName(PokeballType.POKEBALL)}`, 'Adds 1 extra item option at the end of every battle',
(type, _args) => new Modifiers.ExtraModifierModifier(type), 'pb_gold', null, 'pb_bounce_1'),
@ -811,16 +823,17 @@ const modifierPool = {
new WeightedModifierType(modifierTypes.ABILITY_CHARM, 2),
new WeightedModifierType(modifierTypes.IV_SCANNER, 2),
new WeightedModifierType(modifierTypes.EXP_BALANCE, 1),
new WeightedModifierType(modifierTypes.COIN_CASE, 1),
new WeightedModifierType(modifierTypes.MEGA_EVOLUTION_ITEM, (party: Pokemon[]) => party[0].scene.getModifiers(Modifiers.MegaEvolutionAccessModifier).length && !party.filter(p => p.getFormKey().indexOf(SpeciesFormKey.MEGA) > -1).length ? 1 : 0),
new WeightedModifierType(modifierTypes.REVERSE_DNA_SPLICERS, (party: Pokemon[]) => party[0].scene.gameMode !== GameMode.SPLICED_ENDLESS && party.filter(p => p.fusionSpecies).length ? 3 : 0),
].map(m => { m.setTier(ModifierTier.ULTRA); return m; }),
[ModifierTier.MASTER]: [
new WeightedModifierType(modifierTypes.MASTER_BALL, 3),
new WeightedModifierType(modifierTypes.SHINY_CHARM, 2),
new WeightedModifierType(modifierTypes.MEGA_BRACELET, 1),
new WeightedModifierType(modifierTypes.DNA_SPLICERS, (party: Pokemon[]) => party[0].scene.gameMode !== GameMode.SPLICED_ENDLESS && party.filter(p => !p.fusionSpecies).length > 1 ? 1 : 0),
new WeightedModifierType(modifierTypes.MINI_BLACK_HOLE, (party: Pokemon[]) => party[0].scene.gameData.unlocks[Unlockables.MINI_BLACK_HOLE] ? 1 : 0),
new WeightedModifierType(modifierTypes.MASTER_BALL, 32),
new WeightedModifierType(modifierTypes.SHINY_CHARM, 18),
new WeightedModifierType(modifierTypes.MEGA_BRACELET, 12),
new WeightedModifierType(modifierTypes.VOUCHER, 6),
new WeightedModifierType(modifierTypes.VOUCHER_PLUS, 1),
new WeightedModifierType(modifierTypes.DNA_SPLICERS, (party: Pokemon[]) => party[0].scene.gameMode !== GameMode.SPLICED_ENDLESS && party.filter(p => !p.fusionSpecies).length > 1 ? 12 : 0),
new WeightedModifierType(modifierTypes.MINI_BLACK_HOLE, (party: Pokemon[]) => party[0].scene.gameData.unlocks[Unlockables.MINI_BLACK_HOLE] ? 2 : 0),
].map(m => { m.setTier(ModifierTier.MASTER); return m; }),
[ModifierTier.LUXURY]: [
new WeightedModifierType(modifierTypes.GOLDEN_EXP_CHARM, 1),

View File

@ -15,6 +15,7 @@ import { TempBattleStat } from '../data/temp-battle-stat';
import { BerryType, getBerryEffectFunc, getBerryPredicate } from '../data/berry';
import { StatusEffect, getStatusEffectDescriptor } from '../data/status-effect';
import { MoneyAchv } from '../system/achv';
import { VoucherType } from '../system/voucher';
type ModifierType = ModifierTypes.ModifierType;
export type ModifierPredicate = (modifier: Modifier) => boolean;
@ -234,6 +235,25 @@ export class AddPokeballModifier extends ConsumableModifier {
}
}
export class AddVoucherModifier extends ConsumableModifier {
private voucherType: VoucherType;
private count: integer;
constructor(type: ModifierType, voucherType: VoucherType, count: integer) {
super(type);
this.voucherType = voucherType;
this.count = count;
}
apply(args: any[]): boolean {
const voucherCounts = (args[0] as BattleScene).gameData.voucherCounts;
voucherCounts[this.voucherType] += this.count;
return true;
}
}
export abstract class LapsingPersistentModifier extends PersistentModifier {
protected battlesLeft: integer;
@ -1299,7 +1319,7 @@ export class ShinyRateBoosterModifier extends PersistentModifier {
}
apply(args: any[]): boolean {
(args[0] as Utils.IntegerHolder).value /= Math.pow(2, -3 - this.getStackCount());
(args[0] as Utils.IntegerHolder).value *= Math.pow(2, 2 + this.getStackCount());
return true;
}

View File

@ -271,7 +271,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
const originalWarn = console.warn;
// Ignore warnings for missing frames, because there will be a lot
console.warn = () => {};
const battleFrameNames = this.scene.anims.generateFrameNames(this.getBattleSpriteKey(), { zeroPad: 4, suffix: ".png", start: 1, end: 256 });
const battleFrameNames = this.scene.anims.generateFrameNames(this.getBattleSpriteKey(), { zeroPad: 4, suffix: ".png", start: 1, end: 400 });
console.warn = originalWarn;
this.scene.anims.create({
key: this.getBattleSpriteKey(),
@ -686,7 +686,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
this.summonData.moveset[moveIndex] = move;
}
trySetShiny(): boolean {
trySetShiny(thresholdOverride?: integer): boolean {
const rand1 = Utils.binToDec(Utils.decToBin(this.id).substring(0, 16));
const rand2 = Utils.binToDec(Utils.decToBin(this.id).substring(16, 32));
@ -694,10 +694,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
const F = rand1 ^ rand2;
let shinyThreshold = new Utils.IntegerHolder(32);
if (!this.hasTrainer()) {
this.scene.applyModifiers(ShinyRateBoosterModifier, true, shinyThreshold);
console.log(shinyThreshold.value);
}
if (thresholdOverride === undefined) {
if (!this.hasTrainer()) {
this.scene.applyModifiers(ShinyRateBoosterModifier, true, shinyThreshold);
console.log(shinyThreshold.value, 'SHINY THRESHOLD');
}
} else
shinyThreshold.value = thresholdOverride;
this.shiny = (E ^ F) < shinyThreshold.value;
if ((E ^ F) < 32)
@ -1836,11 +1839,13 @@ export class EnemyPokemon extends Pokemon {
constructor(scene: BattleScene, species: PokemonSpecies, level: integer, trainer: boolean, dataSource?: PokemonData) {
super(scene, 236, 84, species, level, dataSource?.abilityIndex, dataSource ? dataSource.formIndex : scene.getSpeciesFormIndex(species),
dataSource?.gender, dataSource?.shiny, null, dataSource);
dataSource?.gender, dataSource ? dataSource.shiny : false, null, dataSource);
this.trainer = trainer;
if (!dataSource) {
this.trySetShiny();
let prevolution: Species;
let speciesId = species.speciesId;
while ((prevolution = pokemonPrevolutions[speciesId])) {

View File

@ -31,6 +31,14 @@ export class Achv {
this.conditionFunc = conditionFunc;
}
getName(): string {
return this.name;
}
getIconImage(): string {
return this.iconImage;
}
setSecret(hasParent?: boolean): this {
this.secret = true;
this.hasParent = !!hasParent;
@ -118,10 +126,13 @@ export const achvs = {
MEGA_EVOLVE: new Achv('Megamorph', 'Mega evolve a Pokémon', 'mega_bracelet', 50),
SPLICE: new Achv('Infinite Fusion', 'Splice two Pokémon together with DNA Splicers', 'dna_splicers', 10),
MINI_BLACK_HOLE: new ModifierAchv('A Hole Lot of Items', 'Acquire a Mini Black Hole', 'mini_black_hole', 25, modifier => modifier instanceof TurnHeldItemTransferModifier).setSecret(),
CATCH_LEGENDARY: new Achv('Legendary', 'Catch a legendary Pokémon', 'mb', 50).setSecret(),
CATCH_MYTHICAL: new Achv('Mythical', 'Catch a mythical Pokémon', 'strange_ball', 50).setSecret(),
CATCH_LEGENDARY: new Achv('Legendary', 'Catch a legendary Pokémon', 'mb', 75).setSecret(),
SEE_SHINY: new Achv('Shiny', 'Find a shiny Pokémon in the wild', 'pb_gold', 75),
SHINY_PARTY: new Achv('That\'s Dedication', 'Have a full party of shiny Pokémon', 'shiny_charm', 100).setSecret(true),
HATCH_MYTHICAL: new Achv('Mythical Egg', 'Hatch a mythical Pokémon from an egg', 'pair_of_tickets', 75).setSecret(),
HATCH_LEGENDARY: new Achv('Legendary Egg', 'Hatch a legendary Pokémon from an egg', 'mystic_ticket', 100).setSecret(),
HATCH_SHINY: new Achv('Shiny Egg', 'Hatch a shiny Pokémon from an egg', 'golden_mystic_ticket', 100).setSecret(),
HIDDEN_ABILITY: new Achv('Hidden Potential', 'Catch a Pokémon with a hidden ability', 'ability_charm', 75),
PERFECT_IVS: new Achv('Certificate of Authenticity', 'Get perfect IVs on a Pokémon', 'blunder_policy', 100),
CLASSIC_VICTORY: new Achv('Undefeated', 'Beat the game in classic mode', 'relic_crown', 150)

View File

@ -3,16 +3,18 @@ import { Egg, GachaType } from "../data/egg";
export default class EggData {
public id: integer;
public gachaType: GachaType;
public hatchWaves: integer;
public timestamp: integer;
constructor(source: Egg | any) {
const sourceEgg = source instanceof Egg ? source as Egg : null;
this.id = sourceEgg ? sourceEgg.id : source.id;
this.gachaType = sourceEgg ? sourceEgg.gachaType : source.gachaType;
this.hatchWaves = sourceEgg ? sourceEgg.hatchWaves : source.hatchWaves;
this.timestamp = sourceEgg ? sourceEgg.timestamp : source.timestamp;
}
toEgg(): Egg {
return new Egg(this.id, this.gachaType, this.timestamp);
return new Egg(this.id, this.gachaType, this.hatchWaves, this.timestamp);
}
}

View File

@ -16,6 +16,7 @@ import { Setting, setSetting, settingDefaults } from "./settings";
import { achvs } from "./achv";
import EggData from "./egg-data";
import { Egg } from "../data/egg";
import { VoucherType, vouchers } from "./voucher";
interface SystemSaveData {
trainerId: integer;
@ -23,6 +24,8 @@ interface SystemSaveData {
dexData: DexData;
unlocks: Unlocks;
achvUnlocks: AchvUnlocks;
voucherUnlocks: VoucherUnlocks;
voucherCounts: VoucherCounts;
eggs: EggData[];
gameVersion: string;
timestamp: integer;
@ -54,6 +57,14 @@ interface AchvUnlocks {
[key: string]: integer
}
interface VoucherUnlocks {
[key: string]: integer
}
export interface VoucherCounts {
[type: string]: integer;
}
export interface DexData {
[key: integer]: DexEntry
}
@ -96,6 +107,8 @@ export class GameData {
public achvUnlocks: AchvUnlocks;
public voucherUnlocks: VoucherUnlocks;
public voucherCounts: VoucherCounts;
public eggs: Egg[];
constructor(scene: BattleScene) {
@ -109,6 +122,12 @@ export class GameData {
[Unlockables.SPLICED_ENDLESS_MODE]: false
};
this.achvUnlocks = {};
this.voucherUnlocks = {};
this.voucherCounts = {
[VoucherType.REGULAR]: 0,
[VoucherType.PLUS]: 0,
[VoucherType.PREMIUM]: 0
};
this.eggs = [];
this.initDexData();
this.loadSystem();
@ -124,6 +143,8 @@ export class GameData {
dexData: this.dexData,
unlocks: this.unlocks,
achvUnlocks: this.achvUnlocks,
voucherUnlocks: this.voucherUnlocks,
voucherCounts: this.voucherCounts,
eggs: this.eggs.map(e => new EggData(e)),
gameVersion: this.scene.game.config.gameVersion,
timestamp: new Date().getTime()
@ -141,7 +162,16 @@ export class GameData {
if (!localStorage.hasOwnProperty('data'))
return false;
const data = JSON.parse(atob(localStorage.getItem('data')), (k: string, v: any) => k.endsWith('Attr') ? BigInt(v) : v) as SystemSaveData;
const data = JSON.parse(atob(localStorage.getItem('data')), (k: string, v: any) => {
if (k === 'eggs') {
const ret: EggData[] = [];
for (let e of v)
ret.push(new EggData(e));
return ret;
}
return k.endsWith('Attr') ? BigInt(v) : v;
}) as SystemSaveData;
console.debug(data);
@ -168,6 +198,20 @@ export class GameData {
}
}
if (data.voucherUnlocks) {
for (let v of Object.keys(data.voucherUnlocks)) {
if (vouchers.hasOwnProperty(v))
this.voucherUnlocks[v] = data.voucherUnlocks[v];
}
}
if (data.voucherCounts) {
Utils.getEnumKeys(VoucherType).forEach(key => {
const index = VoucherType[key];
this.voucherCounts[index] = data.voucherCounts[index] || 0;
});
}
this.eggs = data.eggs
? data.eggs.map(e => e.toEgg())
: [];
@ -423,6 +467,20 @@ export class GameData {
});
}
updateSpeciesDexIvs(speciesId: Species, ivs: integer[]): void {
let dexEntry: DexEntry;
do {
dexEntry = this.scene.gameData.dexData[speciesId];
const dexIvs = dexEntry.ivs;
for (let i = 0; i < dexIvs.length; i++) {
if (dexIvs[i] < ivs[i])
dexIvs[i] = ivs[i];
}
if (dexIvs.filter(iv => iv === 31).length === 6)
this.scene.validateAchv(achvs.PERFECT_IVS);
} while (pokemonPrevolutions.hasOwnProperty(speciesId) && (speciesId = pokemonPrevolutions[speciesId]));
}
getSpeciesDefaultDexAttr(species: PokemonSpecies): bigint {
let ret = 0n;
const dexEntry = this.dexData[species.speciesId];

106
src/system/voucher.ts Normal file
View File

@ -0,0 +1,106 @@
import BattleScene from "../battle-scene";
import { TrainerType, trainerConfigs } from "../data/trainer-type";
import { ModifierTier } from "../modifier/modifier-type";
import { Achv, achvs } from "./achv";
export enum VoucherType {
REGULAR,
PLUS,
PREMIUM
}
export class Voucher {
public id: string;
public voucherType: VoucherType;
public description: string;
private conditionFunc: (scene: BattleScene, args: any[]) => boolean;
constructor(voucherType: VoucherType, description: string, conditionFunc?: (scene: BattleScene, args: any[]) => boolean) {
this.description = description;
this.voucherType = voucherType;
this.conditionFunc = conditionFunc;
}
validate(scene: BattleScene, args: any[]): boolean {
return !this.conditionFunc || this.conditionFunc(scene, args);
}
getName(): string {
return getVoucherTypeName(this.voucherType);
}
getIconImage(): string {
return getVoucherTypeIcon(this.voucherType);
}
getTier(): ModifierTier {
switch (this.voucherType) {
case VoucherType.REGULAR:
return ModifierTier.GREAT;
case VoucherType.PLUS:
return ModifierTier.ULTRA;
case VoucherType.PREMIUM:
return ModifierTier.MASTER;
}
}
}
export function getVoucherTypeName(voucherType: VoucherType): string {
switch (voucherType) {
case VoucherType.REGULAR:
return 'Egg Voucher';
case VoucherType.PLUS:
return 'Egg Voucher Plus';
case VoucherType.PREMIUM:
return 'Egg Voucher Premium';
}
}
export function getVoucherTypeIcon(voucherType: VoucherType): string {
switch (voucherType) {
case VoucherType.REGULAR:
return 'coupon';
case VoucherType.PLUS:
return 'pair_of_tickets';
case VoucherType.PREMIUM:
return 'mystic_ticket';
}
}
export interface Vouchers {
[key: string]: Voucher
}
export const vouchers: Vouchers = {};
const voucherAchvs: Achv[] = [ achvs.CLASSIC_VICTORY ];
{
(function() {
const bossTrainerTypes = Object.keys(trainerConfigs)
.filter(tt => trainerConfigs[tt].isBoss && trainerConfigs[tt].getDerivedType() !== TrainerType.RIVAL);
for (let trainerType of bossTrainerTypes) {
const voucherType = trainerConfigs[trainerType].moneyMultiplier < 10
? VoucherType.REGULAR
: VoucherType.PLUS;
const key = TrainerType[trainerType];
vouchers[key] = new Voucher(voucherType, `Defeat ${trainerConfigs[trainerType].name}`);
}
for (let achv of voucherAchvs) {
const voucherType = achv.score >= 150
? VoucherType.PREMIUM
: achv.score >= 100
? VoucherType.PLUS
: VoucherType.REGULAR;
vouchers[achv.id] = new Voucher(voucherType, achv.description);
}
const voucherKeys = Object.keys(vouchers);
for (let k of voucherKeys)
vouchers[k].id = k;
})();
}

View File

@ -1,5 +1,6 @@
import BattleScene from "../battle-scene";
import { Achv } from "../system/achv";
import { Voucher } from "../system/voucher";
import { TextStyle, addTextObject } from "./text";
export default class AchvBar extends Phaser.GameObjects.Container {
@ -9,7 +10,7 @@ export default class AchvBar extends Phaser.GameObjects.Container {
private scoreText: Phaser.GameObjects.Text;
private descriptionText: Phaser.GameObjects.Text;
private queue: Achv[] = [];
private queue: (Achv | Voucher)[] = [];
public shown: boolean;
@ -47,7 +48,7 @@ export default class AchvBar extends Phaser.GameObjects.Container {
this.shown = false;
}
showAchv(achv: Achv): void {
showAchv(achv: Achv | Voucher): void {
if (this.shown) {
this.queue.push(achv);
return;
@ -56,10 +57,12 @@ export default class AchvBar extends Phaser.GameObjects.Container {
const tier = achv.getTier();
this.bg.setTexture(`achv_bar${tier ? `_${tier + 1}` : ''}`);
this.icon.setFrame(achv.iconImage);
this.titleText.setText(achv.name);
this.icon.setFrame(achv.getIconImage());
this.titleText.setText(achv.getName());
this.descriptionText.setText(achv.description);
this.scoreText.setText(`+${achv.score}pt`);
if (achv instanceof Achv)
this.scoreText.setText(`+${(achv as Achv).score}pt`);
(this.scene as BattleScene).playSound('achv');

View File

@ -127,10 +127,10 @@ export default class CommandUiHandler extends UiHandler {
if (!this.cursorObj) {
this.cursorObj = this.scene.add.image(0, 0, 'cursor');
ui.add(this.cursorObj);
this.commandsContainer.add(this.cursorObj);
}
this.cursorObj.setPosition(211 + (cursor % 2 === 1 ? 56 : 0), -31 + (cursor >= 2 ? 16 : 0));
this.cursorObj.setPosition(-5 + (cursor % 2 === 1 ? 56 : 0), 8 + (cursor >= 2 ? 16 : 0));
return changed;
}

View File

@ -0,0 +1,600 @@
import BattleScene, { Button } from "../battle-scene";
import { Mode } from "./ui";
import { TextStyle, addTextObject, getModifierTierTextTint } from "./text";
import MessageUiHandler from "./message-ui-handler";
import * as Utils from "../utils";
import { ModifierTier } from "../modifier/modifier-type";
import { EGG_SEED, Egg, GachaType, getEggTierDefaultHatchWaves, getEggDescriptor, getLegendaryGachaSpeciesForTimestamp, getTypeGachaTypeForTimestamp } from "../data/egg";
import { VoucherType, getVoucherTypeIcon } from "../system/voucher";
import { getPokemonSpecies } from "../data/pokemon-species";
import { Type } from "../data/type";
const defaultText = 'Select a machine.';
export default class EggGachaUiHandler extends MessageUiHandler {
private eggGachaContainer: Phaser.GameObjects.Container;
private eggGachaMessageBox: Phaser.GameObjects.NineSlice;
private eggGachaOptionsContainer: Phaser.GameObjects.Container;
private eggGachaOptionSelectBg: Phaser.GameObjects.NineSlice;
private gachaContainers: Phaser.GameObjects.Container[];
private gachaKnobs: Phaser.GameObjects.Sprite[];
private gachaHatches: Phaser.GameObjects.Sprite[];
private gachaInfoContainers: Phaser.GameObjects.Container[];
private eggGachaOverlay: Phaser.GameObjects.Rectangle;
private eggGachaSummaryContainer: Phaser.GameObjects.Container;
private voucherCountLabels: Phaser.GameObjects.Text[];
private gachaCursor: integer;
private cursorObj: Phaser.GameObjects.Image;
private transitioning: boolean;
private transitionCancelled: boolean;
constructor(scene: BattleScene) {
super(scene, Mode.EGG_GACHA);
this.gachaContainers = [];
this.gachaKnobs = [];
this.gachaHatches = [];
this.gachaInfoContainers = [];
this.voucherCountLabels = [];
}
setup() {
this.gachaCursor = 0;
const ui = this.getUi();
this.eggGachaContainer = this.scene.add.container(0, -this.scene.game.canvas.height / 6);
this.eggGachaContainer.setVisible(false);
ui.add(this.eggGachaContainer);
const bg = this.scene.add.nineslice(0, 0, 'default_bg', null, 320, 180, 0, 0, 16, 0);
bg.setOrigin(0, 0);
this.eggGachaContainer.add(bg);
const hatchFrameNames = this.scene.anims.generateFrameNames('gacha_hatch', { suffix: ".png", start: 1, end: 4 });
this.scene.anims.create({
key: 'open',
frames: hatchFrameNames,
frameRate: 12
});
this.scene.anims.create({
key: 'close',
frames: hatchFrameNames.reverse(),
frameRate: 12
});
Utils.getEnumValues(GachaType).forEach((gachaType, g) => {
const gachaTypeKey = GachaType[gachaType].toString().toLowerCase();
const gachaContainer = this.scene.add.container(180 * g, 18);
const gacha = this.scene.add.sprite(0, 0, `gacha_${gachaTypeKey}`);
gacha.setOrigin(0, 0);
const gachaUnderlay = this.scene.add.sprite(115, 80, `gacha_underlay_${gachaTypeKey}`);
gachaUnderlay.setOrigin(0, 0);
const gachaGlass = this.scene.add.sprite(0, 0, 'gacha_glass');
gachaGlass.setOrigin(0, 0);
const gachaInfoContainer = this.scene.add.container(160, 46);
const gachaUpLabel = addTextObject(this.scene, 4, 0, 'UP!', TextStyle.WINDOW);
gachaUpLabel.setOrigin(0, 0);
gachaInfoContainer.add(gachaUpLabel);
switch (gachaType as GachaType) {
case GachaType.LEGENDARY:
const pokemonIcon = this.scene.add.sprite(-20, 6, 'pokemon_icons_0');
pokemonIcon.setScale(0.5);
pokemonIcon.setOrigin(0, 0.5);
gachaInfoContainer.add(pokemonIcon);
break;
case GachaType.TYPE:
const typeIcon = this.scene.add.sprite(-22, 7, 'types', 'unknown');
typeIcon.setScale(0.75);
typeIcon.setOrigin(0, 0.5);
gachaUpLabel.x += 4;
gachaInfoContainer.add(typeIcon);
break;
case GachaType.SHINY:
gachaUpLabel.setText('Shiny UP!');
gachaUpLabel.setX(0);
gachaUpLabel.setOrigin(0.5, 0);
break;
}
const gachaKnob = this.scene.add.sprite(191, 89, 'gacha_knob');
const gachaHatch = this.scene.add.sprite(115, 73, 'gacha_hatch');
gachaHatch.setOrigin(0, 0);
gachaContainer.add(gachaUnderlay);
gachaContainer.add(gacha);
gachaContainer.add(gachaGlass);
gachaContainer.add(gachaKnob);
gachaContainer.add(gachaHatch);
gachaContainer.add(gachaInfoContainer);
gachaGlass.setAlpha(0.5);
gachaHatch.setAlpha(0.9);
gachaHatch.on('animationupdate', (_anim, frame) => gachaUnderlay.setFrame(frame.textureFrame === '4.png' ? 'open_hatch' : 'default'));
this.gachaContainers.push(gachaContainer);
this.gachaKnobs.push(gachaKnob);
this.gachaHatches.push(gachaHatch);
this.gachaInfoContainers.push(gachaInfoContainer);
this.eggGachaContainer.add(gachaContainer);
this.updateGachaInfo(g);
});
this.eggGachaOptionsContainer = this.scene.add.container()
this.eggGachaOptionsContainer = this.scene.add.container((this.scene.game.canvas.width / 6), 148);
this.eggGachaContainer.add(this.eggGachaOptionsContainer);
this.eggGachaOptionSelectBg = this.scene.add.nineslice(0, 0, 'window', null, 96, 96, 6, 6, 6, 6);
this.eggGachaOptionSelectBg.setOrigin(1, 1);
this.eggGachaOptionsContainer.add(this.eggGachaOptionSelectBg);
const optionText = addTextObject(this.scene, 0, 0, ' x1 1 Pull\n x10 10 Pulls\n x1 5 Pulls\n x1 10 Pulls\nCancel', TextStyle.WINDOW, { maxLines: 5 });
optionText.setLineSpacing(12);
this.eggGachaOptionsContainer.add(optionText);
optionText.setPositionRelative(this.eggGachaOptionSelectBg, 16, 9);
new Array(4).fill(null).map((_, i) => {
const voucherType = i < 2 ? VoucherType.REGULAR : i === 2 ? VoucherType.PLUS : VoucherType.PREMIUM;
const icon = this.scene.add.sprite(0, 0, 'items', getVoucherTypeIcon(voucherType));
icon.setScale(0.5);
icon.setPositionRelative(this.eggGachaOptionSelectBg, 20, 17 + i * 16);
this.eggGachaOptionsContainer.add(icon);
});
this.eggGachaContainer.add(this.eggGachaOptionsContainer);
new Array(Utils.getEnumKeys(VoucherType).length).fill(null).map((_, i) => {
const container = this.scene.add.container((this.scene.game.canvas.width / 6) - 56 * i, 0);
const bg = this.scene.add.nineslice(0, 0, 'window', null, 56, 22, 6, 6, 6, 6);
bg.setOrigin(1, 0);
container.add(bg);
const countLabel = addTextObject(this.scene, -48, 3, '0', TextStyle.WINDOW);
countLabel.setOrigin(0, 0);
container.add(countLabel);
this.voucherCountLabels.push(countLabel);
const iconImage = getVoucherTypeIcon(i as VoucherType);
const icon = this.scene.add.sprite(-19, 2, 'items', iconImage);
icon.setOrigin(0, 0);
icon.setScale(0.5);
container.add(icon);
this.eggGachaContainer.add(container);
});
this.eggGachaOverlay = this.scene.add.rectangle(0, 0, bg.displayWidth, bg.displayHeight, 0x000000);
this.eggGachaOverlay.setOrigin(0, 0);
this.eggGachaOverlay.setAlpha(0);
this.eggGachaContainer.add(this.eggGachaOverlay);
this.eggGachaSummaryContainer = this.scene.add.container(0, 0);
this.eggGachaSummaryContainer.setVisible(false);
this.eggGachaContainer.add(this.eggGachaSummaryContainer);
const gachaMessageBoxContainer = this.scene.add.container(0, 148);
this.eggGachaContainer.add(gachaMessageBoxContainer);
const gachaMessageBox = this.scene.add.nineslice(0, 0, 'window', null, 320, 32, 6, 6, 6, 6);
gachaMessageBox.setOrigin(0, 0);
gachaMessageBoxContainer.add(gachaMessageBox);
this.eggGachaMessageBox = gachaMessageBox;
const gachaMessageText = addTextObject(this.scene, 8, 8, '', TextStyle.WINDOW, { maxLines: 2 });
gachaMessageText.setOrigin(0, 0);
gachaMessageBoxContainer.add(gachaMessageText);
this.message = gachaMessageText;
this.eggGachaContainer.add(gachaMessageBoxContainer);
this.setCursor(0);
}
show(args: any[]): void {
super.show(args);
this.getUi().showText(defaultText, 0);
this.setGachaCursor(1);
for (let g = 0; g < this.gachaContainers.length; g++)
this.updateGachaInfo(g);
this.updateVoucherCounts();
this.eggGachaContainer.setVisible(true);
}
getDelayValue(delay: integer) {
if (this.transitioning && this.transitionCancelled)
delay = Math.ceil(delay / 5);
return Utils.fixedInt(delay);
}
pull(pullCount?: integer, count?: integer, eggs?: Egg[]) {
this.eggGachaOptionsContainer.setVisible(false);
this.setTransitioning(true);
if (!pullCount)
pullCount = 1;
if (!count)
count = 0;
if (!eggs) {
eggs = [];
const tiers = new Array(pullCount).fill(null).map(() => {
const tierValue = Utils.randInt(256);
return tierValue >= 52 ? ModifierTier.COMMON : tierValue >= 8 ? ModifierTier.GREAT : tierValue >= 1 ? ModifierTier.ULTRA : ModifierTier.MASTER;
});
/*if (pullCount >= 100 && !tiers.filter(t => t >= ModifierTier.ULTRA).length)
tiers[Utils.randInt(tiers.length)] = ModifierTier.ULTRA;*/
if (pullCount >= 10 && !tiers.filter(t => t >= ModifierTier.GREAT).length)
tiers[Utils.randInt(tiers.length)] = ModifierTier.GREAT;
const timestamp = new Date().getTime();
for (let tier of tiers) {
const egg = new Egg(Utils.randInt(EGG_SEED, EGG_SEED * tier), this.gachaCursor, getEggTierDefaultHatchWaves(tier), timestamp);
if (egg.isManaphyEgg())
egg.hatchWaves = getEggTierDefaultHatchWaves(ModifierTier.ULTRA);
eggs.push(egg);
this.scene.gameData.eggs.push(egg);
}
this.scene.gameData.saveSystem();
}
if (this.transitionCancelled) {
return this.showSummary(eggs);
}
const egg = this.scene.add.sprite(127, 75, 'egg', `egg_${eggs[count].getKey()}`);
egg.setScale(0.5);
this.gachaContainers[this.gachaCursor].add(egg);
this.gachaContainers[this.gachaCursor].moveTo(egg, 1);
const doPullAnim = () => {
this.scene.playSound('gacha_running', { loop: true });
this.scene.time.delayedCall(this.getDelayValue(count ? 500 : 1250), () => {
this.scene.playSound('gacha_dispense');
this.scene.time.delayedCall(this.getDelayValue(750), () => {
this.scene.sound.stopByKey('gacha_running');
this.scene.tweens.add({
targets: egg,
duration: this.getDelayValue(350),
y: 95,
ease: 'Bounce.easeOut',
onComplete: () => {
this.scene.time.delayedCall(this.getDelayValue(125), () => {
this.scene.playSound('pb_catch');
this.gachaHatches[this.gachaCursor].play('open');
this.scene.tweens.add({
targets: egg,
duration: this.getDelayValue(350),
scale: 0.75,
ease: 'Sine.easeIn'
});
this.scene.tweens.add({
targets: egg,
y: 110,
duration: this.getDelayValue(350),
ease: 'Back.easeOut',
onComplete: () => {
this.gachaHatches[this.gachaCursor].play('close');
this.scene.tweens.add({
targets: egg,
y: 200,
duration: this.getDelayValue(350),
ease: 'Cubic.easeIn',
onComplete: () => {
if (++count < pullCount)
this.pull(pullCount, count, eggs);
else
this.showSummary(eggs);
}
});
}
});
});
}
});
});
});
};
if (!count) {
this.scene.playSound('gacha_dial');
this.scene.tweens.add({
targets: this.gachaKnobs[this.gachaCursor],
duration: this.getDelayValue(350),
angle: 90,
ease: 'Cubic.easeInOut',
onComplete: () => {
this.scene.tweens.add({
targets: this.gachaKnobs[this.gachaCursor],
duration: this.getDelayValue(350),
angle: 0,
ease: 'Sine.easeInOut'
});
this.scene.time.delayedCall(this.getDelayValue(350), doPullAnim);
}
});
} else
doPullAnim();
}
showSummary(eggs: Egg[]): void {
this.transitioning = false;
this.eggGachaSummaryContainer.setVisible(true);
this.scene.tweens.add({
targets: this.eggGachaOverlay,
alpha: 0.5,
ease: 'Sine.easeOut',
duration: 750,
onComplete: () => {
const rows = Math.ceil(eggs.length / 5);
const cols = Math.min(eggs.length, 5);
const height = this.eggGachaOverlay.displayHeight - this.eggGachaMessageBox.displayHeight;
const eggContainers = eggs.map((egg, t) => {
const col = t % 5;
const row = Math.floor(t / 5);
const sliceWidth = this.eggGachaOverlay.displayWidth / (cols + 2);
const sliceHeight = height / (rows + 2);
const yOffset = (sliceHeight / 2 * (row / Math.max(rows - 1, 1))) + sliceHeight / 4;
const ret = this.scene.add.container(sliceWidth * (col + 1) + (sliceWidth * 0.5), sliceHeight * (row + 1) + yOffset);
ret.setScale(0.0001);
const eggSprite = this.scene.add.sprite(0, 0, 'egg', `egg_${egg.getKey()}`);
ret.add(eggSprite);
const eggText = addTextObject(this.scene, 0, 14, getEggDescriptor(egg), TextStyle.PARTY, { align: 'center' });
eggText.setOrigin(0.5, 0);
eggText.setTint(getModifierTierTextTint(!egg.isManaphyEgg() ? egg.tier : ModifierTier.ULTRA));
ret.add(eggText);
this.eggGachaSummaryContainer.add(ret);
return ret;
});
eggContainers.forEach((eggContainer, e) => {
this.scene.tweens.add({
targets: eggContainer,
delay: this.getDelayValue(e * 100),
duration: this.getDelayValue(350),
scale: 1,
ease: 'Sine.easeOut'
});
});
}
});
}
hideSummary() {
this.setTransitioning(true);
this.scene.tweens.add({
targets: [ this.eggGachaOverlay, this.eggGachaSummaryContainer ],
alpha: 0,
duration: this.getDelayValue(250),
ease: 'Cubic.easeIn',
onComplete: () => {
this.eggGachaSummaryContainer.setVisible(false);
this.eggGachaSummaryContainer.setAlpha(1);
this.eggGachaSummaryContainer.removeAll(true);
this.setTransitioning(false);
this.eggGachaOptionsContainer.setVisible(true);
}
});
}
updateGachaInfo(gachaType: GachaType): void {
const infoContainer = this.gachaInfoContainers[gachaType];
switch (gachaType as GachaType) {
case GachaType.LEGENDARY:
const species = getPokemonSpecies(getLegendaryGachaSpeciesForTimestamp(this.scene, new Date().getTime()));
const pokemonIcon = infoContainer.getAt(1) as Phaser.GameObjects.Sprite;
pokemonIcon.setTexture(species.getIconAtlasKey(), species.getIconId(false));
break;
case GachaType.TYPE:
const typeIcon = infoContainer.getAt(1) as Phaser.GameObjects.Sprite;
typeIcon.setFrame(Type[getTypeGachaTypeForTimestamp(this.scene, new Date().getTime())].toLowerCase());
break;
}
}
consumeVouchers(voucherType: VoucherType, count: integer): void {
this.scene.gameData.voucherCounts[voucherType] = Math.max(this.scene.gameData.voucherCounts[voucherType] - count, 0);
this.updateVoucherCounts();
}
updateVoucherCounts(): void {
this.voucherCountLabels.forEach((label, type) => {
label.setText(this.scene.gameData.voucherCounts[type].toString());
});
}
showError(text: string): void {
this.showText(text, null, () => this.showText(defaultText), Utils.fixedInt(1500));
}
setTransitioning(transitioning: boolean): void {
if (this.transitioning === transitioning)
return;
this.transitioning = transitioning;
this.transitionCancelled = false;
}
processInput(button: Button): boolean {
const ui = this.getUi();
let success = false;
let error = false;
if (this.transitioning) {
if (!this.transitionCancelled && (button === Button.ACTION || button === Button.CANCEL)) {
this.transitionCancelled = true;
success = true;
} else
return false;
} else {
if (this.eggGachaSummaryContainer.visible) {
if (button === Button.ACTION || button === Button.CANCEL) {
this.hideSummary();
success = true;
}
} else {
switch (button) {
case Button.ACTION:
switch (this.cursor) {
case 0:
if (!this.scene.gameData.voucherCounts[VoucherType.REGULAR]) {
error = true;
this.showError('You don\'t have enough vouchers!');
} else if (this.scene.gameData.eggs.length < 99) {
this.consumeVouchers(VoucherType.REGULAR, 1);
this.pull();
success = true;
} else {
error = true;
this.showError('You have too many eggs!');
}
break;
case 2:
if (!this.scene.gameData.voucherCounts[VoucherType.PLUS]) {
error = true;
this.showError('You don\'t have enough vouchers!');
} else if (this.scene.gameData.eggs.length < 95) {
this.consumeVouchers(VoucherType.PLUS, 1);
this.pull(5);
success = true;
} else {
error = true;
this.showError('You have too many eggs!');
}
break;
case 1:
case 3:
if ((this.cursor === 1 && this.scene.gameData.voucherCounts[VoucherType.REGULAR] < 10)
|| (this.cursor === 3 && !this.scene.gameData.voucherCounts[VoucherType.PREMIUM])) {
error = true;
this.showError('You don\'t have enough vouchers!');
} else if (this.scene.gameData.eggs.length < 90) {
if (this.cursor === 3)
this.consumeVouchers(VoucherType.PREMIUM, 1);
else
this.consumeVouchers(VoucherType.REGULAR, 10);
this.pull(10);
success = true;
} else {
error = true;
this.showError('You have too many eggs!');
}
break;
case 4:
ui.revertMode();
success = true;
break;
}
break;
case Button.CANCEL:
this.getUi().revertMode();
success = true;
break;
case Button.UP:
if (this.cursor)
success = this.setCursor(this.cursor - 1);
break;
case Button.DOWN:
if (this.cursor < 4)
success = this.setCursor(this.cursor + 1);
break;
case Button.LEFT:
if (this.gachaCursor)
success = this.setGachaCursor(this.gachaCursor - 1);
break;
case Button.RIGHT:
if (this.gachaCursor < Utils.getEnumKeys(GachaType).length - 1)
success = this.setGachaCursor(this.gachaCursor + 1);
break;
}
}
}
if (success)
ui.playSelect();
else if (error)
ui.playError();
return success || error;
}
setCursor(cursor: integer): boolean {
const ret = super.setCursor(cursor);
if (!this.cursorObj) {
this.cursorObj = this.scene.add.image(0, 0, 'cursor');
this.eggGachaOptionsContainer.add(this.cursorObj);
}
this.cursorObj.setPositionRelative(this.eggGachaOptionSelectBg, 10, 17 + this.cursor * 16);
return ret;
}
setGachaCursor(cursor: integer): boolean {
let oldCursor = this.gachaCursor;
let changed = oldCursor !== cursor;
if (changed) {
this.gachaCursor = cursor;
this.setTransitioning(true);
this.scene.tweens.add({
targets: this.gachaContainers,
duration: this.eggGachaContainer.visible ? 500 : 0,
x: (_target, _key, _value, index) => 180 * (index - cursor),
ease: 'Cubic.easeInOut',
onComplete: () => this.setTransitioning(false)
});
}
return changed;
}
clear(): void {
super.clear();
this.setGachaCursor(-1);
this.eggGachaContainer.setVisible(false);
}
}

View File

@ -3,39 +3,40 @@ import { Mode } from "./ui";
import UiHandler from "./uiHandler";
export default class EggHatchSceneHandler extends UiHandler {
public eggHatchContainer: Phaser.GameObjects.Container;
public eggHatchContainer: Phaser.GameObjects.Container;
constructor(scene: BattleScene) {
super(scene, Mode.EGG_HATCH_SCENE);
}
setup() {
this.eggHatchContainer = this.scene.add.container(0, -this.scene.game.canvas.height / 6);
this.scene.fieldUI.add(this.eggHatchContainer);
constructor(scene: BattleScene) {
super(scene, Mode.EGG_HATCH_SCENE);
}
const eggLightraysAnimFrames = this.scene.anims.generateFrameNames('egg_lightrays', { start: 0, end: 3 });
this.scene.anims.create({
key: 'egg_lightrays',
frames: eggLightraysAnimFrames,
frameRate: 32
});
}
setup() {
this.eggHatchContainer = this.scene.add.container(0, -this.scene.game.canvas.height / 6);
this.scene.fieldUI.add(this.eggHatchContainer);
show(_args: any[]): void {
super.show(_args);
this.scene.fieldUI.bringToTop(this.eggHatchContainer);
}
processInput(button: Button): boolean {
return this.scene.ui.getMessageHandler().processInput(button);
}
setCursor(_cursor: integer): boolean {
return false;
}
const eggLightraysAnimFrames = this.scene.anims.generateFrameNames('egg_lightrays', { start: 0, end: 3 });
this.scene.anims.create({
key: 'egg_lightrays',
frames: eggLightraysAnimFrames,
frameRate: 32
});
}
clear() {
this.eggHatchContainer.removeAll(true);
}
}
show(_args: any[]): void {
super.show(_args);
this.getUi().showText(null, 0);
}
processInput(button: Button): boolean {
return this.scene.ui.getMessageHandler().processInput(button);
}
setCursor(_cursor: integer): boolean {
return false;
}
clear() {
super.clear();
this.eggHatchContainer.removeAll(true);
}
}

View File

@ -0,0 +1,201 @@
import BattleScene, { Button } from "../battle-scene";
import { Mode } from "./ui";
import PokemonIconAnimHandler, { PokemonIconAnimMode } from "./pokemon-icon-anim-handler";
import { TextStyle, addTextObject } from "./text";
import MessageUiHandler from "./message-ui-handler";
import { EGG_SEED, Egg, GachaType, getEggGachaTypeDescriptor, getEggHatchWavesMessage, getEggDescriptor } from "../data/egg";
import * as Utils from "../utils";
export default class EggListUiHandler extends MessageUiHandler {
private eggListContainer: Phaser.GameObjects.Container;
private eggListIconContainer: Phaser.GameObjects.Container;
private eggSprite: Phaser.GameObjects.Sprite;
private eggNameText: Phaser.GameObjects.Text;
private eggDateText: Phaser.GameObjects.Text;
private eggHatchWavesText: Phaser.GameObjects.Text;
private eggGachaInfoText: Phaser.GameObjects.Text;
private eggListMessageBoxContainer: Phaser.GameObjects.Container;
private cursorObj: Phaser.GameObjects.Image;
private iconAnimHandler: PokemonIconAnimHandler;
constructor(scene: BattleScene) {
super(scene, Mode.EGG_LIST);
}
setup() {
const ui = this.getUi();
this.eggListContainer = this.scene.add.container(0, -this.scene.game.canvas.height / 6);
this.eggListContainer.setVisible(false);
ui.add(this.eggListContainer);
const bgColor = this.scene.add.rectangle(0, 0, this.scene.game.canvas.width / 6, this.scene.game.canvas.height / 6, 0x006860);
bgColor.setOrigin(0, 0);
this.eggListContainer.add(bgColor);
const starterSelectBg = this.scene.add.image(1, 1, 'egg_list_bg');
starterSelectBg.setOrigin(0, 0);
this.eggListContainer.add(starterSelectBg);
this.iconAnimHandler = new PokemonIconAnimHandler();
this.iconAnimHandler.setup(this.scene);
this.eggNameText = addTextObject(this.scene, 6, 66, '', TextStyle.SUMMARY);
this.eggNameText.setOrigin(0, 0);
this.eggListContainer.add(this.eggNameText);
this.eggDateText = addTextObject(this.scene, 8, 91, '', TextStyle.TOOLTIP_CONTENT);
this.eggListContainer.add(this.eggDateText);
this.eggHatchWavesText = addTextObject(this.scene, 8, 108, '', TextStyle.TOOLTIP_CONTENT);
this.eggHatchWavesText.setWordWrapWidth(540);
this.eggListContainer.add(this.eggHatchWavesText);
this.eggGachaInfoText = addTextObject(this.scene, 8, 152, '', TextStyle.TOOLTIP_CONTENT);
this.eggGachaInfoText.setWordWrapWidth(540);
this.eggListContainer.add(this.eggGachaInfoText);
this.eggListIconContainer = this.scene.add.container(115, 9);
this.eggListContainer.add(this.eggListIconContainer);
this.cursorObj = this.scene.add.image(0, 0, 'starter_select_cursor');
this.cursorObj.setOrigin(0, 0);
this.eggListContainer.add(this.cursorObj);
this.eggSprite = this.scene.add.sprite(54, 37, `egg`);
this.eggListContainer.add(this.eggSprite);
this.eggListMessageBoxContainer = this.scene.add.container(0, this.scene.game.canvas.height / 6);
this.eggListMessageBoxContainer.setVisible(false);
this.eggListContainer.add(this.eggListMessageBoxContainer);
const starterSelectMessageBox = this.scene.add.image(0, 0, 'starter_select_message');
starterSelectMessageBox.setOrigin(0, 1);
this.eggListMessageBoxContainer.add(starterSelectMessageBox);
this.message = addTextObject(this.scene, 8, -8, '', TextStyle.WINDOW, { maxLines: 1 });
this.message.setOrigin(0, 1);
this.eggListMessageBoxContainer.add(this.message);
this.cursor = -1;
}
show(args: any[]): void {
super.show(args);
this.eggListContainer.setVisible(true);
let e = 0;
/*this.scene.gameData.eggs = [
new Egg(1, 1, 5, new Date().getTime()),
new Egg(1 + EGG_SEED, 1, 15, new Date().getTime()),
new Egg(1 + EGG_SEED * 2, 1, 50, new Date().getTime()),
new Egg(1 + EGG_SEED * 3, GachaType.LEGENDARY, 100, new Date().getTime())
];*/
for (let egg of this.scene.gameData.eggs) {
const x = (e % 11) * 18;
const y = Math.floor(e / 11) * 18;
const icon = this.scene.add.sprite(x - 2, y + 2, 'egg_icons');
icon.setScale(0.5);
icon.setOrigin(0, 0);
icon.setFrame(egg.getKey());
this.eggListIconContainer.add(icon);
this.iconAnimHandler.addOrUpdate(icon, PokemonIconAnimMode.NONE);
e++;
}
this.setCursor(0);
}
showText(text: string, delay?: integer, callback?: Function, callbackDelay?: integer, prompt?: boolean, promptDelay?: integer) {
super.showText(text, delay, callback, callbackDelay, prompt, promptDelay);
}
processInput(button: Button): boolean {
const ui = this.getUi();
let success = false;
let error = false;
if (button === Button.CANCEL) {
ui.revertMode();
success = true;
} else {
const eggCount = this.eggListIconContainer.getAll().length;
const rows = Math.ceil(eggCount / 11);
const row = Math.floor(this.cursor / 11);
switch (button) {
case Button.UP:
if (row)
success = this.setCursor(this.cursor - 11);
break;
case Button.DOWN:
if (row < rows - 2 || (row < rows - 1 && this.cursor % 11 <= (eggCount - 1) % 11))
success = this.setCursor(this.cursor + 11);
break;
case Button.LEFT:
if (this.cursor % 11)
success = this.setCursor(this.cursor - 1);
break;
case Button.RIGHT:
if (this.cursor % 11 < (row < rows - 1 ? 10 : (eggCount - 1) % 11))
success = this.setCursor(this.cursor + 1);
break;
}
}
if (success)
ui.playSelect();
else if (error)
ui.playError();
return success || error;
}
setEggDetails(egg: Egg): void {
this.eggSprite.setFrame(`egg_${egg.getKey()}`);
this.eggNameText.setText(`Egg (${getEggDescriptor(egg)})`);
this.eggDateText.setText(
new Date(egg.timestamp).toLocaleString(undefined, {
weekday: 'short',
year: 'numeric',
month: '2-digit',
day: 'numeric'
})
);
this.eggHatchWavesText.setText(getEggHatchWavesMessage(egg.hatchWaves));
this.eggGachaInfoText.setText(getEggGachaTypeDescriptor(this.scene, egg));
}
setCursor(cursor: integer): boolean {
let changed = false;
let lastCursor = this.cursor;
changed = super.setCursor(cursor);
if (changed) {
this.cursorObj.setPosition(114 + 18 * (cursor % 11), 10 + 18 * Math.floor(cursor / 11));
if (lastCursor > -1)
this.iconAnimHandler.addOrUpdate(this.eggListIconContainer.getAt(lastCursor) as Phaser.GameObjects.Sprite, PokemonIconAnimMode.NONE);
this.iconAnimHandler.addOrUpdate(this.eggListIconContainer.getAt(cursor) as Phaser.GameObjects.Sprite, PokemonIconAnimMode.ACTIVE);
this.setEggDetails(this.scene.gameData.eggs[cursor]);
}
return changed;
}
clear(): void {
super.clear();
this.cursor = -1;
this.eggListContainer.setVisible(false);
this.iconAnimHandler.removeAll();
this.eggListIconContainer.removeAll(true);
}
}

View File

@ -6,7 +6,10 @@ import * as Utils from "../utils";
export enum MenuOptions {
SETTINGS,
ACHIEVEMENTS
ACHIEVEMENTS,
VOUCHERS,
EGG_LIST,
EGG_GACHA
}
export default class MenuUiHandler extends UiHandler {
@ -62,6 +65,7 @@ export default class MenuUiHandler extends UiHandler {
const ui = this.getUi();
let success = false;
let error = false;
if (button === Button.ACTION) {
switch (this.cursor as MenuOptions) {
@ -73,6 +77,24 @@ export default class MenuUiHandler extends UiHandler {
this.scene.ui.setOverlayMode(Mode.ACHIEVEMENTS);
success = true;
break;
case MenuOptions.VOUCHERS:
this.scene.ui.setOverlayMode(Mode.VOUCHERS);
success = true;
break;
case MenuOptions.EGG_LIST:
if (this.scene.gameData.eggs.length) {
this.scene.ui.revertMode();
this.scene.ui.setOverlayMode(Mode.EGG_LIST);
success = true;
} else
error = true;
break;
case MenuOptions.EGG_GACHA:
this.scene.ui.revertMode();
this.scene.ui.setOverlayMode(Mode.EGG_GACHA);
success = true;
break;
}
} else if (button === Button.CANCEL) {
success = true;
@ -93,6 +115,8 @@ export default class MenuUiHandler extends UiHandler {
if (success)
ui.playSelect();
else if (error)
ui.playError();
return true;
}

View File

@ -8,7 +8,6 @@ export enum PokemonIconAnimMode {
}
export default class PokemonIconAnimHandler {
private counter: Phaser.Tweens.Tween;
private icons: Map<Phaser.GameObjects.Sprite, PokemonIconAnimMode>;
private toggled: boolean;
@ -22,7 +21,7 @@ export default class PokemonIconAnimHandler {
for (let i of this.icons.keys())
i.y += this.getModeYDelta(this.icons.get(i)) * (this.toggled ? 1 : -1);
};
this.counter = scene.tweens.addCounter({
scene.tweens.addCounter({
duration: Utils.fixedInt(200),
from: 0,
to: 1,
@ -70,4 +69,12 @@ export default class PokemonIconAnimHandler {
this.icons.delete(i);
}
}
removeAll(): void {
for (let i of this.icons.keys()) {
if (this.toggled)
i.y -= this.getModeYDelta(this.icons.get(i));
this.icons.delete(i);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,19 +1,24 @@
import BBCodeText from "phaser3-rex-plugins/plugins/gameobjects/tagtext/bbcodetext/BBCodeText";
import BattleScene from "../battle-scene";
import { Stat, getStatName } from "../data/pokemon-stat";
import { TextStyle, addTextObject } from "./text";
import { TextStyle, addBBCodeTextObject, addTextObject, getTextColor } from "./text";
import { Gender, getGenderColor } from "../data/gender";
const ivChartSize = 24;
const ivChartStatCoordMultipliers = [ [ 0, 1 ], [ 0.825, 0.5 ], [ 0.825, -0.5 ], [ 0, -1 ], [ -0.825, -0.5 ], [ -0.825, 0.5 ] ];
const defaultIvChartData = new Array(12).fill(null).map(() => 0);
export class StatsContainer extends Phaser.GameObjects.Container {
private showDiff: boolean;
private statsIvsCache: integer[];
private ivChart: Phaser.GameObjects.Polygon;
private ivStatValueTexts: Phaser.GameObjects.Text[];
private ivStatValueTexts: BBCodeText[];
constructor(scene: BattleScene, x: number, y: number) {
constructor(scene: BattleScene, x: number, y: number, showDiff?: boolean) {
super(scene, x, y);
this.showDiff = !!showDiff;
this.setup();
}
@ -48,7 +53,7 @@ export class StatsContainer extends Phaser.GameObjects.Container {
const statLabel = addTextObject(this.scene, ivChartBg.x + (ivChartSize) * ivChartStatCoordMultipliers[i][0] * 1.325, ivChartBg.y + (ivChartSize) * ivChartStatCoordMultipliers[i][1] * 1.325 - 4, getStatName(i as Stat), TextStyle.TOOLTIP_CONTENT);
statLabel.setOrigin(0.5);
this.ivStatValueTexts[i] = addTextObject(this.scene, statLabel.x, statLabel.y + 8, '0', TextStyle.TOOLTIP_CONTENT);
this.ivStatValueTexts[i] = addBBCodeTextObject(this.scene, statLabel.x, statLabel.y + 8, '0', TextStyle.TOOLTIP_CONTENT);
this.ivStatValueTexts[i].setOrigin(0.5)
this.add(statLabel);
@ -56,13 +61,22 @@ export class StatsContainer extends Phaser.GameObjects.Container {
});
}
updateIvs(ivs: integer[]): void {
updateIvs(ivs: integer[], originalIvs?: integer[]): void {
if (ivs) {
const ivChartData = new Array(6).fill(null).map((_, i) => [ (ivs[i] / 31) * ivChartSize * ivChartStatCoordMultipliers[i][0], (ivs[i] / 31) * ivChartSize * ivChartStatCoordMultipliers[i][1] ] ).flat();
const lastIvChartData = this.statsIvsCache || defaultIvChartData;
this.statsIvsCache = ivChartData.slice(0);
this.ivStatValueTexts.map((t: Phaser.GameObjects.Text, i: integer) => t.setText(ivs[i].toString()));
this.ivStatValueTexts.map((t: BBCodeText, i: integer) => {
let label = ivs[i].toString();
if (this.showDiff && originalIvs) {
if (originalIvs[i] < ivs[i])
label += ` ([color=${getGenderColor(Gender.MALE)}]+${ivs[i] - originalIvs[i]}[/color])`;
else
label += ' (-)';
}
t.setText(`[shadow]${label}[/shadow]`);
});
this.scene.tweens.addCounter({
from: 0,

View File

@ -1,3 +1,5 @@
import BBCodeText from "phaser3-rex-plugins/plugins/gameobjects/tagtext/bbcodetext/BBCodeText";
export enum TextStyle {
MESSAGE,
WINDOW,
@ -14,7 +16,32 @@ export enum TextStyle {
TOOLTIP_CONTENT
};
export function addTextObject(scene: Phaser.Scene, x: number, y: number, content: string, style: TextStyle, extraStyleOptions?: Phaser.Types.GameObjects.Text.TextStyle) {
export function addTextObject(scene: Phaser.Scene, x: number, y: number, content: string, style: TextStyle, extraStyleOptions?: Phaser.Types.GameObjects.Text.TextStyle): Phaser.GameObjects.Text {
const [ styleOptions, shadowColor, shadowSize ] = getTextStyleOptions(style, extraStyleOptions);
const ret = scene.add.text(x, y, content, styleOptions);
ret.setScale(0.1666666667);
ret.setShadow(shadowSize, shadowSize, shadowColor);
if (!styleOptions.lineSpacing)
ret.setLineSpacing(5);
return ret;
}
export function addBBCodeTextObject(scene: Phaser.Scene, x: number, y: number, content: string, style: TextStyle, extraStyleOptions?: Phaser.Types.GameObjects.Text.TextStyle): BBCodeText {
const [ styleOptions, shadowColor, shadowSize ] = getTextStyleOptions(style, extraStyleOptions);
const ret = new BBCodeText(scene, x, y, content, styleOptions as BBCodeText.TextStyle);
scene.add.existing(ret);
ret.setScale(0.1666666667);
ret.setShadow(shadowSize, shadowSize, shadowColor);
if (!styleOptions.lineSpacing)
ret.setLineSpacing(5);
return ret;
}
function getTextStyleOptions(style: TextStyle, extraStyleOptions?: Phaser.Types.GameObjects.Text.TextStyle): [ Phaser.Types.GameObjects.Text.TextStyle, string, integer ] {
let shadowColor: string;
let shadowSize = 6;
@ -64,16 +91,10 @@ export function addTextObject(scene: Phaser.Scene, x: number, y: number, content
styleOptions = Object.assign(styleOptions, extraStyleOptions);
}
const ret = scene.add.text(x, y, content, styleOptions);
ret.setScale(0.1666666667);
ret.setShadow(shadowSize, shadowSize, shadowColor);
if (!styleOptions.lineSpacing)
ret.setLineSpacing(5);
return ret;
return [ styleOptions, shadowColor, shadowSize ];
}
export function getTextColor(textStyle: TextStyle, shadow?: boolean) {
export function getTextColor(textStyle: TextStyle, shadow?: boolean): string {
switch (textStyle) {
case TextStyle.MESSAGE:
return !shadow ? '#f8f8f8' : '#6b5a73';

View File

@ -21,6 +21,10 @@ import MenuUiHandler from './menu-ui-handler';
import AchvsUiHandler from './achvs-ui-handler';
import OptionSelectUiHandler from './option-select-ui-handler';
import EggHatchSceneHandler from './egg-hatch-scene-handler';
import EggListUiHandler from './egg-list-ui-handler';
import EggGachaUiHandler from './egg-gacha-ui-handler';
import VouchersUiHandler from './vouchers-ui-handler';
import VoucherBar from './voucher-bar';
export enum Mode {
MESSAGE,
@ -40,7 +44,10 @@ export enum Mode {
GAME_MODE_SELECT,
MENU,
SETTINGS,
ACHIEVEMENTS
ACHIEVEMENTS,
VOUCHERS,
EGG_LIST,
EGG_GACHA
};
const transitionModes = [
@ -48,7 +55,9 @@ const transitionModes = [
Mode.SUMMARY,
Mode.STARTER_SELECT,
Mode.EVOLUTION_SCENE,
Mode.EGG_HATCH_SCENE
Mode.EGG_HATCH_SCENE,
Mode.EGG_LIST,
Mode.EGG_GACHA
];
const noTransitionModes = [
@ -56,7 +65,9 @@ const noTransitionModes = [
Mode.OPTION_SELECT,
Mode.GAME_MODE_SELECT,
Mode.MENU,
Mode.SETTINGS
Mode.SETTINGS,
Mode.ACHIEVEMENTS,
Mode.VOUCHERS
];
export default class UI extends Phaser.GameObjects.Container {
@ -65,6 +76,7 @@ export default class UI extends Phaser.GameObjects.Container {
private handlers: UiHandler[];
private overlay: Phaser.GameObjects.Rectangle;
public achvBar: AchvBar;
public voucherBar: VoucherBar;
private tooltipContainer: Phaser.GameObjects.Container;
private tooltipBg: Phaser.GameObjects.NineSlice;
@ -96,7 +108,10 @@ export default class UI extends Phaser.GameObjects.Container {
new GameModeSelectUiHandler(scene),
new MenuUiHandler(scene),
new SettingsUiHandler(scene),
new AchvsUiHandler(scene)
new AchvsUiHandler(scene),
new VouchersUiHandler(scene),
new EggListUiHandler(scene),
new EggGachaUiHandler(scene)
];
}
@ -110,7 +125,8 @@ export default class UI extends Phaser.GameObjects.Container {
this.setupTooltip();
this.achvBar = new AchvBar(this.scene as BattleScene);
this.achvBar.setup();
this.achvBar.setup();
(this.scene as BattleScene).uiContainer.add(this.achvBar);
}
@ -267,8 +283,9 @@ export default class UI extends Phaser.GameObjects.Container {
}
resolve();
};
if ((transitionModes.indexOf(this.mode) > -1 || transitionModes.indexOf(mode) > -1)
&& (noTransitionModes.indexOf(this.mode) === -1 && noTransitionModes.indexOf(mode) === -1) && !(this.scene as BattleScene).auto) {
if (((!chainMode && ((transitionModes.indexOf(this.mode) > -1 || transitionModes.indexOf(mode) > -1)
&& (noTransitionModes.indexOf(this.mode) === -1 && noTransitionModes.indexOf(mode) === -1)))
|| (chainMode && noTransitionModes.indexOf(mode) === -1)) && !(this.scene as BattleScene).auto) {
this.fadeOut(250).then(() => {
this.scene.time.delayedCall(100, () => {
doSetMode();
@ -300,12 +317,29 @@ export default class UI extends Phaser.GameObjects.Container {
return this.setModeInternal(mode, false, false, true, args);
}
revertMode(): boolean {
if (!this.modeChain.length)
return false;
this.getHandler().clear();
this.mode = this.modeChain.pop();
return true;
revertMode(): Promise<boolean> {
return new Promise<boolean>(resolve => {
if (!this.modeChain.length)
return resolve(false);
const lastMode = this.mode;
const doRevertMode = () => {
this.getHandler().clear();
this.mode = this.modeChain.pop();
};
if (noTransitionModes.indexOf(lastMode) === -1) {
this.fadeOut(250).then(() => {
this.scene.time.delayedCall(100, () => {
doRevertMode();
this.fadeIn(250);
});
});
} else
doRevertMode();
resolve(true);
});
}
}

View File

@ -0,0 +1,196 @@
import BattleScene, { Button } from "../battle-scene";
import { Voucher, getVoucherTypeIcon, getVoucherTypeName, vouchers } from "../system/voucher";
import MessageUiHandler from "./message-ui-handler";
import { TextStyle, addTextObject } from "./text";
import { Mode } from "./ui";
export default class VouchersUiHandler extends MessageUiHandler {
private vouchersContainer: Phaser.GameObjects.Container;
private voucherIconsContainer: Phaser.GameObjects.Container;
private voucherIconsBg: Phaser.GameObjects.NineSlice;
private voucherIcons: Phaser.GameObjects.Sprite[];
private titleText: Phaser.GameObjects.Text;
private unlockText: Phaser.GameObjects.Text;
private cursorObj: Phaser.GameObjects.NineSlice;
constructor(scene: BattleScene, mode?: Mode) {
super(scene, mode);
}
setup() {
const ui = this.getUi();
this.vouchersContainer = this.scene.add.container(1, -(this.scene.game.canvas.height / 6) + 1);
this.vouchersContainer.setInteractive(new Phaser.Geom.Rectangle(0, 0, this.scene.game.canvas.width / 6, this.scene.game.canvas.height / 6), Phaser.Geom.Rectangle.Contains);
const headerBg = this.scene.add.nineslice(0, 0, 'window', null, (this.scene.game.canvas.width / 6) - 2, 24, 6, 6, 6, 6);
headerBg.setOrigin(0, 0);
const headerText = addTextObject(this.scene, 0, 0, 'Vouchers', TextStyle.SETTINGS_LABEL);
headerText.setOrigin(0, 0);
headerText.setPositionRelative(headerBg, 8, 4);
this.voucherIconsBg = this.scene.add.nineslice(0, headerBg.height, 'window', null, (this.scene.game.canvas.width / 6) - 2, (this.scene.game.canvas.height / 6) - headerBg.height - 68, 6, 6, 6, 6);
this.voucherIconsBg.setOrigin(0, 0);
this.voucherIconsContainer = this.scene.add.container(6, headerBg.height + 6);
this.voucherIcons = [];
for (let a = 0; a < Object.keys(vouchers).length; a++) {
const x = (a % 17) * 18;
const y = Math.floor(a / 17) * 18;
const icon = this.scene.add.sprite(x, y, 'items', 'unknown');
icon.setOrigin(0, 0);
icon.setScale(0.5);
this.voucherIcons.push(icon);
this.voucherIconsContainer.add(icon);
}
const titleBg = this.scene.add.nineslice(0, headerBg.height + this.voucherIconsBg.height, 'window', null, 220, 24, 6, 6, 6, 6);
titleBg.setOrigin(0, 0);
this.titleText = addTextObject(this.scene, 0, 0, '', TextStyle.WINDOW);
this.titleText.setOrigin(0, 0);
this.titleText.setPositionRelative(titleBg, 8, 4);
const unlockBg = this.scene.add.nineslice(titleBg.x + titleBg.width, titleBg.y, 'window', null, 98, 24, 6, 6, 6, 6);
unlockBg.setOrigin(0, 0);
this.unlockText = addTextObject(this.scene, 0, 0, '', TextStyle.WINDOW);
this.unlockText.setOrigin(0, 0);
this.unlockText.setPositionRelative(unlockBg, 8, 4);
const descriptionBg = this.scene.add.nineslice(0, titleBg.y + titleBg.height, 'window', null, (this.scene.game.canvas.width / 6) - 2, 42, 6, 6, 6, 6);
descriptionBg.setOrigin(0, 0);
const descriptionText = addTextObject(this.scene, 0, 0, '', TextStyle.WINDOW, { maxLines: 2 });
descriptionText.setWordWrapWidth(1870);
descriptionText.setOrigin(0, 0);
descriptionText.setPositionRelative(descriptionBg, 8, 4);
this.message = descriptionText;
this.vouchersContainer.add(headerBg);
this.vouchersContainer.add(headerText);
this.vouchersContainer.add(this.voucherIconsBg);
this.vouchersContainer.add(this.voucherIconsContainer);
this.vouchersContainer.add(titleBg);
this.vouchersContainer.add(this.titleText);
this.vouchersContainer.add(unlockBg);
this.vouchersContainer.add(this.unlockText);
this.vouchersContainer.add(descriptionBg);
this.vouchersContainer.add(descriptionText);
ui.add(this.vouchersContainer);
this.setCursor(0);
this.vouchersContainer.setVisible(false);
}
show(args: any[]) {
super.show(args);
const voucherUnlocks = this.scene.gameData.voucherUnlocks;
Object.values(vouchers).forEach((voucher: Voucher, i: integer) => {
const icon = this.voucherIcons[i];
const unlocked = voucherUnlocks.hasOwnProperty(voucher.id);
icon.setFrame(getVoucherTypeIcon(voucher.voucherType));
if (!unlocked)
icon.setTintFill(0);
else
icon.clearTint();
});
this.vouchersContainer.setVisible(true);
this.setCursor(0);
this.getUi().moveTo(this.vouchersContainer, this.getUi().length - 1);
this.getUi().hideTooltip();
}
protected showVoucher(voucher: Voucher) {
const voucherUnlocks = this.scene.gameData.voucherUnlocks;
const unlocked = voucherUnlocks.hasOwnProperty(voucher.id);
this.titleText.setText(getVoucherTypeName(voucher.voucherType));
this.showText(voucher.description);
this.unlockText.setText(unlocked ? new Date(voucherUnlocks[voucher.id]).toLocaleDateString() : 'Locked');
}
processInput(button: Button): boolean {
const ui = this.getUi();
let success = false;
if (button === Button.CANCEL) {
success = true;
this.scene.ui.revertMode();
} else {
switch (button) {
case Button.UP:
if (this.cursor >= 17)
success = this.setCursor(this.cursor - 17);
break;
case Button.DOWN:
if (this.cursor + 17 < Object.keys(vouchers).length)
success = this.setCursor(this.cursor + 17);
break;
case Button.LEFT:
if (this.cursor)
success = this.setCursor(this.cursor - 1);
break;
case Button.RIGHT:
if (this.cursor < Object.keys(vouchers).length - 1)
success = this.setCursor(this.cursor + 1);
break;
}
}
if (success)
ui.playSelect();
return success;
}
setCursor(cursor: integer): boolean {
let ret = super.setCursor(cursor);
let updateVoucher = ret;
if (!this.cursorObj) {
this.cursorObj = this.scene.add.nineslice(0, 0, 'starter_select_cursor_highlight', null, 16, 16, 1, 1, 1, 1);
this.cursorObj.setOrigin(0, 0);
this.voucherIconsContainer.add(this.cursorObj);
updateVoucher = true;
}
this.cursorObj.setPositionRelative(this.voucherIcons[this.cursor], 0, 0);
if (updateVoucher)
this.showVoucher(vouchers[Object.keys(vouchers)[cursor]]);
return ret;
}
clear() {
super.clear();
this.vouchersContainer.setVisible(false);
this.eraseCursor();
}
eraseCursor() {
if (this.cursorObj)
this.cursorObj.destroy();
this.cursorObj = null;
}
}

View File

@ -76,6 +76,13 @@ export function randIntRange(min: integer, max: integer): integer {
return randInt(max - min, min);
}
export function getSunday(date: Date): Date {
const day = date.getDay(),
diff = date.getDate() - day;
const newDate = new Date(date.setDate(diff));
return new Date(newDate.getFullYear(), newDate.getMonth(), newDate.getDate());
}
export function getFrameMs(frameCount: integer): integer {
return Math.floor((1 / 60) * 1000 * frameCount);
}