Filtering starters by name is working

This commit is contained in:
Wlowscha 2024-12-30 10:29:11 +01:00
parent 6cb2d71b1e
commit 3009de74cb
No known key found for this signature in database
GPG Key ID: 3C8F1AD330565D04
4 changed files with 279 additions and 44 deletions

View File

@ -19,6 +19,8 @@ export enum FilterTextRow{
export class FilterText extends Phaser.GameObjects.Container {
private window: Phaser.GameObjects.NineSlice;
private labels: Phaser.GameObjects.Text[] = [];
private selections: Phaser.GameObjects.Text[] = [];
private selectionStrings: string[] = [];
// private dropDowns: DropDown[] = [];
private rows: FilterTextRow[] = [];
public cursorObj: Phaser.GameObjects.Image;
@ -33,9 +35,15 @@ export class FilterText extends Phaser.GameObjects.Container {
private readonly textPadding = 8;
private readonly defaultWordWrapWidth = 1224;
constructor(scene: BattleScene, x: number, y: number, width: number, height: number) {
private onChange: () => void;
public defaultText: string = "---";
constructor(scene: BattleScene, x: number, y: number, width: number, height: number, onChange: () => void,) {
super(scene, x, y);
this.onChange = onChange;
this.width = width;
this.height = height;
@ -84,6 +92,7 @@ export class FilterText extends Phaser.GameObjects.Container {
const paddingX = 6;
const cursorOffset = 8;
const extraSpaceX = 50;
if (this.rows.includes(row)) {
return false;
@ -95,6 +104,12 @@ export class FilterText extends Phaser.GameObjects.Container {
this.labels.push(filterTypesLabel);
this.add(filterTypesLabel);
const filterTypesSelection = addTextObject(this.scene, paddingX + cursorOffset + extraSpaceX, 3, this.defaultText, TextStyle.TOOLTIP_CONTENT);
this.selections.push(filterTypesSelection);
this.add(filterTypesSelection);
this.selectionStrings.push("");
this.calcFilterPositions();
this.numFilters++;
@ -110,6 +125,11 @@ export class FilterText extends Phaser.GameObjects.Container {
return this.dropDowns[this.rows.indexOf(row)];
}
resetSelection(index: number): void {
this.selections[index].setText(this.defaultText);
this.selectionStrings[index] = "";
this.onChange();
}
startSearch(index: number, ui: UI): void {
@ -121,36 +141,27 @@ export class FilterText extends Phaser.GameObjects.Container {
// ui.revertMode();
ui.playSelect();
const dialogueTestName = sanitizedName;
console.log("1", dialogueTestName);
//TODO: Is it really necessary to encode and decode?
const dialogueName = decodeURIComponent(escape(atob(dialogueTestName)));
console.log("2", dialogueName);
const handler = ui.getHandler() as AwaitableUiHandler;
handler.tutorialActive = true;
const interpolatorOptions: any = {};
const splitArr = dialogueName.split(" "); // this splits our inputted text into words to cycle through later
const translatedString = splitArr[0]; // this is our outputted i18 string
const regex = RegExp("\\{\\{(\\w*)\\}\\}", "g"); // this is a regex expression to find all the text between {{ }} in the i18 output
const matches = i18next.t(translatedString).match(regex) ?? [];
if (matches.length > 0) {
for (let match = 0; match < matches.length; match++) {
// we add 1 here because splitArr[0] is our first value for the translatedString, and after that is where the variables are
// the regex here in the replace (/\W/g) is to remove the {{ and }} and just give us all alphanumeric characters
if (typeof splitArr[match + 1] !== "undefined") {
interpolatorOptions[matches[match].replace(/\W/g, "")] = i18next.t(splitArr[match + 1]);
}
}
}
// Switch to the dialog test window
this.setDialogTestMode(true);
ui.showText(String(i18next.t(translatedString, interpolatorOptions)), null, () => this.scene.ui.showText("", 0, () => {
handler.tutorialActive = false;
// Go back to the default message window
this.setDialogTestMode(false);
}), null, true);
console.log("6", "switch");
this.selections[index].setText(String(i18next.t(dialogueName)));
console.log("6.5", "revert");
ui.revertMode();
this.onChange();
},
() => {
console.log("7", "revert");
ui.revertMode();
this.onChange;
}
];
ui.setMode(Mode.TEST_DIALOGUE, buttonAction, prefilledText);
console.log("8", "setmode");
ui.setOverlayMode(Mode.POKEDEX_SCAN, buttonAction, prefilledText, index);
}
@ -206,9 +217,11 @@ export class FilterText extends Phaser.GameObjects.Container {
for (let i = 0; i < this.labels.length; i++) {
if (i === 0) {
this.labels[i].y = paddingY;
this.selections[i].y = paddingY;
} else {
const lastBottom = this.labels[i - 1].y + this.labels[i - 1].displayHeight;
this.labels[i].y = lastBottom + spacing;
this.selections[i].y = lastBottom + spacing;
}
// Uncomment and adjust if necessary to position dropdowns vertically
// this.dropDowns[i].y = this.labels[i].y + this.labels[i].displayHeight + paddingY;
@ -264,8 +277,9 @@ export class FilterText extends Phaser.GameObjects.Container {
this.dropDowns[this.lastCursor].toggleOptionState();
}
getVals(row: FilterTextRow): any[] {
return this.getFilter(row).getVals();
getValue(row: number): string {
console.log("Getting value", this.selections[row].getWrappedText()[0]);
return this.selections[row].getWrappedText()[0];
}
setValsToDefault(): void {

View File

@ -0,0 +1,211 @@
import { FormModalUiHandler, InputFieldConfig } from "./form-modal-ui-handler";
import { ModalConfig } from "./modal-ui-handler";
import i18next from "i18next";
import { PlayerPokemon } from "#app/field/pokemon";
import { OptionSelectItem } from "./abstact-option-select-ui-handler";
import { isNullOrUndefined } from "#app/utils";
import { Mode } from "./ui";
import { FilterTextRow } from "./filter-text";
export default class PokedexScanUiHandler extends FormModalUiHandler {
keys: string[];
reducedKeys: string[];
parallelKeys: string[];
constructor(scene, mode) {
super(scene, mode);
}
setup() {
super.setup();
const flattenKeys = (object?: any, topKey?: string, midleKey?: string[]): Array<any> => {
return Object.keys(object ?? {}).map((t, i) => {
const value = Object.values(object)[i];
if (typeof value === "object" && !isNullOrUndefined(value)) { // we check for not null or undefined here because if the language json file has a null key, the typeof will still be an object, but that object will be null, causing issues
// If the value is an object, execute the same process
// si el valor es un objeto ejecuta el mismo proceso
return flattenKeys(value, topKey ?? t, topKey ? midleKey ? [ ...midleKey, t ] : [ t ] : undefined).filter((t) => t.length > 0);
} else if (typeof value === "string" || isNullOrUndefined(value)) { // we check for null or undefined here as per above - the typeof is still an object but the value is null so we need to exit out of this and pass the null key
// Return in the format expected by i18next
return midleKey ? `${topKey}:${midleKey.map((m) => m).join(".")}.${t}` : `${topKey}:${t}`;
}
}).filter((t) => t);
};
const keysInArrays = flattenKeys(i18next.getDataByLanguage(String(i18next.resolvedLanguage))).filter((t) => t.length > 0); // Array of arrays
const keys = keysInArrays.flat(Infinity).map(String); // One array of string
this.keys = keys;
}
getModalTitle(config?: ModalConfig): string {
return "Choose option";
}
getWidth(config?: ModalConfig): number {
return 300;
}
getMargin(config?: ModalConfig): [number, number, number, number] {
return [ 0, 0, 48, 0 ];
}
getButtonLabels(config?: ModalConfig): string[] {
return [ "Select", "Cancel" ];
}
getReadableErrorMessage(error: string): string {
const colonIndex = error?.indexOf(":");
if (colonIndex > 0) {
error = error.slice(0, colonIndex);
}
return super.getReadableErrorMessage(error);
}
override getInputFieldConfigs(): InputFieldConfig[] {
return [{ label: "Dialogue" }];
}
reduceKeys(row: FilterTextRow): void {
console.log("Function was called!");
console.log(this.keys);
switch (row) {
case FilterTextRow.NAME: {
const startString = "pokemon:";
const endString = "";
this.reducedKeys = this.keys.filter(str => str.startsWith(startString) && str.endsWith(endString));
break;
}
case FilterTextRow.MOVE_1:
case FilterTextRow.MOVE_2: {
const startString = "move:";
const endString = ".name";
this.reducedKeys = this.keys.filter(str => str.startsWith(startString) && str.endsWith(endString));
break;
}
case FilterTextRow.ABILITY_1:
case FilterTextRow.ABILITY_2: {
const startString = "ability:";
const endString = ".name";
this.reducedKeys = this.keys.filter(str => str.startsWith(startString) && str.endsWith(endString));
break;
}
default: {
this.reducedKeys = this.keys;
}
}
console.log(this.reducedKeys);
// this.parallelKeys = this.reducedKeys.map(key => this.translateKey(key));
this.parallelKeys = this.reducedKeys.map(key => String(i18next.t(key)));
console.log(this.parallelKeys);
}
translateKey(key: string): string {
const interpolatorOptions: any = {};
const splitArr = key.split(" "); // this splits our inputted text into words to cycle through later
const translatedString = splitArr[0]; // this is our outputted i18 string
const regex = RegExp("\\{\\{(\\w*)\\}\\}", "g"); // this is a regex expression to find all the text between {{ }} in the i18 output
const matches = i18next.t(translatedString).match(regex) ?? [];
if (matches.length > 0) {
for (let match = 0; match < matches.length; match++) {
// we add 1 here because splitArr[0] is our first value for the translatedString, and after that is where the variables are
// the regex here in the replace (/\W/g) is to remove the {{ and }} and just give us all alphanumeric characters
if (typeof splitArr[match + 1] !== "undefined") {
interpolatorOptions[matches[match].replace(/\W/g, "")] = i18next.t(splitArr[match + 1]);
}
}
}
return String(i18next.t(translatedString, interpolatorOptions));
}
// args[2] is an index of FilterTextRow
show(args: any[]): boolean {
const ui = this.getUi();
const hasTitle = !!this.getModalTitle();
this.updateFields(this.getInputFieldConfigs(), hasTitle);
this.updateContainer(args[0] as ModalConfig);
const input = this.inputs[0];
input.setMaxLength(255);
console.log(args[2]);
this.reduceKeys(args[2]);
input.on("keydown", (inputObject, evt: KeyboardEvent) => {
if ([ "escape", "space" ].some((v) => v === evt.key.toLowerCase() || v === evt.code.toLowerCase()) && ui.getMode() === Mode.AUTO_COMPLETE) {
// Delete autocomplete list and recovery focus.
inputObject.on("blur", () => inputObject.node.focus(), { once: true });
ui.revertMode();
}
});
input.on("textchange", (inputObject, evt: InputEvent) => {
// Delete autocomplete.
if (ui.getMode() === Mode.AUTO_COMPLETE) {
ui.revertMode();
}
let options: OptionSelectItem[] = [];
const splitArr = inputObject.text.split(" ");
const filteredKeys = this.parallelKeys.filter((command) => command.toLowerCase().includes(splitArr[splitArr.length - 1].toLowerCase()));
if (inputObject.text !== "" && filteredKeys.length > 0) {
// if performance is required, you could reduce the number of total results by changing the slice below to not have all ~8000 inputs going
options = filteredKeys.slice(0).map((value) => {
return {
label: value,
handler: () => {
// this is here to make sure that if you try to backspace then enter, the last known evt.data (backspace) is picked up
// this is because evt.data is null for backspace, so without this, the autocomplete windows just closes
if (!isNullOrUndefined(evt.data) || evt.inputType?.toLowerCase() === "deletecontentbackward") {
const separatedArray = inputObject.text.split(" ");
separatedArray[separatedArray.length - 1] = value;
inputObject.setText(separatedArray.join(" "));
}
ui.revertMode();
return true;
}
};
});
}
if (options.length > 0) {
const modalOpts = {
options: options,
maxOptions: 5,
modalContainer: this.modalContainer
};
ui.setOverlayMode(Mode.AUTO_COMPLETE, modalOpts);
}
});
if (super.show(args)) {
const config = args[0] as ModalConfig;
this.inputs[0].resize(1150, 116);
this.inputContainers[0].list[0].width = 200;
if (args[1] && typeof (args[1] as PlayerPokemon).getNameToRender === "function") {
this.inputs[0].text = (args[1] as PlayerPokemon).getNameToRender();
} else {
this.inputs[0].text = args[1];
}
this.submitAction = (_) => {
if (ui.getMode() === Mode.POKEDEX_SCAN) {
this.sanitizeInputs();
const sanitizedName = btoa(unescape(encodeURIComponent(this.inputs[0].text)));
config.buttonActions[0](sanitizedName);
return true;
}
return false;
};
return true;
}
return false;
}
}

View File

@ -194,7 +194,7 @@ export default class PokedexUiHandler extends MessageUiHandler {
// Create and initialise filter text fields
this.filterTextContainer = this.scene.add.container(0, 0);
this.filterText = new FilterText(this.scene, 1, filterBarHeight + 2, 110, 100);
this.filterText = new FilterText(this.scene, 1, filterBarHeight + 2, 140, 100, this.updateStarters);
// const nameTextField: TextField = new TextField(this.scene, 0, 0, genOptions, this.updateStarters, DropDownType.HYBRID);
this.filterText.addFilter(FilterTextRow.NAME, i18next.t("filterText:nameField"));
@ -291,15 +291,15 @@ export default class PokedexUiHandler extends MessageUiHandler {
// gen filter
const genOptions: DropDownOption[] = [
new DropDownOption(this.scene, 1, new DropDownLabel(i18next.t("PokedexUiHandler:gen1"))),
new DropDownOption(this.scene, 2, new DropDownLabel(i18next.t("PokedexUiHandler:gen2"))),
new DropDownOption(this.scene, 3, new DropDownLabel(i18next.t("PokedexUiHandler:gen3"))),
new DropDownOption(this.scene, 4, new DropDownLabel(i18next.t("PokedexUiHandler:gen4"))),
new DropDownOption(this.scene, 5, new DropDownLabel(i18next.t("PokedexUiHandler:gen5"))),
new DropDownOption(this.scene, 6, new DropDownLabel(i18next.t("PokedexUiHandler:gen6"))),
new DropDownOption(this.scene, 7, new DropDownLabel(i18next.t("PokedexUiHandler:gen7"))),
new DropDownOption(this.scene, 8, new DropDownLabel(i18next.t("PokedexUiHandler:gen8"))),
new DropDownOption(this.scene, 9, new DropDownLabel(i18next.t("PokedexUiHandler:gen9"))),
new DropDownOption(this.scene, 1, new DropDownLabel(i18next.t("pokedex-ui-handler:gen1"))),
new DropDownOption(this.scene, 2, new DropDownLabel(i18next.t("pokedex-ui-handler:gen2"))),
new DropDownOption(this.scene, 3, new DropDownLabel(i18next.t("pokedex-ui-handler:gen3"))),
new DropDownOption(this.scene, 4, new DropDownLabel(i18next.t("pokedex-ui-handler:gen4"))),
new DropDownOption(this.scene, 5, new DropDownLabel(i18next.t("pokedex-ui-handler:gen5"))),
new DropDownOption(this.scene, 6, new DropDownLabel(i18next.t("pokedex-ui-handler:gen6"))),
new DropDownOption(this.scene, 7, new DropDownLabel(i18next.t("pokedex-ui-handler:gen7"))),
new DropDownOption(this.scene, 8, new DropDownLabel(i18next.t("pokedex-ui-handler:gen8"))),
new DropDownOption(this.scene, 9, new DropDownLabel(i18next.t("pokedex-ui-handler:gen9"))),
];
const genDropDown: DropDown = new DropDown(this.scene, 0, 0, genOptions, this.updateStarters, DropDownType.HYBRID);
this.filterBar.addFilter(DropDownColumn.GEN, i18next.t("filterBar:genFilter"), genDropDown);
@ -582,7 +582,7 @@ export default class PokedexUiHandler extends MessageUiHandler {
console.log(this.filterTextOptions);
this.optionSelectText = addTextObject(this.scene, 0, 0, this.filterTextOptions.map(o => `${i18next.t(`pokedexUiHandler:${FilterTextOptions[o]}`)}`).join("\n"), TextStyle.WINDOW, { maxLines: this.filterTextOptions.length });
this.optionSelectText = addTextObject(this.scene, 0, 0, this.filterTextOptions.map(o => `${i18next.t(`pokedex-ui-handler:${FilterTextOptions[o]}`)}`).join("\n"), TextStyle.WINDOW, { maxLines: this.filterTextOptions.length });
this.optionSelectText.setLineSpacing(12);
// Positioning the menu
@ -951,12 +951,7 @@ export default class PokedexUiHandler extends MessageUiHandler {
success = true;
} else if (this.filterTextMode) {
if (numberOfStarters > 0) {
this.setFilterTextMode(false);
this.scrollCursor = 0;
this.updateScroll();
this.setCursor(0);
}
this.filterText.resetSelection(this.filterTextCursor);
success = true;
} else if (this.statsMode) {
this.toggleStatsMode(false);
@ -1116,7 +1111,7 @@ export default class PokedexUiHandler extends MessageUiHandler {
};
if (!pokemonPrevolutions.hasOwnProperty(this.lastSpecies.speciesId)) {
options.push({
label: i18next.t("PokedexUiHandler:useCandies"),
label: i18next.t("pokedex-ui-handler:useCandies"),
handler: () => {
ui.setMode(Mode.POKEDEX, "refresh").then(() => showUseCandies());
return true;
@ -1357,10 +1352,15 @@ export default class PokedexUiHandler extends MessageUiHandler {
container.cost = this.scene.gameData.getSpeciesStarterValue(container.species.speciesId);
// First, ensure you have the caught attributes for the species else default to bigint 0
// TODO: This might be removed depending on how accessible we want the pokedex function to be
const caughtAttr = this.scene.gameData.dexData[container.species.speciesId]?.caughtAttr || BigInt(0);
const starterData = this.scene.gameData.starterData[container.species.speciesId];
const isStarterProgressable = speciesEggMoves.hasOwnProperty(container.species.speciesId);
// Name filter
console.log(container.species.name);
const fitsName = [ container.species.name, this.filterText.defaultText ].includes(this.filterText.getValue(FilterTextRow.NAME));
// Gen filter
const fitsGen = this.filterBar.getVals(DropDownColumn.GEN).includes(container.species.generation);
@ -1482,7 +1482,7 @@ export default class PokedexUiHandler extends MessageUiHandler {
}
});
if (fitsGen && fitsType && fitsCaught && fitsPassive && fitsCostReduction && fitsFavorite && fitsWin && fitsHA && fitsEgg && fitsPokerus) {
if (fitsName && fitsGen && fitsType && fitsCaught && fitsPassive && fitsCostReduction && fitsFavorite && fitsWin && fitsHA && fitsEgg && fitsPokerus) {
this.filteredStarterContainers.push(container);
}
});
@ -1615,6 +1615,12 @@ export default class PokedexUiHandler extends MessageUiHandler {
const pos = calcStarterPosition(cursor, this.scrollCursor);
this.cursorObj.setPosition(pos.x - 1, pos.y + 1);
const species = this.filteredStarterContainers[cursor]?.species;
if (species) {
this.setSpecies(species);
}
}
return changed;
@ -2064,7 +2070,7 @@ export default class PokedexUiHandler extends MessageUiHandler {
this.clearText();
this.blockInput = false;
};
ui.showText(i18next.t("PokedexUiHandler:confirmExit"), null, () => {
ui.showText(i18next.t("pokedex-ui-handler:confirmExit"), null, () => {
ui.setModeWithoutClear(Mode.CONFIRM, () => {
ui.setMode(Mode.POKEDEX, "refresh");
console.log("Press exit", ui.getModeChain(), ui.getMode());

View File

@ -54,6 +54,7 @@ import TestDialogueUiHandler from "#app/ui/test-dialogue-ui-handler";
import AutoCompleteUiHandler from "./autocomplete-ui-handler";
import { Device } from "#enums/devices";
import MysteryEncounterUiHandler from "./mystery-encounter-ui-handler";
import PokedexScanUiHandler from "./pokedex-scan-ui-handler";
export enum Mode {
MESSAGE,
@ -86,6 +87,7 @@ export enum Mode {
EGG_LIST,
EGG_GACHA,
POKEDEX,
POKEDEX_SCAN,
LOGIN_FORM,
REGISTRATION_FORM,
LOADING,
@ -130,6 +132,7 @@ const noTransitionModes = [
Mode.SETTINGS_KEYBOARD,
Mode.ACHIEVEMENTS,
Mode.GAME_STATS,
Mode.POKEDEX_SCAN,
Mode.LOGIN_FORM,
Mode.REGISTRATION_FORM,
Mode.LOADING,
@ -196,6 +199,7 @@ export default class UI extends Phaser.GameObjects.Container {
new EggListUiHandler(scene),
new EggGachaUiHandler(scene),
new PokedexUiHandler(scene),
new PokedexScanUiHandler(scene, Mode.TEST_DIALOGUE),
new LoginFormUiHandler(scene),
new RegistrationFormUiHandler(scene),
new LoadingModalUiHandler(scene),