Biome filter; displays for baseStats, biomes and evolutions

This commit is contained in:
Wlowscha 2025-01-03 03:02:12 +01:00
parent dcf3399e31
commit 8b173ba2a9
No known key found for this signature in database
GPG Key ID: 3C8F1AD330565D04
5 changed files with 306 additions and 17 deletions

View File

@ -101,6 +101,18 @@ export interface BiomePokemonPools {
[key: integer]: BiomeTierPokemonPools
}
export interface BiomeTierTod {
biome: Biome,
tier: BiomePoolTier,
tod: TimeOfDay[]
}
export interface CatchableSpecies{
[key: integer]: BiomeTierTod[]
}
export const catchableSpecies: CatchableSpecies = {};
export interface BiomeTierTrainerPools {
[key: integer]: TrainerType[]
}
@ -7715,6 +7727,10 @@ export function initBiomes() {
uncatchableSpecies.push(speciesId);
}
// prepares new array in catchableSpecies to host available biomes
//TODO: this must be improved to only make arrays for starters
catchableSpecies[speciesId] = [];
for (const b of biomeEntries) {
const biome = b[0];
const tier = b[1];
@ -7724,6 +7740,12 @@ export function initBiomes() {
: [ b[2] ]
: [ TimeOfDay.ALL ];
catchableSpecies[speciesId].push({
biome: biome,
tier: tier,
tod: timesOfDay
});
for (const tod of timesOfDay) {
if (!biomePokemonPools.hasOwnProperty(biome) || !biomePokemonPools[biome].hasOwnProperty(tier) || !biomePokemonPools[biome][tier].hasOwnProperty(tod)) {
continue;

View File

@ -2,6 +2,7 @@ import BattleScene from "#app/battle-scene";
import { SceneBase } from "#app/scene-base";
import { addTextObject, TextStyle } from "./text";
import { addWindow, WindowVariant } from "./ui-theme";
import { ScrollBar } from "#app/ui/scroll-bar";
import i18next from "i18next";
export enum DropDownState {
@ -282,21 +283,37 @@ export class DropDown extends Phaser.GameObjects.Container {
private onChange: () => void;
private lastDir: SortDirection = SortDirection.ASC;
private defaultSettings: any[];
private dropDownScrollBar: ScrollBar;
private totalOptions: number = 0;
private maxOptions: number = 0;
private shownOptions: number = 0;
private tooManyOptions: Boolean = false;
private firstShown: number = 0;
private optionHeight: number = 0;
private optionSpacing: number = 0;
private optionPaddingX: number = 4;
private optionPaddingY: number = 6;
private optionWidth: number = 100;
private cursorOffset: number = 0;
constructor(scene: BattleScene, x: number, y: number, options: DropDownOption[], onChange: () => void, type: DropDownType = DropDownType.MULTI, optionSpacing: number = 2) {
const windowPadding = 5;
const optionHeight = 7;
const optionPaddingX = 4;
const optionPaddingY = 6;
const cursorOffset = 7;
const optionWidth = 100;
super(scene, x - cursorOffset - windowPadding, y);
this.optionWidth = 100;
this.optionHeight = 7;
this.optionSpacing = optionSpacing;
this.optionPaddingX = 4;
this.optionPaddingY = 6;
this.cursorOffset = cursorOffset;
this.options = options;
this.dropDownType = type;
this.onChange = onChange;
this.cursorObj = scene.add.image(optionPaddingX + 3, 0, "cursor");
this.cursorObj = scene.add.image(this.optionPaddingX + 3, 0, "cursor");
this.cursorObj.setScale(0.5);
this.cursorObj.setOrigin(0, 0.5);
this.cursorObj.setVisible(false);
@ -306,31 +323,51 @@ export class DropDown extends Phaser.GameObjects.Container {
this.options.unshift(new DropDownOption(scene, "ALL", new DropDownLabel(i18next.t("filterBar:all"), undefined, this.checkForAllOn() ? DropDownState.ON : DropDownState.OFF)));
}
this.maxOptions = 19;
this.totalOptions = this.options.length;
this.tooManyOptions = this.totalOptions > this.maxOptions;
this.shownOptions = this.tooManyOptions ? this.maxOptions : this.totalOptions;
this.defaultSettings = this.getSettings();
// Place ui elements in the correct spot
options.forEach((option, index) => {
const toggleVisibility = type !== DropDownType.SINGLE || option.state === DropDownState.ON;
option.setupToggleIcon(type, toggleVisibility);
option.width = optionWidth;
option.y = index * optionHeight + index * optionSpacing + optionPaddingY;
option.width = this.optionWidth;
option.y = index * this.optionHeight + index * optionSpacing + this.optionPaddingY;
const baseX = cursorOffset + optionPaddingX + 3;
const baseY = optionHeight / 2;
const baseX = cursorOffset + this.optionPaddingX + 3;
const baseY = this.optionHeight / 2;
option.setLabelPosition(baseX + 8, baseY);
if (type === DropDownType.SINGLE) {
option.setTogglePosition(baseX + 3, baseY + 1);
} else {
option.setTogglePosition(baseX, baseY);
}
if (index >= this.shownOptions) {
option.visible = false;
}
this.firstShown = 0;
});
this.window = addWindow(scene, 0, 0, optionWidth, options[options.length - 1].y + optionHeight + optionPaddingY, false, false, undefined, undefined, WindowVariant.XTHIN);
this.window = addWindow(scene, 0, 0, this.optionWidth, options[this.shownOptions - 1].y + this.optionHeight + this.optionPaddingY, false, false, undefined, undefined, WindowVariant.XTHIN);
this.add(this.window);
this.add(options);
this.add(this.cursorObj);
this.setVisible(false);
if (this.tooManyOptions) {
// Setting the last parameter to 1 turns out to be optimal in all cases.
this.dropDownScrollBar = new ScrollBar(scene, this.window.width - 3, 5, 5, this.window.height - 10, 1);
this.add(this.dropDownScrollBar);
this.dropDownScrollBar.setTotalRows(this.totalOptions);
this.dropDownScrollBar.setScrollCursor(0);
}
}
getWidth(): number {
@ -360,6 +397,11 @@ export class DropDown extends Phaser.GameObjects.Container {
}
setCursor(cursor: integer): boolean {
if (this.tooManyOptions) {
this.setLabels(cursor);
}
this.cursor = cursor;
if (cursor < 0) {
cursor = 0;
@ -382,6 +424,41 @@ export class DropDown extends Phaser.GameObjects.Container {
return true;
}
setLabels(cursor: integer) {
if ((cursor === 0) && (this.lastCursor === this.totalOptions - 1)) {
this.firstShown = 0;
} else if ((cursor === this.totalOptions - 1) && (this.lastCursor === 0)) {
this.firstShown = this.totalOptions - this.shownOptions;
} else if ((cursor - this.firstShown >= this.shownOptions) && (cursor > this.lastCursor)) {
this.firstShown += 1;
} else if ((cursor < this.firstShown) && (cursor < this.lastCursor)) {
this.firstShown -= 1;
}
this.options.forEach((option, index) => {
option.y = (index - this.firstShown) * (this.optionHeight + this.optionSpacing) + this.optionPaddingY;
const baseX = this.cursorOffset + this.optionPaddingX + 3;
const baseY = this.optionHeight / 2;
option.setLabelPosition(baseX + 8, baseY);
if (this.dropDownType === DropDownType.SINGLE) {
option.setTogglePosition(baseX + 3, baseY + 1);
} else {
option.setTogglePosition(baseX, baseY);
}
if ((index < this.firstShown) || ( index >= this.firstShown + this.shownOptions)) {
option.visible = false;
} else {
option.visible = true;
}
});
this.dropDownScrollBar.setScrollCursor(cursor);
}
/**
* Switch the option at the provided index to its next state and update visuals
* Update accordingly the other options if needed:
@ -586,7 +663,12 @@ export class DropDown extends Phaser.GameObjects.Container {
x = this.options[i].getCurrentLabelX() ?? 0;
}
}
this.window.width = maxWidth + x - this.window.x + 6;
this.window.width = maxWidth + x - this.window.x + 9;
if (this.tooManyOptions) {
this.window.width += 6;
this.dropDownScrollBar.x = this.window.width - 9;
}
if (this.x + this.window.width > this.parentContainer.width) {
this.x = this.parentContainer.width - this.window.width;

View File

@ -8,6 +8,7 @@ import { addWindow, WindowVariant } from "./ui-theme";
export enum DropDownColumn {
GEN,
TYPES,
BIOME,
CAUGHT,
UNLOCKS,
MISC,
@ -96,7 +97,7 @@ export class FilterBar extends Phaser.GameObjects.Container {
* Position the filter dropdowns evenly across the width of the container
*/
private calcFilterPositions(): void {
const paddingX = 6;
const paddingX = 0;
const cursorOffset = 8;
let totalWidth = paddingX * 2 + cursorOffset;

View File

@ -1,4 +1,4 @@
import { pokemonPrevolutions, pokemonStarters } from "#app/data/balance/pokemon-evolutions";
import { EvolutionItem, pokemonEvolutions, pokemonPrevolutions, pokemonStarters, SpeciesFormEvolution } from "#app/data/balance/pokemon-evolutions";
import { Variant, getVariantTint, getVariantIcon } from "#app/data/variant";
import { argbFromRgba } from "@material/material-color-utilities";
import i18next from "i18next";
@ -44,6 +44,9 @@ import type { Nature } from "#enums/nature";
import BgmBar from "./bgm-bar";
import * as Utils from "../utils";
import { speciesTmMoves } from "#app/data/balance/tms";
import { BiomePoolTier, BiomeTierTod, catchableSpecies } from "#app/data/balance/biomes";
import { Biome } from "#app/enums/biome";
import { TimeOfDay } from "#app/enums/time-of-day";
export type StarterSelectCallback = (starters: Starter[]) => void;
@ -235,6 +238,10 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
private passive: Ability;
private hasPassive: boolean;
private hasAbilities: number[];
private biomes: BiomeTierTod[];
private baseStats: number[];
private baseTotal: number;
private evolutions: SpeciesFormEvolution[];
private speciesStarterDexEntry: DexEntry | null;
private speciesStarterMoves: Moves[];
@ -498,7 +505,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
// The font size should be set per language
const instructionTextSize = textSettings.instructionTextSize;
this.instructionsContainer = this.scene.add.container(4, 156);
this.instructionsContainer = this.scene.add.container(4, 128);
this.instructionsContainer.setVisible(true);
this.starterSelectContainer.add(this.instructionsContainer);
@ -724,7 +731,13 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
hasAbility2,
hasHiddenAbility
];
console.log(this.hasAbilities);
this.biomes = catchableSpecies[species.speciesId];
this.baseStats = species.baseStats;
this.baseTotal = species.baseTotal;
this.evolutions = pokemonEvolutions[species.speciesId];
}
/**
@ -1036,6 +1049,55 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
console.log("Cursor", this.cursor);
switch (this.cursor) {
case MenuOptions.BASE_STATS:
this.blockInput = true;
console.log("level moves", MenuOptions.LEVEL_MOVES);
ui.setMode(Mode.POKEDEX_PAGE, "refresh").then(() => {
ui.showText(i18next.t("pokedexUiHandler:baseStats"), null, () => {
const options: any[] = [];
const shortStats = [ "HP", "ATK", "DEF", "SPATK", "SPDEF", "SPD" ];
this.baseStats.map((bst, index) => {
options.push({
label: i18next.t(`pokemonInfo:Stat.${shortStats[index]}shortened`).padEnd(5, " ") + ": " + `${bst}`,
handler: () => {
return false;
}
});
});
options.push({
label: i18next.t("pokedexUiHandler:baseTotal") + ": " + `${this.baseTotal}`,
color: "#ccbe00",
handler: () => {
return false;
}
});
options.push({
label: i18next.t("menu:cancel"),
handler: () => {
this.moveInfoOverlay.clear();
this.clearText();
ui.setMode(Mode.POKEDEX_PAGE, "refresh");
return true;
},
onHover: () => this.moveInfoOverlay.clear()
});
ui.setModeWithoutClear(Mode.OPTION_SELECT, {
options: options,
supportHover: true,
maxOptions: 8,
yOffset: 19
});
this.blockInput = false;
});
});
break;
case MenuOptions.LEVEL_MOVES:
this.blockInput = true;
@ -1278,6 +1340,105 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
});
break;
case MenuOptions.BIOMES:
this.blockInput = true;
ui.setMode(Mode.POKEDEX_PAGE, "refresh").then(() => {
const options: any[] = [];
ui.showText(i18next.t("pokedexUiHandler:abilities"), null, () => {
this.biomes.map(b => {
options.push({
label: i18next.t(`biome:${Biome[b.biome].toUpperCase()}`) + " - " +
i18next.t(`biome:${BiomePoolTier[b.tier].toUpperCase()}`) +
( b.tod.length === 1 && b.tod[0] === -1 ? "" : " (" + b.tod.map(tod => i18next.t(`biome:${TimeOfDay[tod].toUpperCase()}`)).join(", ") + ")"),
handler: () => false
});
});
options.push({
label: i18next.t("menu:cancel"),
handler: () => {
this.moveInfoOverlay.clear();
this.clearText();
ui.setMode(Mode.POKEDEX_PAGE, "refresh");
return true;
},
onHover: () => this.moveInfoOverlay.clear()
});
ui.setModeWithoutClear(Mode.OPTION_SELECT, {
options: options,
supportHover: true,
maxOptions: 8,
yOffset: 19
});
this.blockInput = false;
});
});
break;
case MenuOptions.EVOLUTIONS:
this.blockInput = true;
ui.setMode(Mode.POKEDEX_PAGE, "refresh").then(() => {
const options: any[] = [];
ui.showText(i18next.t("pokedexUiHandler:abilities"), null, () => {
if (!this.evolutions) {
this.blockInput = false;
return true;
}
if (this.evolutions.length === 0) {
this.blockInput = false;
return true;
}
this.evolutions.map(evo => {
console.log(evo);
console.log(Species[evo.speciesId]);
options.push({
label: i18next.t(`pokemon:${Species[evo.speciesId].toUpperCase()}`),
color: "#ccbe00",
handler: () => false
});
options.push({
label: evo.level > 1 ? `${evo.level}` : (evo.item ? i18next.t(`modifier-type:EvolutionItem.${EvolutionItem[evo.item].toUpperCase()}`) : ""),
skip: true,
handler: () => false
});
});
options.push({
label: i18next.t("menu:cancel"),
handler: () => {
this.moveInfoOverlay.clear();
this.clearText();
ui.setMode(Mode.POKEDEX_PAGE, "refresh");
return true;
},
onHover: () => this.moveInfoOverlay.clear()
});
ui.setModeWithoutClear(Mode.OPTION_SELECT, {
options: options,
supportHover: true,
maxOptions: 8,
yOffset: 19
});
this.blockInput = false;
});
});
break;
case MenuOptions.TOGGLE_IVS:
this.toggleStatsMode();
ui.setMode(Mode.POKEDEX_PAGE, "refresh");

View File

@ -7,6 +7,7 @@ import { speciesEggMoves } from "#app/data/balance/egg-moves";
import { pokemonFormLevelMoves, pokemonSpeciesLevelMoves } from "#app/data/balance/pokemon-level-moves";
import PokemonSpecies, { allSpecies, getPokemonSpeciesForm, getPokerusStarters, PokemonForm } from "#app/data/pokemon-species";
import { getStarterValueFriendshipCap, speciesStarterCosts, POKERUS_STARTER_COUNT } from "#app/data/balance/starters";
import { catchableSpecies } from "#app/data/balance/biomes";
import { Type } from "#enums/type";
import { AbilityAttr, DexAttr, DexAttrProps, DexEntry, StarterMoveset, StarterAttributes, StarterPreferences, StarterPrefs } from "#app/system/game-data";
import { Tutorial, handleTutorial } from "#app/tutorial";
@ -38,6 +39,7 @@ import { starterPassiveAbilities } from "#app/data/balance/passives";
import { allMoves } from "#app/data/move";
import { speciesTmMoves } from "#app/data/balance/tms";
import { pokemonStarters } from "#app/data/balance/pokemon-evolutions";
import { Biome } from "#enums/biome";
// We don't need this interface here
@ -352,7 +354,7 @@ export default class PokedexUiHandler extends MessageUiHandler {
// Create and initialise filter bar
this.filterBarContainer = this.scene.add.container(0, 0);
this.filterBar = new FilterBar(this.scene, speciesContainerX, 1, 175, filterBarHeight);
this.filterBar = new FilterBar(this.scene, speciesContainerX - 10, 1, 175 + 10, filterBarHeight);
// gen filter
const genOptions: DropDownOption[] = [
@ -383,6 +385,15 @@ export default class PokedexUiHandler extends MessageUiHandler {
});
this.filterBar.addFilter(DropDownColumn.TYPES, i18next.t("filterBar:typeFilter"), new DropDown(this.scene, 0, 0, typeOptions, this.updateStarters, DropDownType.HYBRID, 0.5));
// biome filter. Making an entry in the dropdown for each biome
const biomeOptions = Object.values(Biome)
.filter((value) => typeof value === "number") // Filter numeric values from the enum
.map((biomeValue, index) =>
new DropDownOption(this.scene, index, new DropDownLabel(i18next.t(`biome:${Biome[biomeValue].toUpperCase()}`)))
);
const biomeDropDown: DropDown = new DropDown(this.scene, 0, 0, biomeOptions, this.updateStarters, DropDownType.HYBRID);
this.filterBar.addFilter(DropDownColumn.BIOME, i18next.t("filterBar:biomeFilter"), biomeDropDown);
// caught filter
const shiny1Sprite = this.scene.add.sprite(0, 0, "shiny_icons");
shiny1Sprite.setOrigin(0.15, 0.2);
@ -1361,6 +1372,18 @@ export default class PokedexUiHandler extends MessageUiHandler {
// Type filter
const fitsType = this.filterBar.getVals(DropDownColumn.TYPES).some(type => container.species.isOfType((type as number) - 1));
// Biome filter
const indexToBiome = new Map(
Object.values(Biome).map((value, index) => [ index, value ])
);
// We get biomes for both the mon and its starters to ensure that evolutions get the correct filters.
// TODO: We might also need to do it the other way around.
// const biomes = catchableSpecies[container.species.speciesId].concat(catchableSpecies[this.getStarterSpeciesId(container.species.speciesId)]).map(b => Biome[b.biome]);
const biomes = catchableSpecies[container.species.speciesId].map(b => Biome[b.biome]);
const fitsBiome = this.filterBar.getVals(DropDownColumn.BIOME).some(item => biomes.includes(indexToBiome.get(item)));
// Caught / Shiny filter
const isNonShinyCaught = !!(caughtAttr & DexAttr.NON_SHINY);
const isShinyCaught = !!(caughtAttr & DexAttr.SHINY);
@ -1476,7 +1499,7 @@ export default class PokedexUiHandler extends MessageUiHandler {
}
});
if (fitsName && fitsAbilities && fitsMoves && fitsGen && fitsType && fitsCaught && fitsPassive && fitsCostReduction && fitsFavorite && fitsWin && fitsHA && fitsEgg && fitsPokerus) {
if (fitsName && fitsAbilities && fitsMoves && fitsGen && fitsBiome && fitsType && fitsCaught && fitsPassive && fitsCostReduction && fitsFavorite && fitsWin && fitsHA && fitsEgg && fitsPokerus) {
this.filteredStarterContainers.push(container);
}
});