diff --git a/README.md b/README.md index 42eaddb56a2..59210f859a9 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,6 @@ - Get starters from save data caught Pokemon - Moves - Move logic - - Can't use when PP consumed - Abilities - Ability logic - Ability activation indicator (?) @@ -26,7 +25,6 @@ - Status effect indicator - Modifiers - PP Up - - Ether/elixir - Type enhancers - Evolution items - Various mainline game items for various enhancements @@ -38,6 +36,5 @@ - Custom art - Battle bases and backgrounds - Game sequence - - Biome sequence (random?) - Endgame? - Endless mode? diff --git a/public/images/ui/biome_select_window_2.png b/public/images/ui/biome_select_window_2.png new file mode 100644 index 00000000000..f3c66d1286a Binary files /dev/null and b/public/images/ui/biome_select_window_2.png differ diff --git a/public/images/ui/biome_select_window_3.png b/public/images/ui/biome_select_window_3.png new file mode 100644 index 00000000000..e8dc0459e67 Binary files /dev/null and b/public/images/ui/biome_select_window_3.png differ diff --git a/src/battle-phases.ts b/src/battle-phases.ts index 3c13ca67ce0..1e0a75c2afa 100644 --- a/src/battle-phases.ts +++ b/src/battle-phases.ts @@ -15,6 +15,7 @@ import EvolutionSceneHandler from "./ui/evolution-scene-handler"; import { EvolutionPhase } from "./evolution-phase"; import { BattlePhase } from "./battle-phase"; import { BattleStat, getBattleStatLevelChangeDescription, getBattleStatName } from "./battle-stat"; +import { Biome, biomeLinks } from "./biome"; export class SelectStarterPhase extends BattlePhase { constructor(scene: BattleScene) { @@ -132,7 +133,7 @@ export class NewBiomeEncounterPhase extends NextEncounterPhase { } } -export class SwitchBiomePhase extends BattlePhase { +export class SelectBiomePhase extends BattlePhase { constructor(scene: BattleScene) { super(scene); } @@ -142,6 +143,35 @@ export class SwitchBiomePhase extends BattlePhase { this.scene.arena.fadeOutBgm(2000); + const currentBiome = this.scene.arena.biomeType; + + const setNextBiome = (nextBiome: Biome) => { + this.scene.unshiftPhase(new SwitchBiomePhase(this.scene, nextBiome)); + this.end(); + }; + + if (Array.isArray(biomeLinks[currentBiome])) + this.scene.ui.setMode(Mode.BIOME_SELECT, currentBiome, (biomeIndex: integer) => { + this.scene.ui.setMode(Mode.MESSAGE); + setNextBiome((biomeLinks[currentBiome] as Biome[])[biomeIndex]); + }); + else + setNextBiome(biomeLinks[currentBiome] as Biome) + } +} + +export class SwitchBiomePhase extends BattlePhase { + private nextBiome: Biome; + + constructor(scene: BattleScene, nextBiome: Biome) { + super(scene); + + this.nextBiome = nextBiome; + } + + start() { + super.start(); + this.scene.tweens.add({ targets: this.scene.arenaEnemy, x: '+=300', @@ -149,7 +179,7 @@ export class SwitchBiomePhase extends BattlePhase { onComplete: () => { this.scene.arenaEnemy.setX(this.scene.arenaEnemy.x - 600); - this.scene.newBiome(); + this.scene.newBiome(this.nextBiome); const biomeKey = this.scene.arena.getBiomeKey(); const bgTexture = `${biomeKey}_bg`; @@ -380,6 +410,7 @@ export class CommandPhase extends BattlePhase { success = true; break; } + if (success) { const enemyMove = enemyPokemon.getNextMove(); const enemyPhase = new EnemyMovePhase(this.scene, enemyPokemon, enemyMove); diff --git a/src/battle-scene.ts b/src/battle-scene.ts index ad082d78802..b6dde2d7905 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -1,7 +1,7 @@ import Phaser from 'phaser'; import { Biome, BiomeArena } from './biome'; import UI from './ui/ui'; -import { EncounterPhase, SummonPhase, CommandPhase, NextEncounterPhase, SwitchBiomePhase, NewBiomeEncounterPhase } from './battle-phases'; +import { EncounterPhase, SummonPhase, CommandPhase, NextEncounterPhase, SwitchBiomePhase, NewBiomeEncounterPhase, SelectBiomePhase } from './battle-phases'; import { PlayerPokemon, EnemyPokemon } from './pokemon'; import PokemonSpecies, { allSpecies, getPokemonSpecies } from './pokemon-species'; import * as Utils from './utils'; @@ -153,6 +153,9 @@ export default class BattleScene extends Phaser.Scene { this.loadImage('summary_moves_overlay_pp', 'ui'); this.loadAtlas('summary_moves_cursor', 'ui'); + this.loadImage('biome_select_window_2', 'ui'); + this.loadImage('biome_select_window_3', 'ui'); + // Load arena images Utils.getEnumValues(Biome).map(at => { const atKey = Biome[at].toLowerCase(); @@ -238,25 +241,6 @@ export default class BattleScene extends Phaser.Scene { this.field = field; - this.newBiome(); - - const biomeKey = this.arena.getBiomeKey(); - this.arenaBg = this.add.sprite(0, 0, `${biomeKey}_bg`); - this.arenaBgTransition = this.add.sprite(0, 0, `${biomeKey}_bg`); - this.arenaPlayer = this.add.sprite(340, 20, `${biomeKey}_a`); - this.arenaPlayerTransition = this.add.sprite(40, 20, `${biomeKey}_a`); - this.arenaEnemy = this.add.sprite(-240, 13, `${biomeKey}_b`); - this.arenaNextEnemy = this.add.sprite(-240, 13, `${biomeKey}_b`); - - this.arenaBgTransition.setVisible(false); - this.arenaPlayerTransition.setVisible(false); - - [this.arenaBg, this.arenaBgTransition, this.arenaPlayer, this.arenaPlayerTransition, this.arenaEnemy, this.arenaNextEnemy].forEach(a => { - a.setOrigin(0, 0); - field.add(a); - }); - this.arena.playBgm(); - const fieldUI = this.add.container(0, this.game.canvas.height); fieldUI.setDepth(1); fieldUI.setScale(6); @@ -281,6 +265,29 @@ export default class BattleScene extends Phaser.Scene { const isRandom = this.isButtonPressed(Button.RANDOM); // For testing purposes + if (isRandom) { + const biomes = Utils.getEnumValues(Biome); + this.newBiome(biomes[Utils.randInt(biomes.length)]); + } else + this.newBiome(Biome.PLAINS); + + const biomeKey = this.arena.getBiomeKey(); + this.arenaBg = this.add.sprite(0, 0, `${biomeKey}_bg`); + this.arenaBgTransition = this.add.sprite(0, 0, `${biomeKey}_bg`); + this.arenaPlayer = this.add.sprite(340, 20, `${biomeKey}_a`); + this.arenaPlayerTransition = this.add.sprite(40, 20, `${biomeKey}_a`); + this.arenaEnemy = this.add.sprite(-240, 13, `${biomeKey}_b`); + this.arenaNextEnemy = this.add.sprite(-240, 13, `${biomeKey}_b`); + + this.arenaBgTransition.setVisible(false); + this.arenaPlayerTransition.setVisible(false); + + [this.arenaBg, this.arenaBgTransition, this.arenaPlayer, this.arenaPlayerTransition, this.arenaEnemy, this.arenaNextEnemy].forEach(a => { + a.setOrigin(0, 0); + field.add(a); + }); + this.arena.playBgm(); + for (let s = 0; s < 3; s++) { const playerSpecies = !isRandom ? getPokemonSpecies(s === 0 ? Species.TORCHIC : s === 1 ? Species.TREECKO : Species.MUDKIP) : this.randomSpecies(5); const playerPokemon = new PlayerPokemon(this, playerSpecies, 5); @@ -378,7 +385,7 @@ export default class BattleScene extends Phaser.Scene { if (this.currentBattle.waveIndex % 10) this.unshiftPhase(new NextEncounterPhase(this)); else { - this.unshiftPhase(new SwitchBiomePhase(this)); + this.unshiftPhase(new SelectBiomePhase(this)); this.unshiftPhase(new NewBiomeEncounterPhase(this)); } } else { @@ -391,8 +398,7 @@ export default class BattleScene extends Phaser.Scene { return this.currentBattle; } - newBiome(): BiomeArena { - const biome = this.currentBattle ? Utils.randInt(20) as Biome : Biome.PLAINS; + newBiome(biome: Biome): BiomeArena { this.arena = new BiomeArena(this, biome, Biome[biome].toLowerCase()); return this.arena; } diff --git a/src/biome.ts b/src/biome.ts index 6ff01607f4f..64aa741e96e 100644 --- a/src/biome.ts +++ b/src/biome.ts @@ -1,7 +1,7 @@ import BattleScene from "./battle-scene"; import SoundFade from "phaser3-rex-plugins/plugins/soundfade.js"; import { pokemonEvolutions, SpeciesEvolution } from "./pokemon-evolutions"; -import { default as PokemonSpecies, allSpecies, getPokemonSpecies } from "./pokemon-species"; +import { default as PokemonSpecies, getPokemonSpecies } from "./pokemon-species"; import { Species } from "./species"; import { Type } from './type'; import * as Utils from './utils'; @@ -35,6 +35,54 @@ export enum Biome { SPACE }; +export function getBiomeName(biome: Biome) { + switch (biome) { + case Biome.PLAINS: + return 'STARTER PLAINS'; + case Biome.GRASS: + return 'GRASSY FIELD'; + case Biome.RUINS: + return 'ANCIENT RUINS'; + case Biome.ABYSS: + return 'THE ABYSS'; + case Biome.SPACE: + return 'STRATOSPHERE'; + default: + return Biome[biome].replace(/\_/g, ' '); + } +} + +interface BiomeLinks { + [key: integer]: Biome | Biome[] +} + +export const biomeLinks: BiomeLinks = { + [Biome.PLAINS]: Biome.GRASS, + [Biome.GRASS]: [ Biome.TALL_GRASS, Biome.CITY, Biome.LAKE ], + [Biome.TALL_GRASS]: [ Biome.FOREST, Biome.CAVE ], + [Biome.CITY]: [ Biome.DOJO, Biome.POWER_PLANT ], + [Biome.FOREST]: Biome.MEADOW, + [Biome.SEA]: [ Biome.SEABED, Biome.ICE_CAVE ], + [Biome.SWAMP]: [ Biome.GRAVEYARD, Biome.TALL_GRASS ], + [Biome.BEACH]: Biome.SEA, + [Biome.LAKE]: [ Biome.BEACH, Biome.SWAMP ], + [Biome.SEABED]: Biome.CAVE, + [Biome.MOUNTAIN]: [ Biome.WASTELAND, Biome.VOLCANO ], + [Biome.LAND]: [ Biome.DESERT, Biome.MOUNTAIN ], + [Biome.CAVE]: [ Biome.LAND, Biome.BEACH ], + [Biome.DESERT]: Biome.RUINS, + [Biome.ICE_CAVE]: Biome.LAKE, + [Biome.MEADOW]: Biome.GRASS, + [Biome.POWER_PLANT]: Biome.GRASS, + [Biome.VOLCANO]: Biome.ICE_CAVE, + [Biome.GRAVEYARD]: Biome.ABYSS, + [Biome.DOJO]: Biome.GRASS, + [Biome.RUINS]: Biome.FOREST, + [Biome.WASTELAND]: Biome.LAND, + [Biome.ABYSS]: Biome.SPACE, + [Biome.SPACE]: Biome.RUINS +}; + enum PoolTier { COMMON, UNCOMMON, @@ -47,12 +95,24 @@ enum PoolTier { BOSS_ULTRA_RARE }; +interface SpeciesTree { + [key: integer]: Species[] +} + +interface BiomeTierPools { + [key: integer]: Array +} + +interface BiomePools { + [key: integer]: BiomeTierPools +} + export class BiomeArena { private scene: BattleScene; public biomeType: integer; private bgm: string; - private pokemonPool: PokemonSpecies[][]; + private pokemonPool: BiomeTierPools; constructor(scene: BattleScene, biome: integer, bgm: string) { this.scene = scene; @@ -140,7 +200,7 @@ export class BiomeArena { } } -const biomePools = { +const biomePools: BiomePools = { [Biome.PLAINS]: { [PoolTier.COMMON]: [ { 1: [ Species.CATERPIE ], 7: [ Species.METAPOD ] }, @@ -3805,8 +3865,10 @@ const biomePools = { let treeIndex = -1; let arrayIndex = 0; + // Not sure why this works but let's just leave it alone until we need it again + for (let t = 0; t < biomeTierPool.length; t++) { - const existingSpeciesIds = biomeTierPool[t] as Species[]; + const existingSpeciesIds = biomeTierPool[t] as SpeciesTree; for (let es = 0; es < existingSpeciesIds.length; es++) { const existingSpeciesId = existingSpeciesIds[es]; if (pokemonEvolutions.hasOwnProperty(existingSpeciesId) && (pokemonEvolutions[existingSpeciesId] as SpeciesEvolution[]).find(ese => ese.speciesId === speciesId)) { diff --git a/src/modifier.ts b/src/modifier.ts index 131ba23b9dc..bf251880fce 100644 --- a/src/modifier.ts +++ b/src/modifier.ts @@ -766,7 +766,7 @@ const modifierPool = { const thresholdPartyMemberCount = party.filter(p => p.moveset.filter(m => m.ppUsed)).length; return thresholdPartyMemberCount; }), - new WeightedModifierType(new PokemonPpRestoreModifierType('MAX ETHER', 100), (party: PlayerPokemon[]) => { + new WeightedModifierType(new PokemonPpRestoreModifierType('MAX ETHER', -1), (party: PlayerPokemon[]) => { const thresholdPartyMemberCount = party.filter(p => p.moveset.filter(m => m.ppUsed > 10)).length; return Math.ceil(thresholdPartyMemberCount / 3); }) @@ -793,7 +793,7 @@ const modifierPool = { const thresholdPartyMemberCount = party.filter(p => p.moveset.filter(m => m.ppUsed)).length; return thresholdPartyMemberCount; }), - new WeightedModifierType(new PokemonAllMovePpRestoreModifierType('MAX ELIXIR', 100), (party: PlayerPokemon[]) => { + new WeightedModifierType(new PokemonAllMovePpRestoreModifierType('MAX ELIXIR', -1), (party: PlayerPokemon[]) => { const thresholdPartyMemberCount = party.filter(p => p.moveset.filter(m => m.ppUsed > 10)).length; return Math.ceil(thresholdPartyMemberCount / 3); }), @@ -863,16 +863,13 @@ export function regenerateModifierPoolThresholds(party: PlayerPokemon[]) { console.log(modifierPoolThresholds) } -export function getModifierTypesForWave(waveIndex: integer, count: integer, party: PlayerPokemon[]): Array { +export function getModifierTypesForWave(waveIndex: integer, count: integer, party: PlayerPokemon[]): ModifierType[] { if (waveIndex % 10 === 0) return modifierPool[ModifierTier.LUXURY]; - const ret = []; - for (let m = 0; m < count; m++) - ret.push(getNewModifierType(party)); - return ret; + return new Array(count).fill(0).map(() => getNewModifierType(party)); } -function getNewModifierType(party: PlayerPokemon[]) { +function getNewModifierType(party: PlayerPokemon[]): ModifierType { const tierValue = Utils.randInt(256); const tier = tierValue >= 52 ? ModifierTier.COMMON : tierValue >= 8 ? ModifierTier.GREAT : tierValue >= 1 ? ModifierTier.ULTRA : ModifierTier.MASTER; const thresholds = Object.keys(modifierPoolThresholds[tier]); @@ -892,5 +889,5 @@ function getNewModifierType(party: PlayerPokemon[]) { modifierType = (modifierType as WeightedModifierType).modifierType; if (modifierType instanceof ModifierTypeGenerator) modifierType = (modifierType as ModifierTypeGenerator).generateType(party); - return modifierType; + return modifierType as ModifierType; } \ No newline at end of file diff --git a/src/ui/biome-select-ui-handler.ts b/src/ui/biome-select-ui-handler.ts new file mode 100644 index 00000000000..ff513a1bc8c --- /dev/null +++ b/src/ui/biome-select-ui-handler.ts @@ -0,0 +1,108 @@ +import BattleScene, { Button } from "../battle-scene"; +import { Biome, biomeLinks, getBiomeName } from "../biome"; +import { addTextObject, TextStyle } from "../text"; +import { Mode } from "./ui"; +import UiHandler from "./uiHandler"; + +export default class BiomeSelectUiHandler extends UiHandler { + private biomeSelectContainer: Phaser.GameObjects.Container; + private biomeSelectBg: Phaser.GameObjects.Image; + private biomesText: Phaser.GameObjects.Text; + private biomeChoices: Biome[]; + + private cursorObj: Phaser.GameObjects.Image; + + private biomeSelectHandler: Function; + + constructor(scene: BattleScene) { + super(scene, Mode.BIOME_SELECT); + } + + setup() { + const ui = this.getUi(); + + this.biomeSelectContainer = this.scene.add.container((this.scene.game.canvas.width / 6) - 97, -49); + this.biomeSelectContainer.setVisible(false); + ui.add(this.biomeSelectContainer); + + this.biomeSelectBg = this.scene.add.image(0, 0, 'biome_select_window_2'); + this.biomeSelectBg.setOrigin(0, 1); + this.biomeSelectContainer.add(this.biomeSelectBg); + + this.biomesText = addTextObject(this.scene, 0, 0, '', TextStyle.WINDOW, { maxLines: 3 }); + this.biomesText.setLineSpacing(12); + this.biomeSelectContainer.add(this.biomesText); + } + + show(args: any[]) { + if (args.length >= 2 && typeof(args[0]) === 'number' && args[1] instanceof Function) { + super.show(args); + + if (!Array.isArray(biomeLinks[args[0]])) + return; + + this.biomeChoices = biomeLinks[args[0]] as Biome[]; + this.biomeSelectBg.setTexture(`biome_select_window_${this.biomeChoices.length}`) + this.biomesText.setText(this.biomeChoices.map(b => getBiomeName(b)).join('\n')); + this.biomesText.setPositionRelative(this.biomeSelectBg, 16, 9); + this.biomeSelectHandler = args[1] as Function; + + this.biomeSelectContainer.setVisible(true); + this.setCursor(0); + } + } + + processInput(button: Button) { + const ui = this.getUi(); + + let success = false; + + if (button === Button.ACTION || button === Button.CANCEL) { + success = true; + const originalBiomeSelectHandler = this.biomeSelectHandler; + this.biomeSelectHandler = null; + originalBiomeSelectHandler(this.cursor); + this.clear(); + } else { + switch (button) { + case Button.UP: + if (this.cursor) + success = this.setCursor(this.cursor - 1); + break; + case Button.DOWN: + if (this.cursor < this.biomeChoices.length - 1) + success = this.setCursor(this.cursor + 1); + break; + } + } + + if (success) + ui.playSelect(); + } + + setCursor(cursor: integer): boolean { + const ret = super.setCursor(cursor); + + if (!this.cursorObj) { + this.cursorObj = this.scene.add.image(0, 0, 'cursor'); + this.biomeSelectContainer.add(this.cursorObj); + } + + this.cursorObj.setPositionRelative(this.biomeSelectBg, 12, 17 + 16 * this.cursor); + + return ret; + } + + clear() { + super.clear(); + this.biomeSelectContainer.setVisible(false); + this.biomeSelectHandler = null; + this.eraseCursor(); + } + + eraseCursor() { + if (this.cursorObj) + this.cursorObj.destroy(); + this.cursorObj = null; + } +} \ No newline at end of file diff --git a/src/ui/ui.ts b/src/ui/ui.ts index eaf57bf7271..9c466c57a6b 100644 --- a/src/ui/ui.ts +++ b/src/ui/ui.ts @@ -11,6 +11,7 @@ import BallUiHandler from './ball-ui-handler'; import SummaryUiHandler from './summary-ui-handler'; import StarterSelectUiHandler from './starter-select-ui-handler'; import EvolutionSceneHandler from './evolution-scene-handler'; +import BiomeSelectUiHandler from './biome-select-ui-handler'; export enum Mode { MESSAGE = 0, @@ -21,6 +22,7 @@ export enum Mode { PARTY, CONFIRM, SUMMARY, + BIOME_SELECT, STARTER_SELECT, EVOLUTION_SCENE }; @@ -56,6 +58,7 @@ export default class UI extends Phaser.GameObjects.Container { new PartyUiHandler(scene), new ConfirmUiHandler(scene), new SummaryUiHandler(scene), + new BiomeSelectUiHandler(scene), new StarterSelectUiHandler(scene), new EvolutionSceneHandler(scene) ];