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
pull/14/head
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, "id": 383,
"graphic": "'!", "graphic": "!",
"frames": [ "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", "image": "egg.png",
"format": "RGBA8888", "format": "RGBA8888",
"size": { "size": {
"w": 112, "w": 138,
"h": 30 "h": 30
}, },
"scale": 1, "scale": 1,
@ -12,14 +12,14 @@
{ {
"filename": "egg_0", "filename": "egg_0",
"rotated": false, "rotated": false,
"trimmed": true, "trimmed": false,
"sourceSize": { "sourceSize": {
"w": 32, "w": 28,
"h": 32 "h": 30
}, },
"spriteSourceSize": { "spriteSourceSize": {
"x": 2, "x": 0,
"y": 1, "y": 0,
"w": 28, "w": 28,
"h": 30 "h": 30
}, },
@ -33,14 +33,14 @@
{ {
"filename": "egg_1", "filename": "egg_1",
"rotated": false, "rotated": false,
"trimmed": true, "trimmed": false,
"sourceSize": { "sourceSize": {
"w": 32, "w": 28,
"h": 32 "h": 30
}, },
"spriteSourceSize": { "spriteSourceSize": {
"x": 2, "x": 0,
"y": 1, "y": 0,
"w": 28, "w": 28,
"h": 30 "h": 30
}, },
@ -54,14 +54,14 @@
{ {
"filename": "egg_2", "filename": "egg_2",
"rotated": false, "rotated": false,
"trimmed": true, "trimmed": false,
"sourceSize": { "sourceSize": {
"w": 32, "w": 28,
"h": 32 "h": 30
}, },
"spriteSourceSize": { "spriteSourceSize": {
"x": 2, "x": 0,
"y": 1, "y": 0,
"w": 28, "w": 28,
"h": 30 "h": 30
}, },
@ -75,14 +75,14 @@
{ {
"filename": "egg_3", "filename": "egg_3",
"rotated": false, "rotated": false,
"trimmed": true, "trimmed": false,
"sourceSize": { "sourceSize": {
"w": 32, "w": 28,
"h": 32 "h": 30
}, },
"spriteSourceSize": { "spriteSourceSize": {
"x": 2, "x": 0,
"y": 1, "y": 0,
"w": 28, "w": 28,
"h": 30 "h": 30
}, },
@ -92,6 +92,27 @@
"w": 28, "w": 28,
"h": 30 "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": { "meta": {
"app": "https://www.codeandweb.com/texturepacker", "app": "https://www.codeandweb.com/texturepacker",
"version": "3.0", "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 { HealAchv, LevelAchv, MoneyAchv, achvs } from "./system/achv";
import { DexEntry } from "./system/game-data"; import { DexEntry } from "./system/game-data";
import { pokemonPrevolutions } from "./data/pokemon-evolutions"; 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 { export class CheckLoadPhase extends BattlePhase {
private loaded: boolean; 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(); super.end();
} }
} }
@ -2338,6 +2346,7 @@ export class VictoryPhase extends PokemonPhase {
this.scene.pushPhase(new BattleEndPhase(this.scene)); this.scene.pushPhase(new BattleEndPhase(this.scene));
if (this.scene.currentBattle.battleType === BattleType.TRAINER) if (this.scene.currentBattle.battleType === BattleType.TRAINER)
this.scene.pushPhase(new TrainerVictoryPhase(this.scene)); 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.gameMode !== GameMode.CLASSIC || this.scene.currentBattle.waveIndex < this.scene.finalWave) {
if (this.scene.currentBattle.waveIndex % 10) if (this.scene.currentBattle.waveIndex % 10)
this.scene.pushPhase(new SelectModifierPhase(this.scene)); 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)) if (this.scene.currentBattle.waveIndex <= 150 && !(this.scene.currentBattle.waveIndex % 50))
this.scene.pushPhase(new ModifierRewardPhase(this.scene, modifierTypes.GOLDEN_POKEBALL)); 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 AddEnemyBuffModifierPhase(this.scene));
}
} }
this.scene.pushPhase(new NewBattlePhase(this.scene)); this.scene.pushPhase(new NewBattlePhase(this.scene));
} else } else
@ -2367,6 +2378,10 @@ export class TrainerVictoryPhase extends BattlePhase {
start() { start() {
this.scene.playBgm(this.scene.currentBattle.trainer.config.victoryBgm); 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)); this.scene.unshiftPhase(new MoneyRewardPhase(this.scene, this.scene.currentBattle.trainer.config.moneyMultiplier));
const modifierRewardFuncs = this.scene.currentBattle.trainer.config.modifierRewardFuncs; const modifierRewardFuncs = this.scene.currentBattle.trainer.config.modifierRewardFuncs;
@ -2964,18 +2979,7 @@ export class AttemptCapturePhase extends PokemonPhase {
if (pokemon.species.mythical) if (pokemon.species.mythical)
this.scene.validateAchv(achvs.CATCH_MYTHICAL); this.scene.validateAchv(achvs.CATCH_MYTHICAL);
let dexEntry: DexEntry; this.scene.gameData.updateSpeciesDexIvs(pokemon.species.speciesId, pokemon.ivs);
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.ui.showText(`${pokemon.name} was caught!`, null, () => { this.scene.ui.showText(`${pokemon.name} was caught!`, null, () => {
const end = () => { 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 { export class AddEnemyBuffModifierPhase extends BattlePhase {
constructor(scene: BattleScene) { constructor(scene: BattleScene) {
super(scene); super(scene);

View File

@ -35,6 +35,8 @@ import MessageUiHandler from './ui/message-ui-handler';
import { Species } from './data/species'; import { Species } from './data/species';
import InvertPostFX from './pipelines/invert'; import InvertPostFX from './pipelines/invert';
import { Achv, ModifierAchv, achvs } from './system/achv'; import { Achv, ModifierAchv, achvs } from './system/achv';
import { GachaType } from './data/egg';
import { Voucher, vouchers } from './system/voucher';
const enableAuto = true; const enableAuto = true;
const quickStart = false; const quickStart = false;
@ -301,10 +303,22 @@ export default class BattleScene extends Phaser.Scene {
this.loadAtlas('types', ''); this.loadAtlas('types', '');
this.loadAtlas('statuses', ''); this.loadAtlas('statuses', '');
this.loadAtlas('categories', ''); this.loadAtlas('categories', '');
this.loadAtlas('egg', '');
this.loadAtlas('egg_crack', ''); this.loadAtlas('egg', 'egg');
this.loadAtlas('egg_shard', ''); this.loadAtlas('egg_crack', 'egg');
this.loadAtlas('egg_lightrays', ''); 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++) for (let i = 0; i < 10; i++)
this.loadAtlas(`pokemon_icons_${i}`, 'ui'); this.loadAtlas(`pokemon_icons_${i}`, 'ui');
@ -345,6 +359,9 @@ export default class BattleScene extends Phaser.Scene {
this.loadSe('egg_crack'); this.loadSe('egg_crack');
this.loadSe('egg_hatch'); this.loadSe('egg_hatch');
this.loadSe('gacha_dial');
this.loadSe('gacha_running');
this.loadSe('gacha_dispense');
this.loadSe('PRSFX- Transform', 'battle_anims'); this.loadSe('PRSFX- Transform', 'battle_anims');
@ -714,9 +731,6 @@ export default class BattleScene extends Phaser.Scene {
//this.pushPhase(new TrainerMessageTestPhase(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) { if (!waveIndex) {
const isNewBiome = !lastBattle || !(lastBattle.waveIndex % 10); const isNewBiome = !lastBattle || !(lastBattle.waveIndex % 10);
const resetArenaState = isNewBiome || this.currentBattle.battleType === BattleType.TRAINER; 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 { validateAchvs(achvType: { new(...args: any[]): Achv }, ...args: any[]): void {
const filteredAchvs = Object.values(achvs).filter(a => a instanceof achvType); const filteredAchvs = Object.values(achvs).filter(a => a instanceof achvType);
let newAchv = false;
for (let achv of filteredAchvs) for (let achv of filteredAchvs)
this.validateAchv(achv, args); 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)) { if (!this.gameData.achvUnlocks.hasOwnProperty(achv.id) && achv.validate(this, args)) {
this.gameData.achvUnlocks[achv.id] = new Date().getTime(); this.gameData.achvUnlocks[achv.id] = new Date().getTime();
this.ui.achvBar.showAchv(achv); 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; return true;
} }

View File

@ -181,24 +181,20 @@ export const biomePokemonPools: BiomePokemonPools = {
Species.VENONAT, Species.VENONAT,
Species.MEOWTH, Species.MEOWTH,
Species.BELLSPROUT, Species.BELLSPROUT,
Species.PICHU,
Species.IGGLYBUFF,
Species.LOTAD, Species.LOTAD,
Species.SEEDOT, Species.SEEDOT,
Species.SHROOMISH, Species.SHROOMISH,
Species.NINCADA, Species.NINCADA,
Species.WHISMUR, Species.WHISMUR,
Species.AZURILL,
Species.SKITTY, Species.SKITTY,
Species.KRICKETOT, Species.KRICKETOT,
Species.BUDEW,
Species.COMBEE, Species.COMBEE,
Species.CHERUBI, Species.CHERUBI,
Species.VENIPEDE, Species.VENIPEDE,
Species.MINCCINO Species.MINCCINO
], ],
[BiomePoolTier.RARE]: [ Species.ABRA, Species.CLEFFA, Species.SURSKIT ], [BiomePoolTier.RARE]: [ Species.ABRA, 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.SUPER_RARE]: [ Species.EEVEE, Species.RALTS ],
[BiomePoolTier.ULTRA_RARE]: [ Species.DITTO ], [BiomePoolTier.ULTRA_RARE]: [ Species.DITTO ],
[BiomePoolTier.BOSS]: [], [BiomePoolTier.BOSS]: [],
[BiomePoolTier.BOSS_RARE]: [], [BiomePoolTier.BOSS_RARE]: [],
@ -250,7 +246,7 @@ export const biomePokemonPools: BiomePokemonPools = {
[BiomePoolTier.BOSS]: [ Species.JUMPLUFF, Species.SUNFLORA, Species.WHIMSICOTT ], [BiomePoolTier.BOSS]: [ Species.JUMPLUFF, Species.SUNFLORA, Species.WHIMSICOTT ],
[BiomePoolTier.BOSS_RARE]: [ Species.VENUSAUR, Species.SUDOWOODO, Species.TORTERRA ], [BiomePoolTier.BOSS_RARE]: [ Species.VENUSAUR, Species.SUDOWOODO, Species.TORTERRA ],
[BiomePoolTier.BOSS_SUPER_RARE]: [], [BiomePoolTier.BOSS_SUPER_RARE]: [],
[BiomePoolTier.BOSS_ULTRA_RARE]: [ Species.SHAYMIN ] [BiomePoolTier.BOSS_ULTRA_RARE]: []
}, },
[Biome.TALL_GRASS]: { [Biome.TALL_GRASS]: {
[BiomePoolTier.COMMON]: [ [BiomePoolTier.COMMON]: [
@ -329,7 +325,7 @@ export const biomePokemonPools: BiomePokemonPools = {
{ 1: [ Species.ROWLET ], 17: [ Species.DARTRIX ], 36: [ Species.DECIDUEYE ] } { 1: [ Species.ROWLET ], 17: [ Species.DARTRIX ], 36: [ Species.DECIDUEYE ] }
], ],
[BiomePoolTier.SUPER_RARE]: [ Species.DURANT ], [BiomePoolTier.SUPER_RARE]: [ Species.DURANT ],
[BiomePoolTier.ULTRA_RARE]: [ Species.CELEBI, Species.KARTANA ], [BiomePoolTier.ULTRA_RARE]: [ Species.KARTANA ],
[BiomePoolTier.BOSS]: [ [BiomePoolTier.BOSS]: [
Species.VENOMOTH, Species.VENOMOTH,
Species.VICTREEBEL, 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_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_SUPER_RARE]: [ Species.KARTANA ],
[BiomePoolTier.BOSS_ULTRA_RARE]: [ Species.CELEBI, Species.CALYREX ] [BiomePoolTier.BOSS_ULTRA_RARE]: [ Species.CALYREX ]
}, },
[Biome.SEA]: { [Biome.SEA]: {
[BiomePoolTier.COMMON]: [ [BiomePoolTier.COMMON]: [
@ -474,7 +470,7 @@ export const biomePokemonPools: BiomePokemonPools = {
{ 1: [ Species.SKRELP ], 48: [ Species.DRAGALGE ] }, { 1: [ Species.SKRELP ], 48: [ Species.DRAGALGE ] },
Species.PINCURCHIN 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]: [ [BiomePoolTier.SUPER_RARE]: [
{ 1: [ Species.OMANYTE ], 40: [ Species.OMASTAR ] }, { 1: [ Species.OMANYTE ], 40: [ Species.OMASTAR ] },
{ 1: [ Species.KABUTO ], 40: [ Species.KABUTOPS ] }, { 1: [ Species.KABUTO ], 40: [ Species.KABUTOPS ] },
@ -484,10 +480,10 @@ export const biomePokemonPools: BiomePokemonPools = {
Species.ARCTOVISH, Species.ARCTOVISH,
Species.HISUI_QWILFISH 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]: [ 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_RARE]: [ Species.OMASTAR, Species.KABUTOPS, Species.RELICANTH, 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_SUPER_RARE]: [ Species.MILOTIC, Species.NIHILEGO, Species.CURSOLA, Species.OVERQWIL ],
[BiomePoolTier.BOSS_ULTRA_RARE]: [ Species.KYOGRE ] [BiomePoolTier.BOSS_ULTRA_RARE]: [ Species.KYOGRE ]
}, },
[Biome.MOUNTAIN]: { [Biome.MOUNTAIN]: {
@ -573,10 +569,10 @@ export const biomePokemonPools: BiomePokemonPools = {
], ],
[BiomePoolTier.RARE]: [ Species.ONIX, { 1: [ Species.FERROSEED ], 40: [ Species.FERROTHORN ] }, Species.CARBINK ], [BiomePoolTier.RARE]: [ Species.ONIX, { 1: [ Species.FERROSEED ], 40: [ Species.FERROTHORN ] }, Species.CARBINK ],
[BiomePoolTier.SUPER_RARE]: [ Species.SHUCKLE ], [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]: [ 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_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]: [] [BiomePoolTier.BOSS_ULTRA_RARE]: []
}, },
[Biome.DESERT]: { [Biome.DESERT]: {
@ -726,7 +722,7 @@ export const biomePokemonPools: BiomePokemonPools = {
{ 1: [ Species.CLOBBOPUS ], 20: [ Species.GRAPPLOCT ] } { 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.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.SUPER_RARE]: [ Species.HITMONTOP, Species.GALLADE, Species.GALAR_FARFETCHD ],
[BiomePoolTier.ULTRA_RARE]: [ Species.TERRAKION, Species.KUBFU, Species.GALAR_ZAPDOS ], [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 ], [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.UNCOMMON]: [ { 1: [ Species.BRONZOR ], 33: [ Species.BRONZONG ] }, Species.KLEFKI ],
[BiomePoolTier.RARE]: [], [BiomePoolTier.RARE]: [],
[BiomePoolTier.SUPER_RARE]: [ { 1: [ Species.PORYGON ], 20: [ Species.PORYGON2 ] }, { 1: [ Species.BELDUM ], 20: [ Species.METANG ], 45: [ Species.METAGROSS ] } ], [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]: [ Species.KLINKLANG, Species.KLEFKI ],
[BiomePoolTier.BOSS_RARE]: [], [BiomePoolTier.BOSS_RARE]: [],
[BiomePoolTier.BOSS_SUPER_RARE]: [ Species.GENESECT, Species.MAGEARNA ], [BiomePoolTier.BOSS_SUPER_RARE]: [ Species.GENESECT, Species.MAGEARNA ],
[BiomePoolTier.BOSS_ULTRA_RARE]: [ Species.MELMETAL ] [BiomePoolTier.BOSS_ULTRA_RARE]: []
}, },
[Biome.RUINS]: { [Biome.RUINS]: {
[BiomePoolTier.COMMON]: [ [BiomePoolTier.COMMON]: [
@ -763,11 +759,11 @@ export const biomePokemonPools: BiomePokemonPools = {
[BiomePoolTier.UNCOMMON]: [ { 1: [ Species.ABRA ], 16: [ Species.KADABRA ] }, Species.SIGILYPH ], [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.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.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]: [ 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_RARE]: [ Species.MR_MIME, Species.ESPEON, Species.WOBBUFFET, Species.ARCHEOPS ],
[BiomePoolTier.BOSS_SUPER_RARE]: [ Species.VICTINI, Species.RUNERIGUS ], [BiomePoolTier.BOSS_SUPER_RARE]: [ Species.REGISTEEL, Species.RUNERIGUS ],
[BiomePoolTier.BOSS_ULTRA_RARE]: [ Species.MEW ] [BiomePoolTier.BOSS_ULTRA_RARE]: []
}, },
[Biome.WASTELAND]: { [Biome.WASTELAND]: {
[BiomePoolTier.COMMON]: [ [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.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.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.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]: [ Species.CLEFABLE, Species.LUNATONE, Species.SOLROCK, Species.BRONZONG, Species.MUSHARNA, Species.REUNICLUS, Species.MINIOR ],
[BiomePoolTier.BOSS_RARE]: [ Species.METAGROSS, Species.PORYGON_Z ], [BiomePoolTier.BOSS_RARE]: [ Species.METAGROSS, Species.PORYGON_Z ],
[BiomePoolTier.BOSS_SUPER_RARE]: [ Species.JIRACHI, Species.DEOXYS, Species.CRESSELIA, Species.CELESTEELA ], [BiomePoolTier.BOSS_SUPER_RARE]: [ Species.DEOXYS, Species.CRESSELIA, Species.CELESTEELA ],
[BiomePoolTier.BOSS_ULTRA_RARE]: [ Species.RAYQUAZA, Species.ARCEUS, Species.SOLGALEO, Species.LUNALA, Species.NECROZMA ] [BiomePoolTier.BOSS_ULTRA_RARE]: [ Species.RAYQUAZA, Species.SOLGALEO, Species.LUNALA, Species.NECROZMA ]
}, },
[Biome.CONSTRUCTION_SITE]: { [Biome.CONSTRUCTION_SITE]: {
[BiomePoolTier.COMMON]: [ [BiomePoolTier.COMMON]: [
@ -831,7 +827,7 @@ export const biomePokemonPools: BiomePokemonPools = {
{ 1: [ Species.RHYHORN ], 42: [ Species.RHYDON ] }, { 1: [ Species.RHYHORN ], 42: [ Species.RHYDON ] },
{ 1: [ Species.SCRAGGY ], 39: [ Species.SCRAFTY ] } { 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.SUPER_RARE]: [ Species.DITTO, Species.HITMONTOP, { 1: [ Species.GALAR_MEOWTH ], 28: [ Species.PERRSERKER ] } ],
[BiomePoolTier.ULTRA_RARE]: [ Species.COBALION ], [BiomePoolTier.ULTRA_RARE]: [ Species.COBALION ],
[BiomePoolTier.BOSS]: [ Species.MACHAMP, Species.CONKELDURR ], [BiomePoolTier.BOSS]: [ Species.MACHAMP, Species.CONKELDURR ],
@ -1075,7 +1071,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
[BiomePoolTier.RARE]: [ TrainerType.BLACK_BELT ], [BiomePoolTier.RARE]: [ TrainerType.BLACK_BELT ],
[BiomePoolTier.SUPER_RARE]: [], [BiomePoolTier.SUPER_RARE]: [],
[BiomePoolTier.ULTRA_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_RARE]: [],
[BiomePoolTier.BOSS_SUPER_RARE]: [], [BiomePoolTier.BOSS_SUPER_RARE]: [],
[BiomePoolTier.BOSS_ULTRA_RARE]: [] [BiomePoolTier.BOSS_ULTRA_RARE]: []
@ -1108,7 +1104,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
[BiomePoolTier.RARE]: [ TrainerType.ARTIST ], [BiomePoolTier.RARE]: [ TrainerType.ARTIST ],
[BiomePoolTier.SUPER_RARE]: [], [BiomePoolTier.SUPER_RARE]: [],
[BiomePoolTier.ULTRA_RARE]: [], [BiomePoolTier.ULTRA_RARE]: [],
[BiomePoolTier.BOSS]: [ TrainerType.CHEREN ], [BiomePoolTier.BOSS]: [ TrainerType.NORMAN, TrainerType.CHEREN ],
[BiomePoolTier.BOSS_RARE]: [], [BiomePoolTier.BOSS_RARE]: [],
[BiomePoolTier.BOSS_SUPER_RARE]: [], [BiomePoolTier.BOSS_SUPER_RARE]: [],
[BiomePoolTier.BOSS_ULTRA_RARE]: [] [BiomePoolTier.BOSS_ULTRA_RARE]: []
@ -2206,10 +2202,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
[ Biome.END, BiomePoolTier.ULTRA_RARE ] [ Biome.END, BiomePoolTier.ULTRA_RARE ]
] ]
], ],
[ Species.MEW, Type.PSYCHIC, -1, [ [ Species.MEW, Type.PSYCHIC, -1, [ ]
[ Biome.RUINS, BiomePoolTier.ULTRA_RARE ],
[ Biome.RUINS, BiomePoolTier.BOSS_ULTRA_RARE ]
]
], ],
[ Species.CHIKORITA, Type.GRASS, -1, [ [ Species.CHIKORITA, Type.GRASS, -1, [
[ Biome.TALL_GRASS, BiomePoolTier.RARE ] [ Biome.TALL_GRASS, BiomePoolTier.RARE ]
@ -2306,21 +2299,13 @@ export const biomeTrainerPools: BiomeTrainerPools = {
[ Biome.SEABED, BiomePoolTier.BOSS ] [ Biome.SEABED, BiomePoolTier.BOSS ]
] ]
], ],
[ Species.PICHU, Type.ELECTRIC, -1, [ [ Species.PICHU, Type.ELECTRIC, -1, [ ]
[ Biome.TOWN, BiomePoolTier.UNCOMMON ]
]
], ],
[ Species.CLEFFA, Type.FAIRY, -1, [ [ Species.CLEFFA, Type.FAIRY, -1, [ ]
[ Biome.TOWN, BiomePoolTier.RARE ]
]
], ],
[ Species.IGGLYBUFF, Type.NORMAL, Type.FAIRY, [ [ Species.IGGLYBUFF, Type.NORMAL, Type.FAIRY, [ ]
[ Biome.TOWN, BiomePoolTier.UNCOMMON ]
]
], ],
[ Species.TOGEPI, Type.FAIRY, -1, [ [ Species.TOGEPI, Type.FAIRY, -1, [ ]
[ Biome.TOWN, BiomePoolTier.SUPER_RARE ]
]
], ],
[ Species.TOGETIC, Type.FAIRY, Type.FLYING, [ [ Species.TOGETIC, Type.FAIRY, Type.FLYING, [
[ Biome.FAIRY_CAVE, BiomePoolTier.UNCOMMON ] [ Biome.FAIRY_CAVE, BiomePoolTier.UNCOMMON ]
@ -2615,11 +2600,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
[ Biome.METROPOLIS, BiomePoolTier.SUPER_RARE ] [ Biome.METROPOLIS, BiomePoolTier.SUPER_RARE ]
] ]
], ],
[ Species.TYROGUE, Type.FIGHTING, -1, [ [ Species.TYROGUE, Type.FIGHTING, -1, [ ]
[ Biome.TOWN, BiomePoolTier.SUPER_RARE ],
[ Biome.DOJO, BiomePoolTier.RARE ],
[ Biome.CONSTRUCTION_SITE, BiomePoolTier.RARE ]
]
], ],
[ Species.HITMONTOP, Type.FIGHTING, -1, [ [ Species.HITMONTOP, Type.FIGHTING, -1, [
[ Biome.DOJO, BiomePoolTier.SUPER_RARE ], [ Biome.DOJO, BiomePoolTier.SUPER_RARE ],
@ -2627,17 +2608,11 @@ export const biomeTrainerPools: BiomeTrainerPools = {
[ Biome.CONSTRUCTION_SITE, BiomePoolTier.SUPER_RARE ] [ Biome.CONSTRUCTION_SITE, BiomePoolTier.SUPER_RARE ]
] ]
], ],
[ Species.SMOOCHUM, Type.ICE, Type.PSYCHIC, [ [ Species.SMOOCHUM, Type.ICE, Type.PSYCHIC, [ ]
[ Biome.TOWN, BiomePoolTier.SUPER_RARE ]
]
], ],
[ Species.ELEKID, Type.ELECTRIC, -1, [ [ Species.ELEKID, Type.ELECTRIC, -1, [ ]
[ Biome.TOWN, BiomePoolTier.SUPER_RARE ]
]
], ],
[ Species.MAGBY, Type.FIRE, -1, [ [ Species.MAGBY, Type.FIRE, -1, [ ]
[ Biome.TOWN, BiomePoolTier.SUPER_RARE ]
]
], ],
[ Species.MILTANK, Type.NORMAL, -1, [ [ Species.MILTANK, Type.NORMAL, -1, [
[ Biome.MEADOW, BiomePoolTier.RARE ], [ Biome.MEADOW, BiomePoolTier.RARE ],
@ -2688,10 +2663,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
[ Biome.MOUNTAIN, BiomePoolTier.BOSS_ULTRA_RARE ] [ Biome.MOUNTAIN, BiomePoolTier.BOSS_ULTRA_RARE ]
] ]
], ],
[ Species.CELEBI, Type.PSYCHIC, Type.GRASS, [ [ Species.CELEBI, Type.PSYCHIC, Type.GRASS, [ ]
[ Biome.FOREST, BiomePoolTier.ULTRA_RARE ],
[ Biome.FOREST, BiomePoolTier.BOSS_ULTRA_RARE ]
]
], ],
[ Species.TREECKO, Type.GRASS, -1, [ [ Species.TREECKO, Type.GRASS, -1, [
[ Biome.FOREST, BiomePoolTier.RARE ] [ Biome.FOREST, BiomePoolTier.RARE ]
@ -2918,9 +2890,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
[ Biome.DOJO, BiomePoolTier.BOSS ] [ Biome.DOJO, BiomePoolTier.BOSS ]
] ]
], ],
[ Species.AZURILL, Type.NORMAL, Type.FAIRY, [ [ Species.AZURILL, Type.NORMAL, Type.FAIRY, [ ]
[ Biome.TOWN, BiomePoolTier.UNCOMMON ]
]
], ],
[ Species.NOSEPASS, Type.ROCK, -1, [ [ Species.NOSEPASS, Type.ROCK, -1, [
[ Biome.CAVE, BiomePoolTier.UNCOMMON ] [ Biome.CAVE, BiomePoolTier.UNCOMMON ]
@ -3216,9 +3186,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
[ Biome.ABYSS, BiomePoolTier.BOSS ] [ Biome.ABYSS, BiomePoolTier.BOSS ]
] ]
], ],
[ Species.WYNAUT, Type.PSYCHIC, -1, [ [ Species.WYNAUT, Type.PSYCHIC, -1, [ ]
[ Biome.TOWN, BiomePoolTier.SUPER_RARE ]
]
], ],
[ Species.SNORUNT, Type.ICE, -1, [ [ Species.SNORUNT, Type.ICE, -1, [
[ Biome.ICE_CAVE, BiomePoolTier.UNCOMMON ] [ Biome.ICE_CAVE, BiomePoolTier.UNCOMMON ]
@ -3306,8 +3274,8 @@ export const biomeTrainerPools: BiomeTrainerPools = {
] ]
], ],
[ Species.REGISTEEL, Type.STEEL, -1, [ [ Species.REGISTEEL, Type.STEEL, -1, [
[ Biome.CAVE, BiomePoolTier.ULTRA_RARE ], [ Biome.RUINS, BiomePoolTier.ULTRA_RARE ],
[ Biome.CAVE, BiomePoolTier.BOSS_SUPER_RARE ] [ Biome.RUINS, BiomePoolTier.BOSS_SUPER_RARE ]
] ]
], ],
[ Species.LATIAS, Type.DRAGON, Type.PSYCHIC, [ [ Species.LATIAS, Type.DRAGON, Type.PSYCHIC, [
@ -3333,10 +3301,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
[ Biome.END, BiomePoolTier.ULTRA_RARE ] [ Biome.END, BiomePoolTier.ULTRA_RARE ]
] ]
], ],
[ Species.JIRACHI, Type.STEEL, Type.PSYCHIC, [ [ Species.JIRACHI, Type.STEEL, Type.PSYCHIC, [ ]
[ Biome.SPACE, BiomePoolTier.ULTRA_RARE ],
[ Biome.SPACE, BiomePoolTier.BOSS_SUPER_RARE ]
]
], ],
[ Species.DEOXYS, Type.PSYCHIC, -1, [ [ Species.DEOXYS, Type.PSYCHIC, -1, [
[ Biome.SPACE, BiomePoolTier.ULTRA_RARE ], [ Biome.SPACE, BiomePoolTier.ULTRA_RARE ],
@ -3435,9 +3400,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
[ Biome.POWER_PLANT, BiomePoolTier.BOSS ] [ Biome.POWER_PLANT, BiomePoolTier.BOSS ]
] ]
], ],
[ Species.BUDEW, Type.GRASS, Type.POISON, [ [ Species.BUDEW, Type.GRASS, Type.POISON, [ ]
[ Biome.TOWN, BiomePoolTier.UNCOMMON ]
]
], ],
[ Species.ROSERADE, Type.GRASS, Type.POISON, [ [ Species.ROSERADE, Type.GRASS, Type.POISON, [
[ Biome.MEADOW, BiomePoolTier.BOSS ] [ Biome.MEADOW, BiomePoolTier.BOSS ]
@ -3599,17 +3562,11 @@ export const biomeTrainerPools: BiomeTrainerPools = {
[ Biome.LABORATORY, BiomePoolTier.BOSS ] [ Biome.LABORATORY, BiomePoolTier.BOSS ]
] ]
], ],
[ Species.BONSLY, Type.ROCK, -1, [ [ Species.BONSLY, Type.ROCK, -1, [ ]
[ Biome.TOWN, BiomePoolTier.SUPER_RARE ]
]
], ],
[ Species.MIME_JR, Type.PSYCHIC, Type.FAIRY, [ [ Species.MIME_JR, Type.PSYCHIC, Type.FAIRY, [ ]
[ Biome.TOWN, BiomePoolTier.SUPER_RARE ]
]
], ],
[ Species.HAPPINY, Type.NORMAL, -1, [ [ Species.HAPPINY, Type.NORMAL, -1, []
[ Biome.TOWN, BiomePoolTier.SUPER_RARE ]
]
], ],
[ Species.CHATOT, Type.NORMAL, Type.FLYING, [ [ Species.CHATOT, Type.NORMAL, Type.FLYING, [
[ Biome.JUNGLE, BiomePoolTier.SUPER_RARE ] [ Biome.JUNGLE, BiomePoolTier.SUPER_RARE ]
@ -3638,13 +3595,9 @@ export const biomeTrainerPools: BiomeTrainerPools = {
[ Biome.END, BiomePoolTier.COMMON ] [ Biome.END, BiomePoolTier.COMMON ]
] ]
], ],
[ Species.MUNCHLAX, Type.NORMAL, -1, [ [ Species.MUNCHLAX, Type.NORMAL, -1, [ ]
[ Biome.TOWN, BiomePoolTier.SUPER_RARE ]
]
], ],
[ Species.RIOLU, Type.FIGHTING, -1, [ [ Species.RIOLU, Type.FIGHTING, -1, [ ]
[ Biome.TOWN, BiomePoolTier.SUPER_RARE ]
]
], ],
[ Species.LUCARIO, Type.FIGHTING, Type.STEEL, [ [ Species.LUCARIO, Type.FIGHTING, Type.STEEL, [
[ Biome.DOJO, BiomePoolTier.RARE ], [ Biome.DOJO, BiomePoolTier.RARE ],
@ -3844,34 +3797,22 @@ export const biomeTrainerPools: BiomeTrainerPools = {
[ Biome.SPACE, BiomePoolTier.BOSS_SUPER_RARE ] [ Biome.SPACE, BiomePoolTier.BOSS_SUPER_RARE ]
] ]
], ],
[ Species.PHIONE, Type.WATER, -1, [ [ Species.PHIONE, Type.WATER, -1, [ ]
[ Biome.SEABED, BiomePoolTier.RARE ],
[ Biome.SEABED, BiomePoolTier.BOSS_RARE ]
]
], ],
[ Species.MANAPHY, Type.WATER, -1, [ [ Species.MANAPHY, Type.WATER, -1, [ ]
[ Biome.SEABED, BiomePoolTier.ULTRA_RARE ],
[ Biome.SEABED, BiomePoolTier.BOSS_SUPER_RARE ]
]
], ],
[ Species.DARKRAI, Type.DARK, -1, [ [ Species.DARKRAI, Type.DARK, -1, [
[ Biome.ABYSS, BiomePoolTier.ULTRA_RARE ], [ Biome.ABYSS, BiomePoolTier.ULTRA_RARE ],
[ Biome.ABYSS, BiomePoolTier.BOSS_SUPER_RARE ] [ Biome.ABYSS, BiomePoolTier.BOSS_SUPER_RARE ]
] ]
], ],
[ Species.SHAYMIN, Type.GRASS, -1, [ [ Species.SHAYMIN, Type.GRASS, -1, [ ]
[ Biome.GRASS, BiomePoolTier.BOSS_ULTRA_RARE ]
]
], ],
[ Species.ARCEUS, Type.NORMAL, -1, [ [ Species.ARCEUS, Type.NORMAL, -1, [
[ Biome.SPACE, BiomePoolTier.BOSS_ULTRA_RARE ],
[ Biome.END, BiomePoolTier.ULTRA_RARE ] [ Biome.END, BiomePoolTier.ULTRA_RARE ]
] ]
], ],
[ Species.VICTINI, Type.PSYCHIC, Type.FIRE, [ [ Species.VICTINI, Type.PSYCHIC, Type.FIRE, [ ]
[ Biome.RUINS, BiomePoolTier.ULTRA_RARE ],
[ Biome.RUINS, BiomePoolTier.BOSS_SUPER_RARE ]
]
], ],
[ Species.SNIVY, Type.GRASS, -1, [ [ Species.SNIVY, Type.GRASS, -1, [
[ Biome.JUNGLE, BiomePoolTier.RARE ] [ Biome.JUNGLE, BiomePoolTier.RARE ]
@ -5345,13 +5286,9 @@ export const biomeTrainerPools: BiomeTrainerPools = {
[ Biome.POWER_PLANT, BiomePoolTier.BOSS_SUPER_RARE ] [ Biome.POWER_PLANT, BiomePoolTier.BOSS_SUPER_RARE ]
] ]
], ],
[ Species.MELTAN, Type.STEEL, -1, [ [ Species.MELTAN, Type.STEEL, -1, [ ]
[ Biome.FACTORY, BiomePoolTier.ULTRA_RARE ]
]
], ],
[ Species.MELMETAL, Type.STEEL, -1, [ [ Species.MELMETAL, Type.STEEL, -1, [ ]
[ Biome.FACTORY, BiomePoolTier.BOSS_ULTRA_RARE ]
]
], ],
[ Species.GROOKEY, Type.GRASS, -1, [ [ Species.GROOKEY, Type.GRASS, -1, [
[ Biome.JUNGLE, BiomePoolTier.RARE ] [ Biome.JUNGLE, BiomePoolTier.RARE ]
@ -6619,7 +6556,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
] ]
], ],
[ TrainerType.NORMAN, [ [ TrainerType.NORMAN, [
[ Biome.PLAINS, BiomePoolTier.BOSS ] [ Biome.METROPOLIS, BiomePoolTier.BOSS ]
] ]
], ],
[ TrainerType.WINONA, [ [ TrainerType.WINONA, [
@ -6673,9 +6610,15 @@ export const biomeTrainerPools: BiomeTrainerPools = {
[ Biome.POWER_PLANT, BiomePoolTier.BOSS ] [ Biome.POWER_PLANT, BiomePoolTier.BOSS ]
] ]
], ],
[ TrainerType.CILAN, [] ], [ TrainerType.CILAN, [
[ TrainerType.CHILI, [] ], [ Biome.PLAINS, BiomePoolTier.BOSS ]
[ TrainerType.CRESS, [] ], ] ],
[ TrainerType.CHILI, [
[ Biome.PLAINS, BiomePoolTier.BOSS ]
] ],
[ TrainerType.CRESS, [
[ Biome.PLAINS, BiomePoolTier.BOSS ]
] ],
[ TrainerType.CHEREN, [ [ TrainerType.CHEREN, [
[ Biome.PLAINS, BiomePoolTier.BOSS ], [ Biome.PLAINS, BiomePoolTier.BOSS ],
[ Biome.METROPOLIS, BiomePoolTier.BOSS ] [ Biome.METROPOLIS, BiomePoolTier.BOSS ]

View File

@ -1,4 +1,9 @@
import { ModifierTier } from "../modifier/modifier-type"; 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; export const EGG_SEED = 1073741824;
@ -12,12 +17,99 @@ export class Egg {
public id: integer; public id: integer;
public tier: ModifierTier; public tier: ModifierTier;
public gachaType: GachaType; public gachaType: GachaType;
public hatchWaves: integer;
public timestamp: integer; public timestamp: integer;
constructor(id: integer, gachaType: GachaType, timestamp: integer) { constructor(id: integer, gachaType: GachaType, hatchWaves: integer, timestamp: integer) {
this.id = id; this.id = id;
this.tier = Math.floor(id / EGG_SEED); this.tier = Math.floor(id / EGG_SEED);
this.gachaType = gachaType; this.gachaType = gachaType;
this.hatchWaves = hatchWaves;
this.timestamp = timestamp; 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; const originalWarn = console.warn;
// Ignore warnings for missing frames, because there will be a lot // Ignore warnings for missing frames, because there will be a lot
console.warn = () => {}; 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; console.warn = originalWarn;
scene.anims.create({ scene.anims.create({
key: this.getSpriteKey(female, formIndex, shiny), key: this.getSpriteKey(female, formIndex, shiny),

View File

@ -3,12 +3,16 @@ import { BattlePhase } from "./battle-phase";
import BattleScene, { AnySound } from "./battle-scene"; import BattleScene, { AnySound } from "./battle-scene";
import * as Utils from "./utils"; import * as Utils from "./utils";
import { Mode } from "./ui/ui"; 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 EggHatchSceneHandler from "./ui/egg-hatch-scene-handler";
import { ModifierTier } from "./modifier/modifier-type"; import { ModifierTier } from "./modifier/modifier-type";
import { Species } from "./data/species"; import { Species } from "./data/species";
import Pokemon, { PlayerPokemon } from "./pokemon"; import Pokemon, { PlayerPokemon } from "./pokemon";
import { getPokemonSpecies, speciesStarters } from "./data/pokemon-species"; 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 { export class EggHatchPhase extends BattlePhase {
private egg: Egg; private egg: Egg;
@ -22,6 +26,9 @@ export class EggHatchPhase extends BattlePhase {
private eggLightraysOverlay: Phaser.GameObjects.Sprite; private eggLightraysOverlay: Phaser.GameObjects.Sprite;
private pokemonSprite: Phaser.GameObjects.Sprite; private pokemonSprite: Phaser.GameObjects.Sprite;
private infoContainer: Phaser.GameObjects.Container;
private statsContainer: StatsContainer;
constructor(scene: BattleScene, egg: Egg) { constructor(scene: BattleScene, egg: Egg) {
super(scene); super(scene);
@ -36,6 +43,13 @@ export class EggHatchPhase extends BattlePhase {
if (!this.egg) if (!this.egg)
return this.end(); 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); this.scene.fadeOutBgm(null, false);
const eggHatchHandler = this.scene.ui.getHandler() as EggHatchSceneHandler; 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.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 = this.scene.add.sprite(0, 0, 'egg_crack', '0');
this.eggCrackSprite.setVisible(false); this.eggCrackSprite.setVisible(false);
@ -70,9 +84,58 @@ export class EggHatchPhase extends BattlePhase {
this.eggHatchOverlay.setAlpha(0); this.eggHatchOverlay.setAlpha(0);
this.scene.fieldUI.add(this.eggHatchOverlay); 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(); 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); this.pokemonSprite.setVisible(false);
@ -108,19 +171,35 @@ export class EggHatchPhase extends BattlePhase {
ease: 'Cubic.easeIn' ease: 'Cubic.easeIn'
}); });
this.scene.time.delayedCall(Utils.fixedInt(1500), () => { 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.eggContainer.setVisible(false);
this.pokemonSprite.play(pokemon.getSpriteKey(true)); this.pokemonSprite.play(pokemon.getSpriteKey(true));
this.pokemonSprite.setVisible(true); this.pokemonSprite.setVisible(true);
this.scene.time.delayedCall(Utils.fixedInt(1000), () => { this.scene.time.delayedCall(Utils.fixedInt(1000), () => {
pokemon.cry(); pokemon.cry();
this.scene.time.delayedCall(Utils.fixedInt(1250), () => { 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.playSoundWithoutBgm('evolution_fanfare');
this.scene.ui.showText(`${pokemon.name} hatched from the egg!`, null, () => { this.scene.ui.showText(`${pokemon.name} hatched from the egg!`, null, () => {
this.scene.ui.showText(null, 0); this.scene.gameData.updateSpeciesDexIvs(pokemon.species.speciesId, pokemon.ivs);
this.end(); this.scene.gameData.setPokemonCaught(pokemon).then(() => {
this.scene.ui.showText(null, 0);
this.end();
});
}, null, true, 3000); }, 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({ this.scene.tweens.add({
@ -193,7 +272,8 @@ export class EggHatchPhase extends BattlePhase {
doSprayParticle(trigIndex: integer, offsetY: number) { doSprayParticle(trigIndex: integer, offsetY: number) {
const initialX = this.eggHatchBg.displayWidth / 2; const initialX = this.eggHatchBg.displayWidth / 2;
const initialY = this.eggHatchBg.displayHeight / 2 + offsetY; 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); this.eggHatchContainer.add(particle);
let f = 0; let f = 0;
@ -228,58 +308,99 @@ export class EggHatchPhase extends BattlePhase {
} }
generatePokemon(): Pokemon { generatePokemon(): Pokemon {
let minStarterValue: integer; let ret: Pokemon;
let maxStarterValue: integer; let speciesOverride: Species;
switch (this.egg.tier) { if (this.egg.isManaphyEgg()) {
case ModifierTier.GREAT: this.scene.executeWithSeedOffset(() => {
minStarterValue = 3; const rand = Utils.randSeedInt(8);
maxStarterValue = 5;
break; speciesOverride = rand ? Species.PHIONE : Species.MANAPHY;
case ModifierTier.ULTRA: }, this.egg.id, EGG_SEED.toString());
minStarterValue = 6; } else if (this.egg.tier === ModifierTier.MASTER
maxStarterValue = 7; && this.egg.gachaType === GachaType.LEGENDARY) {
break; this.scene.executeWithSeedOffset(() => {
case ModifierTier.MASTER: if (!Utils.randSeedInt(2))
minStarterValue = 8; speciesOverride = getLegendaryGachaSpeciesForTimestamp(this.scene, this.egg.timestamp);
maxStarterValue = 9; }, this.egg.id, EGG_SEED.toString());
break;
default:
minStarterValue = 1;
maxStarterValue = 2;
break;
} }
const speciesPool = Object.keys(speciesStarters) if (speciesOverride) {
.filter(s => speciesStarters[s] >= minStarterValue && speciesStarters[s] <= maxStarterValue) this.scene.executeWithSeedOffset(() => {
.map(s => parseInt(s) as Species) ret = new PlayerPokemon(this.scene, getPokemonSpecies(speciesOverride), 5, null, null, undefined, false);
.filter(s => getPokemonSpecies(s).isObtainable()); }, this.egg.id, EGG_SEED.toString());
} else {
let minStarterValue: integer;
let maxStarterValue: integer;
let totalWeight = 0; switch (this.egg.tier) {
const speciesWeights = []; case ModifierTier.GREAT:
for (let speciesId of speciesPool) { minStarterValue = 4;
const weight = Math.floor((((maxStarterValue - speciesStarters[speciesId]) / ((maxStarterValue - minStarterValue) + 1)) * 1.5 + 1) * 100); maxStarterValue = 5;
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];
break; 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 BattleScene from './battle-scene';
import InvertPostFX from './pipelines/invert'; import InvertPostFX from './pipelines/invert';
import { version } from '../package.json'; import { version } from '../package.json';
import BBCodeTextPlugin from 'phaser3-rex-plugins/plugins/bbcodetext-plugin';
const config: Phaser.Types.Core.GameConfig = { const config: Phaser.Types.Core.GameConfig = {
type: Phaser.WEBGL, type: Phaser.WEBGL,
@ -11,6 +12,13 @@ const config: Phaser.Types.Core.GameConfig = {
height: 1080, height: 1080,
mode: Phaser.Scale.FIT mode: Phaser.Scale.FIT
}, },
plugins: {
global: [{
key: 'rexBBCodeTextPlugin',
plugin: BBCodeTextPlugin,
start: true
}]
},
pixelArt: true, pixelArt: true,
pipeline: [ InvertPostFX ] as unknown as Phaser.Types.Core.PipelineConfig, pipeline: [ InvertPostFX ] as unknown as Phaser.Types.Core.PipelineConfig,
scene: [ BattleScene ], scene: [ BattleScene ],

View File

@ -15,6 +15,7 @@ import { GameMode } from '../game-mode';
import { StatusEffect, getStatusEffectDescriptor } from '../data/status-effect'; import { StatusEffect, getStatusEffectDescriptor } from '../data/status-effect';
import { SpeciesFormKey } from '../data/pokemon-species'; import { SpeciesFormKey } from '../data/pokemon-species';
import BattleScene from '../battle-scene'; import BattleScene from '../battle-scene';
import { VoucherType, getVoucherTypeIcon, getVoucherTypeName } from '../system/voucher';
type Modifier = Modifiers.Modifier; 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 { export class PokemonModifierType extends ModifierType {
public selectFilter: PokemonSelectFilter; public selectFilter: PokemonSelectFilter;
@ -702,6 +710,10 @@ export const modifierTypes = {
MINI_BLACK_HOLE: () => new TurnHeldItemTransferModifierType('Mini Black Hole'), 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', 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'), (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.ABILITY_CHARM, 2),
new WeightedModifierType(modifierTypes.IV_SCANNER, 2), new WeightedModifierType(modifierTypes.IV_SCANNER, 2),
new WeightedModifierType(modifierTypes.EXP_BALANCE, 1), 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.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), 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; }), ].map(m => { m.setTier(ModifierTier.ULTRA); return m; }),
[ModifierTier.MASTER]: [ [ModifierTier.MASTER]: [
new WeightedModifierType(modifierTypes.MASTER_BALL, 3), new WeightedModifierType(modifierTypes.MASTER_BALL, 32),
new WeightedModifierType(modifierTypes.SHINY_CHARM, 2), new WeightedModifierType(modifierTypes.SHINY_CHARM, 18),
new WeightedModifierType(modifierTypes.MEGA_BRACELET, 1), new WeightedModifierType(modifierTypes.MEGA_BRACELET, 12),
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.VOUCHER, 6),
new WeightedModifierType(modifierTypes.MINI_BLACK_HOLE, (party: Pokemon[]) => party[0].scene.gameData.unlocks[Unlockables.MINI_BLACK_HOLE] ? 1 : 0), 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; }), ].map(m => { m.setTier(ModifierTier.MASTER); return m; }),
[ModifierTier.LUXURY]: [ [ModifierTier.LUXURY]: [
new WeightedModifierType(modifierTypes.GOLDEN_EXP_CHARM, 1), 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 { BerryType, getBerryEffectFunc, getBerryPredicate } from '../data/berry';
import { StatusEffect, getStatusEffectDescriptor } from '../data/status-effect'; import { StatusEffect, getStatusEffectDescriptor } from '../data/status-effect';
import { MoneyAchv } from '../system/achv'; import { MoneyAchv } from '../system/achv';
import { VoucherType } from '../system/voucher';
type ModifierType = ModifierTypes.ModifierType; type ModifierType = ModifierTypes.ModifierType;
export type ModifierPredicate = (modifier: Modifier) => boolean; 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 { export abstract class LapsingPersistentModifier extends PersistentModifier {
protected battlesLeft: integer; protected battlesLeft: integer;
@ -1299,7 +1319,7 @@ export class ShinyRateBoosterModifier extends PersistentModifier {
} }
apply(args: any[]): boolean { 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; return true;
} }

View File

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

View File

@ -31,6 +31,14 @@ export class Achv {
this.conditionFunc = conditionFunc; this.conditionFunc = conditionFunc;
} }
getName(): string {
return this.name;
}
getIconImage(): string {
return this.iconImage;
}
setSecret(hasParent?: boolean): this { setSecret(hasParent?: boolean): this {
this.secret = true; this.secret = true;
this.hasParent = !!hasParent; this.hasParent = !!hasParent;
@ -118,10 +126,13 @@ export const achvs = {
MEGA_EVOLVE: new Achv('Megamorph', 'Mega evolve a Pokémon', 'mega_bracelet', 50), 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), 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(), 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_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), 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), 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), 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), 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) 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 { export default class EggData {
public id: integer; public id: integer;
public gachaType: GachaType; public gachaType: GachaType;
public hatchWaves: integer;
public timestamp: integer; public timestamp: integer;
constructor(source: Egg | any) { constructor(source: Egg | any) {
const sourceEgg = source instanceof Egg ? source as Egg : null; const sourceEgg = source instanceof Egg ? source as Egg : null;
this.id = sourceEgg ? sourceEgg.id : source.id; this.id = sourceEgg ? sourceEgg.id : source.id;
this.gachaType = sourceEgg ? sourceEgg.gachaType : source.gachaType; this.gachaType = sourceEgg ? sourceEgg.gachaType : source.gachaType;
this.hatchWaves = sourceEgg ? sourceEgg.hatchWaves : source.hatchWaves;
this.timestamp = sourceEgg ? sourceEgg.timestamp : source.timestamp; this.timestamp = sourceEgg ? sourceEgg.timestamp : source.timestamp;
} }
toEgg(): Egg { 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 { achvs } from "./achv";
import EggData from "./egg-data"; import EggData from "./egg-data";
import { Egg } from "../data/egg"; import { Egg } from "../data/egg";
import { VoucherType, vouchers } from "./voucher";
interface SystemSaveData { interface SystemSaveData {
trainerId: integer; trainerId: integer;
@ -23,6 +24,8 @@ interface SystemSaveData {
dexData: DexData; dexData: DexData;
unlocks: Unlocks; unlocks: Unlocks;
achvUnlocks: AchvUnlocks; achvUnlocks: AchvUnlocks;
voucherUnlocks: VoucherUnlocks;
voucherCounts: VoucherCounts;
eggs: EggData[]; eggs: EggData[];
gameVersion: string; gameVersion: string;
timestamp: integer; timestamp: integer;
@ -54,6 +57,14 @@ interface AchvUnlocks {
[key: string]: integer [key: string]: integer
} }
interface VoucherUnlocks {
[key: string]: integer
}
export interface VoucherCounts {
[type: string]: integer;
}
export interface DexData { export interface DexData {
[key: integer]: DexEntry [key: integer]: DexEntry
} }
@ -96,6 +107,8 @@ export class GameData {
public achvUnlocks: AchvUnlocks; public achvUnlocks: AchvUnlocks;
public voucherUnlocks: VoucherUnlocks;
public voucherCounts: VoucherCounts;
public eggs: Egg[]; public eggs: Egg[];
constructor(scene: BattleScene) { constructor(scene: BattleScene) {
@ -109,6 +122,12 @@ export class GameData {
[Unlockables.SPLICED_ENDLESS_MODE]: false [Unlockables.SPLICED_ENDLESS_MODE]: false
}; };
this.achvUnlocks = {}; this.achvUnlocks = {};
this.voucherUnlocks = {};
this.voucherCounts = {
[VoucherType.REGULAR]: 0,
[VoucherType.PLUS]: 0,
[VoucherType.PREMIUM]: 0
};
this.eggs = []; this.eggs = [];
this.initDexData(); this.initDexData();
this.loadSystem(); this.loadSystem();
@ -124,6 +143,8 @@ export class GameData {
dexData: this.dexData, dexData: this.dexData,
unlocks: this.unlocks, unlocks: this.unlocks,
achvUnlocks: this.achvUnlocks, achvUnlocks: this.achvUnlocks,
voucherUnlocks: this.voucherUnlocks,
voucherCounts: this.voucherCounts,
eggs: this.eggs.map(e => new EggData(e)), eggs: this.eggs.map(e => new EggData(e)),
gameVersion: this.scene.game.config.gameVersion, gameVersion: this.scene.game.config.gameVersion,
timestamp: new Date().getTime() timestamp: new Date().getTime()
@ -141,7 +162,16 @@ export class GameData {
if (!localStorage.hasOwnProperty('data')) if (!localStorage.hasOwnProperty('data'))
return false; 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); 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 this.eggs = data.eggs
? data.eggs.map(e => e.toEgg()) ? 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 { getSpeciesDefaultDexAttr(species: PokemonSpecies): bigint {
let ret = 0n; let ret = 0n;
const dexEntry = this.dexData[species.speciesId]; 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 BattleScene from "../battle-scene";
import { Achv } from "../system/achv"; import { Achv } from "../system/achv";
import { Voucher } from "../system/voucher";
import { TextStyle, addTextObject } from "./text"; import { TextStyle, addTextObject } from "./text";
export default class AchvBar extends Phaser.GameObjects.Container { 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 scoreText: Phaser.GameObjects.Text;
private descriptionText: Phaser.GameObjects.Text; private descriptionText: Phaser.GameObjects.Text;
private queue: Achv[] = []; private queue: (Achv | Voucher)[] = [];
public shown: boolean; public shown: boolean;
@ -47,7 +48,7 @@ export default class AchvBar extends Phaser.GameObjects.Container {
this.shown = false; this.shown = false;
} }
showAchv(achv: Achv): void { showAchv(achv: Achv | Voucher): void {
if (this.shown) { if (this.shown) {
this.queue.push(achv); this.queue.push(achv);
return; return;
@ -56,10 +57,12 @@ export default class AchvBar extends Phaser.GameObjects.Container {
const tier = achv.getTier(); const tier = achv.getTier();
this.bg.setTexture(`achv_bar${tier ? `_${tier + 1}` : ''}`); this.bg.setTexture(`achv_bar${tier ? `_${tier + 1}` : ''}`);
this.icon.setFrame(achv.iconImage); this.icon.setFrame(achv.getIconImage());
this.titleText.setText(achv.name); this.titleText.setText(achv.getName());
this.descriptionText.setText(achv.description); 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'); (this.scene as BattleScene).playSound('achv');

View File

@ -127,10 +127,10 @@ export default class CommandUiHandler extends UiHandler {
if (!this.cursorObj) { if (!this.cursorObj) {
this.cursorObj = this.scene.add.image(0, 0, 'cursor'); 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; 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"; import UiHandler from "./uiHandler";
export default class EggHatchSceneHandler extends UiHandler { export default class EggHatchSceneHandler extends UiHandler {
public eggHatchContainer: Phaser.GameObjects.Container; public eggHatchContainer: Phaser.GameObjects.Container;
constructor(scene: BattleScene) { constructor(scene: BattleScene) {
super(scene, Mode.EGG_HATCH_SCENE); 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);
const eggLightraysAnimFrames = this.scene.anims.generateFrameNames('egg_lightrays', { start: 0, end: 3 });
this.scene.anims.create({
key: 'egg_lightrays',
frames: eggLightraysAnimFrames,
frameRate: 32
});
}
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;
}
clear() {
this.eggHatchContainer.removeAll(true);
}
} }
setup() {
this.eggHatchContainer = this.scene.add.container(0, -this.scene.game.canvas.height / 6);
this.scene.fieldUI.add(this.eggHatchContainer);
const eggLightraysAnimFrames = this.scene.anims.generateFrameNames('egg_lightrays', { start: 0, end: 3 });
this.scene.anims.create({
key: 'egg_lightrays',
frames: eggLightraysAnimFrames,
frameRate: 32
});
}
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 { export enum MenuOptions {
SETTINGS, SETTINGS,
ACHIEVEMENTS ACHIEVEMENTS,
VOUCHERS,
EGG_LIST,
EGG_GACHA
} }
export default class MenuUiHandler extends UiHandler { export default class MenuUiHandler extends UiHandler {
@ -62,6 +65,7 @@ export default class MenuUiHandler extends UiHandler {
const ui = this.getUi(); const ui = this.getUi();
let success = false; let success = false;
let error = false;
if (button === Button.ACTION) { if (button === Button.ACTION) {
switch (this.cursor as MenuOptions) { switch (this.cursor as MenuOptions) {
@ -73,6 +77,24 @@ export default class MenuUiHandler extends UiHandler {
this.scene.ui.setOverlayMode(Mode.ACHIEVEMENTS); this.scene.ui.setOverlayMode(Mode.ACHIEVEMENTS);
success = true; success = true;
break; 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) { } else if (button === Button.CANCEL) {
success = true; success = true;
@ -93,6 +115,8 @@ export default class MenuUiHandler extends UiHandler {
if (success) if (success)
ui.playSelect(); ui.playSelect();
else if (error)
ui.playError();
return true; return true;
} }

View File

@ -8,7 +8,6 @@ export enum PokemonIconAnimMode {
} }
export default class PokemonIconAnimHandler { export default class PokemonIconAnimHandler {
private counter: Phaser.Tweens.Tween;
private icons: Map<Phaser.GameObjects.Sprite, PokemonIconAnimMode>; private icons: Map<Phaser.GameObjects.Sprite, PokemonIconAnimMode>;
private toggled: boolean; private toggled: boolean;
@ -22,7 +21,7 @@ export default class PokemonIconAnimHandler {
for (let i of this.icons.keys()) for (let i of this.icons.keys())
i.y += this.getModeYDelta(this.icons.get(i)) * (this.toggled ? 1 : -1); i.y += this.getModeYDelta(this.icons.get(i)) * (this.toggled ? 1 : -1);
}; };
this.counter = scene.tweens.addCounter({ scene.tweens.addCounter({
duration: Utils.fixedInt(200), duration: Utils.fixedInt(200),
from: 0, from: 0,
to: 1, to: 1,
@ -70,4 +69,12 @@ export default class PokemonIconAnimHandler {
this.icons.delete(i); 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 BattleScene from "../battle-scene";
import { Stat, getStatName } from "../data/pokemon-stat"; 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 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 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); const defaultIvChartData = new Array(12).fill(null).map(() => 0);
export class StatsContainer extends Phaser.GameObjects.Container { export class StatsContainer extends Phaser.GameObjects.Container {
private showDiff: boolean;
private statsIvsCache: integer[]; private statsIvsCache: integer[];
private ivChart: Phaser.GameObjects.Polygon; 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); super(scene, x, y);
this.showDiff = !!showDiff;
this.setup(); 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); 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); 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.ivStatValueTexts[i].setOrigin(0.5)
this.add(statLabel); 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) { 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 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; const lastIvChartData = this.statsIvsCache || defaultIvChartData;
this.statsIvsCache = ivChartData.slice(0); 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({ this.scene.tweens.addCounter({
from: 0, from: 0,

View File

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

View File

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