Add spliced endless mode and splice indicator

pull/2/head
Flashfyre 2023-11-04 19:46:48 -04:00
parent e07339e3eb
commit b85bb0916d
16 changed files with 152 additions and 34 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 B

View File

@ -33,7 +33,7 @@ export class Arena {
randomSpecies(waveIndex: integer, level: integer, attempt?: integer): PokemonSpecies {
const isBoss = waveIndex % 10 === 0 && !!this.pokemonPool[BiomePoolTier.BOSS].length
&& (this.biomeType !== Biome.END || this.scene.gameMode !== GameMode.ENDLESS || waveIndex % 250 === 0);
&& (this.biomeType !== Biome.END || this.scene.gameMode === GameMode.CLASSIC || waveIndex % 250 === 0);
const tierValue = Utils.randSeedInt(!isBoss ? 512 : 64);
let tier = !isBoss
? tierValue >= 156 ? BiomePoolTier.COMMON : tierValue >= 32 ? BiomePoolTier.UNCOMMON : tierValue >= 6 ? BiomePoolTier.RARE : tierValue >= 1 ? BiomePoolTier.SUPER_RARE : BiomePoolTier.ULTRA_RARE
@ -103,7 +103,7 @@ export class Arena {
randomTrainerType(waveIndex: integer): TrainerType {
const isBoss = waveIndex > 20 && !(waveIndex % 30) && !!this.trainerPool[BiomePoolTier.BOSS].length
&& (this.biomeType !== Biome.END || this.scene.gameMode !== GameMode.ENDLESS || waveIndex % 250 === 0);
&& (this.biomeType !== Biome.END || this.scene.gameMode === GameMode.CLASSIC || waveIndex % 250 === 0);
const tierValue = Utils.randSeedInt(!isBoss ? 512 : 64);
let tier = !isBoss
? tierValue >= 156 ? BiomePoolTier.COMMON : tierValue >= 32 ? BiomePoolTier.UNCOMMON : tierValue >= 6 ? BiomePoolTier.RARE : tierValue >= 1 ? BiomePoolTier.SUPER_RARE : BiomePoolTier.ULTRA_RARE

View File

@ -472,7 +472,7 @@ export class SelectBiomePhase extends BattlePhase {
if (this.scene.gameMode === GameMode.CLASSIC && this.scene.currentBattle.waveIndex === this.scene.finalWave - 9)
setNextBiome(Biome.END);
else if (this.scene.gameMode === GameMode.ENDLESS) {
else if (this.scene.gameMode !== GameMode.CLASSIC) {
if (this.scene.currentBattle.waveIndex % 50 === 0)
setNextBiome(Biome.END);
else {
@ -2152,10 +2152,10 @@ export class VictoryPhase extends PokemonPhase {
this.scene.pushPhase(new BattleEndPhase(this.scene));
if (this.scene.currentBattle.battleType === BattleType.TRAINER)
this.scene.pushPhase(new TrainerVictoryPhase(this.scene));
if (this.scene.gameMode === GameMode.ENDLESS || 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 > 30 || this.scene.currentBattle.waveIndex % 10) {
this.scene.pushPhase(new SelectModifierPhase(this.scene));
if (this.scene.gameMode === GameMode.ENDLESS && !(this.scene.currentBattle.waveIndex % 50))
if (this.scene.gameMode !== GameMode.CLASSIC && !(this.scene.currentBattle.waveIndex % 50))
this.scene.pushPhase(new SelectEnemyBuffModifierPhase(this.scene));
} else
this.scene.pushPhase(new ModifierRewardPhase(this.scene, modifierTypes.GOLDEN_EXP_CHARM))
@ -2259,6 +2259,8 @@ export class GameOverPhase extends BattlePhase {
if (this.victory) {
if (!this.scene.gameData.unlocks[Unlockables.ENDLESS_MODE])
this.scene.unshiftPhase(new UnlockPhase(this.scene, Unlockables.ENDLESS_MODE));
if (this.scene.getParty().filter(p => p.fusionSpecies).length && !this.scene.gameData.unlocks[Unlockables.SPLICED_ENDLESS_MODE])
this.scene.unshiftPhase(new UnlockPhase(this.scene, Unlockables.SPLICED_ENDLESS_MODE));
if (!this.scene.gameData.unlocks[Unlockables.MINI_BLACK_HOLE])
this.scene.unshiftPhase(new UnlockPhase(this.scene, Unlockables.MINI_BLACK_HOLE));
}

View File

@ -197,6 +197,7 @@ export default class BattleScene extends Phaser.Scene {
this.loadImage('ability_bar', 'ui');
this.loadImage('party_exp_bar', 'ui');
this.loadImage('shiny_star', 'ui', 'shiny.png');
this.loadImage('icon_spliced', 'ui');
this.loadImage('pb_tray_overlay_player', 'ui');
this.loadImage('pb_tray_overlay_enemy', 'ui');
@ -617,7 +618,7 @@ export default class BattleScene extends Phaser.Scene {
if (newTrainer)
this.field.add(newTrainer);
} else {
if (this.gameMode === GameMode.ENDLESS)
if (this.gameMode !== GameMode.CLASSIC)
newBattleType = BattleType.WILD;
else if (battleType === undefined) {
if (newWaveIndex > 20 && !(newWaveIndex % 30))
@ -918,7 +919,7 @@ export default class BattleScene extends Phaser.Scene {
playBgm(bgmName?: string, fadeOut?: boolean): void {
if (bgmName === undefined)
bgmName = this.currentBattle.getBgmOverride() || this.arena.bgm;
bgmName = this.currentBattle.getBgmOverride(this) || this.arena.bgm;
if (this.bgm && bgmName === this.bgm.key) {
if (!this.bgm.isPlaying) {
this.bgm.play({

View File

@ -1,11 +1,12 @@
import BattleScene, { PokeballCounts } from "./battle-scene";
import BattleScene from "./battle-scene";
import { EnemyPokemon, PlayerPokemon, QueuedMove } from "./pokemon";
import { Command } from "./ui/command-ui-handler";
import * as Utils from "./utils";
import Trainer from "./trainer";
import { Species } from "./data/species";
import { Moves } from "./data/move";
import { TrainerConfig, TrainerType } from "./data/trainer-type";
import { TrainerType } from "./data/trainer-type";
import { GameMode } from "./game-mode";
export enum BattleType {
WILD,
@ -94,7 +95,7 @@ export default class Battle {
this.playerParticipantIds.delete(playerPokemon.id);
}
getBgmOverride(): string {
getBgmOverride(scene: BattleScene): string {
const battlers = this.enemyParty.slice(0, this.getBattlerCount());
if (this.battleType === BattleType.TRAINER) {
if (!this.started && this.trainer.config.encounterBgm && this.trainer.config.encounterMessages.length)
@ -113,7 +114,7 @@ export default class Battle {
}
}
if (this.waveIndex <= 4)
if (scene.gameMode === GameMode.CLASSIC && this.waveIndex <= 4)
return 'battle_wild';
return null;

View File

@ -356,6 +356,21 @@ export default class PokemonSpecies extends PokemonSpeciesForm {
return prevolutionLevels;
}
getCompatibleFusionSpeciesFilter(): PokemonSpeciesFilter {
const hasEvolution = pokemonEvolutions.hasOwnProperty(this.speciesId);
const hasPrevolution = pokemonPrevolutions.hasOwnProperty(this.speciesId);
const pseudoLegendary = this.pseudoLegendary;
const legendary = this.legendary;
const mythical = this.mythical;
return species => {
return pokemonEvolutions.hasOwnProperty(species.speciesId) === hasEvolution
&& pokemonPrevolutions.hasOwnProperty(species.speciesId) === hasPrevolution
&& species.pseudoLegendary === pseudoLegendary
&& species.legendary === legendary
&& species.mythical === mythical;
};
}
getFormSpriteKey(formIndex?: integer) {
return this.forms?.length
? this.forms[formIndex || 0].formKey

View File

@ -1,4 +1,11 @@
export enum GameMode {
CLASSIC,
ENDLESS
}
ENDLESS,
SPLICED_ENDLESS
}
export const gameModeNames = {
[GameMode.CLASSIC]: 'Classic',
[GameMode.ENDLESS]: 'Endless',
[GameMode.SPLICED_ENDLESS]: 'Endless (Spliced)'
};

View File

@ -779,6 +779,7 @@ const modifierPool = {
new WeightedModifierType(modifierTypes.TM_GREAT, 2),
new WeightedModifierType(modifierTypes.EXP_SHARE, 1),
new WeightedModifierType(modifierTypes.BASE_STAT_BOOSTER, 3),
new WeightedModifierType(modifierTypes.REVERSE_DNA_SPLICERS, (party: Pokemon[]) => party[0].scene.gameMode === GameMode.SPLICED_ENDLESS && party.filter(p => p.fusionSpecies).length ? 4 : 0),
].map(m => { m.setTier(ModifierTier.GREAT); return m; }),
[ModifierTier.ULTRA]: [
new WeightedModifierType(modifierTypes.ULTRA_BALL, 8),
@ -799,12 +800,13 @@ const modifierPool = {
new WeightedModifierType(modifierTypes.OVAL_CHARM, 2),
new WeightedModifierType(modifierTypes.ABILITY_CHARM, 2),
new WeightedModifierType(modifierTypes.EXP_BALANCE, 1),
new WeightedModifierType(modifierTypes.REVERSE_DNA_SPLICERS, (party: Pokemon[]) => party.filter(p => p.fusionSpecies).length > 1 ? 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),
new WeightedModifierType(modifierTypes.DNA_SPLICERS, (party: Pokemon[]) => party[0].scene.gameMode === GameMode.SPLICED_ENDLESS && party.filter(p => !p.fusionSpecies).length > 1 ? 6 : 0),
].map(m => { m.setTier(ModifierTier.ULTRA); return m; }),
[ModifierTier.MASTER]: [
new WeightedModifierType(modifierTypes.MASTER_BALL, 3),
new WeightedModifierType(modifierTypes.SHINY_CHARM, 2),
new WeightedModifierType(modifierTypes.DNA_SPLICERS, (party: Pokemon[]) => party.filter(p => !p.fusionSpecies).length > 1 ? 1 : 0),
new WeightedModifierType(modifierTypes.DNA_SPLICERS, (party: Pokemon[]) => party[0].scene.gameMode !== GameMode.SPLICED_ENDLESS && party.filter(p => !p.fusionSpecies).length > 1 ? 1 : 0),
new WeightedModifierType(modifierTypes.MINI_BLACK_HOLE, (party: Pokemon[]) => party[0].scene.gameData.unlocks[Unlockables.MINI_BLACK_HOLE] ? 1 : 0),
].map(m => { m.setTier(ModifierTier.MASTER); return m; }),
[ModifierTier.LUXURY]: [

View File

@ -28,6 +28,7 @@ import { BattlerIndex } from './battle';
import { Mode } from './ui/ui';
import PartyUiHandler, { PartyOption, PartyUiMode } from './ui/party-ui-handler';
import SoundFade from 'phaser3-rex-plugins/plugins/soundfade';
import { GameMode } from './game-mode';
export enum FieldPosition {
CENTER,
@ -148,6 +149,23 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
this.winCount = 0;
this.pokerus = false;
if (scene.gameMode === GameMode.SPLICED_ENDLESS) {
this.fusionSpecies = scene.randomSpecies(scene.currentBattle?.waveIndex || 0, level, this.species.getCompatibleFusionSpeciesFilter(), false);
this.fusionAbilityIndex = (this.fusionSpecies.abilityHidden && hasHiddenAbility ? this.fusionSpecies.ability2 ? 2 : 1 : this.fusionSpecies.ability2 ? randAbilityIndex : 0);
this.fusionFormIndex = scene.getSpeciesFormIndex(this.fusionSpecies);
this.fusionShiny = this.shiny;
if (this.getFusionSpeciesForm().malePercent === null)
this.fusionGender = Gender.GENDERLESS;
else {
const genderChance = (this.id % 256) * 0.390625;
if (genderChance < this.getFusionSpeciesForm().malePercent)
this.fusionGender = Gender.MALE;
else
this.fusionGender = Gender.FEMALE;
}
}
}
if (!species.isObtainable())

View File

@ -87,7 +87,8 @@ export class GameData {
this.secretId = Utils.randSeedInt(65536);
this.unlocks = {
[Unlockables.ENDLESS_MODE]: false,
[Unlockables.MINI_BLACK_HOLE]: false
[Unlockables.MINI_BLACK_HOLE]: false,
[Unlockables.SPLICED_ENDLESS_MODE]: false
};
this.initDexData();
this.loadSystem();

View File

@ -1,13 +1,18 @@
import { GameMode, gameModeNames } from "../game-mode";
export enum Unlockables {
ENDLESS_MODE,
MINI_BLACK_HOLE
MINI_BLACK_HOLE,
SPLICED_ENDLESS_MODE
}
export function getUnlockableName(unlockable: Unlockables) {
switch (unlockable) {
case Unlockables.ENDLESS_MODE:
return 'Endless Mode';
return gameModeNames[GameMode.ENDLESS];
case Unlockables.MINI_BLACK_HOLE:
return 'Mini Black Hole';
case Unlockables.SPLICED_ENDLESS_MODE:
return gameModeNames[GameMode.SPLICED_ENDLESS];
}
}

View File

@ -24,6 +24,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
private nameText: Phaser.GameObjects.Text;
private genderText: Phaser.GameObjects.Text;
private ownedIcon: Phaser.GameObjects.Sprite;
private splicedIcon: Phaser.GameObjects.Sprite;
private statusIndicator: Phaser.GameObjects.Sprite;
private levelContainer: Phaser.GameObjects.Container;
private hpBar: Phaser.GameObjects.Image;
@ -69,6 +70,13 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
this.add(this.ownedIcon);
}
this.splicedIcon = this.scene.add.sprite(0, 0, 'icon_spliced');
this.splicedIcon.setVisible(false);
this.splicedIcon.setOrigin(0, 0);
this.splicedIcon.setPositionRelative(this.nameText, 0, 2);
this.splicedIcon.setInteractive(new Phaser.Geom.Rectangle(0, 0, 5, 7), Phaser.Geom.Rectangle.Contains);
this.add(this.splicedIcon);
this.statusIndicator = this.scene.add.sprite(0, 0, 'statuses');
this.statusIndicator.setVisible(false);
this.statusIndicator.setOrigin(0, 0);
@ -113,6 +121,13 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
this.genderText.setColor(getGenderColor(pokemon.gender));
this.genderText.setPositionRelative(this.nameText, nameTextWidth, 0);
this.splicedIcon.setPositionRelative(this.nameText, nameTextWidth + this.genderText.displayWidth + 1, 1);
this.splicedIcon.setVisible(!!pokemon.fusionSpecies);
if (this.splicedIcon.visible) {
this.splicedIcon.on('pointerover', () => (this.scene as BattleScene).ui.showTooltip(null, `Spliced with ${pokemon.fusionSpecies.name}`));
this.splicedIcon.on('pointerout', () => (this.scene as BattleScene).ui.hideTooltip());
}
if (!this.player) {
const speciesOwned = !!pokemon.scene.gameData.getDefaultDexEntry(pokemon.species)?.entry?.caught;
this.ownedIcon.setVisible(speciesOwned);

View File

@ -1,4 +1,6 @@
import BattleScene from "../battle-scene";
import BattleScene, { Button } from "../battle-scene";
import { GameMode, gameModeNames } from "../game-mode";
import { Unlockables } from "../system/unlockables";
import OptionSelectUiHandler from "./option-select-ui-handler";
import { Mode } from "./ui";
@ -9,14 +11,53 @@ export default class GameModeSelectUiHandler extends OptionSelectUiHandler {
}
getWindowWidth(): integer {
return 64;
return 104;
}
getWindowHeight(): number {
return 64;
return (this.getOptions().length + 1) * 16;
}
getOptions(): string[] {
return [ 'Classic', 'Endless', 'Cancel' ];
const ret = [ gameModeNames[GameMode.CLASSIC] ];
if (this.scene.gameData.unlocks[Unlockables.ENDLESS_MODE]) {
ret.push(gameModeNames[GameMode.ENDLESS]);
if (this.scene.gameData.unlocks[Unlockables.SPLICED_ENDLESS_MODE])
ret.push(gameModeNames[GameMode.SPLICED_ENDLESS]);
}
ret.push('Cancel');
return ret;
}
show(args: any[]) {
if (args.length === 2 && args[0] instanceof Function && args[1] instanceof Function) {
this.setupOptions();
super.show(args);
this.handlers = args as Function[];
this.optionSelectContainer.setVisible(true);
this.setCursor(0);
}
}
processInput(button: Button) {
const ui = this.getUi();
const options = this.getOptions();
if (button === Button.ACTION || button === Button.CANCEL) {
if (button === Button.CANCEL)
this.setCursor(options.length - 1);
if (this.cursor < options.length - 1) {
const gameMode = Object.values(gameModeNames).indexOf(options[this.cursor]) as GameMode;
this.handlers[0](gameMode);
} else
this.handlers[1]();
this.clear();
ui.playSelect();
} else
return super.processInput(button);
}
}

View File

@ -8,6 +8,7 @@ export default abstract class OptionSelectUiHandler extends UiHandler {
protected optionSelectContainer: Phaser.GameObjects.Container;
protected optionSelectBg: Phaser.GameObjects.NineSlice;
protected optionSelectText: Phaser.GameObjects.Text;
private cursorObj: Phaser.GameObjects.Image;
@ -24,22 +25,31 @@ export default abstract class OptionSelectUiHandler extends UiHandler {
setup() {
const ui = this.getUi();
this.optionSelectContainer = this.scene.add.container((this.scene.game.canvas.width / 6) - (this.getWindowWidth() + 1), -(this.getWindowWidth() + 1));
this.optionSelectContainer = this.scene.add.container((this.scene.game.canvas.width / 6) - 1, -48);
this.optionSelectContainer.setVisible(false);
ui.add(this.optionSelectContainer);
this.optionSelectBg = this.scene.add.nineslice(0, 0, 'window', null, this.getWindowWidth(), this.getWindowHeight(), 6, 6, 6, 6);
this.optionSelectBg.setOrigin(0, 1);
this.optionSelectBg.setOrigin(1, 1);
this.optionSelectContainer.add(this.optionSelectBg);
this.setupOptions();
this.setCursor(0);
}
protected setupOptions() {
const options = this.getOptions();
const optionSelectText = addTextObject(this.scene, 0, 0, options.join('\n'), TextStyle.WINDOW, { maxLines: options.length });
optionSelectText.setPositionRelative(this.optionSelectBg, 16, 9);
optionSelectText.setLineSpacing(12);
this.optionSelectContainer.add(optionSelectText);
if (this.optionSelectText)
this.optionSelectText.destroy();
this.setCursor(0);
this.optionSelectText = addTextObject(this.scene, 0, 0, options.join('\n'), TextStyle.WINDOW, { maxLines: options.length });
this.optionSelectText.setPositionRelative(this.optionSelectBg, 16, 9);
this.optionSelectText.setLineSpacing(12);
this.optionSelectContainer.add(this.optionSelectText);
this.optionSelectBg.width = Math.max(this.optionSelectText.displayWidth + 24, this.getWindowWidth());
this.optionSelectBg.height = this.getWindowHeight();
}
show(args: any[]) {

View File

@ -371,9 +371,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
};
if (this.scene.gameData.unlocks[Unlockables.ENDLESS_MODE]) {
ui.setMode(Mode.STARTER_SELECT);
ui.showText('Select a game mode.', null, () => {
ui.setModeWithoutClear(Mode.GAME_MODE_SELECT, () => startRun(GameMode.CLASSIC), () => startRun(GameMode.ENDLESS), cancel);
});
ui.showText('Select a game mode.', null, () => ui.setModeWithoutClear(Mode.GAME_MODE_SELECT, startRun, cancel));
} else
startRun(GameMode.CLASSIC);
}, cancel);

View File

@ -146,10 +146,12 @@ export default class UI extends Phaser.GameObjects.Container {
showTooltip(title: string, content: string, overlap?: boolean): void {
this.tooltipContainer.setVisible(true);
this.tooltipTitle.setText(title);
this.tooltipTitle.setText(title || '');
const wrappedContent = this.tooltipContent.runWordWrap(content);
this.tooltipContent.setText(wrappedContent);
this.tooltipBg.height = 31 + 10.5 * (wrappedContent.split('\n').length - 1);
this.tooltipContent.y = title ? 16 : 4;
this.tooltipBg.width = Math.min(Math.max(this.tooltipTitle.displayWidth, this.tooltipContent.displayWidth) + 12, 684);
this.tooltipBg.height = (title ? 31 : 19) + 10.5 * (wrappedContent.split('\n').length - 1);
if (overlap)
(this.scene as BattleScene).uiContainer.moveAbove(this.tooltipContainer, this);
else