[Enhancement] improve `passive` and `win` filters by applying new Tri-state toggle dropdown in the starter select UI (#3252)

* split unlocks filter into shiny and passive

* update tri state dropdown. apply it to passive and win dropdown
This commit is contained in:
Leo Kim 2024-07-31 01:13:17 +09:00 committed by GitHub
parent cce68f6ef7
commit 0ad4986fa5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 158 additions and 63 deletions

View File

@ -6,12 +6,15 @@ import i18next from "i18next";
export enum DropDownState {
ON = 0,
OFF
OFF = 1,
INCLUDE = 2,
EXCLUDE = 3,
}
export enum DropDownType {
MULTI = 0,
SINGLE
SINGLE = 1,
TRI = 2
}
export enum SortDirection {
@ -26,11 +29,30 @@ export class DropDownOption extends Phaser.GameObjects.Container {
public sprite?: Phaser.GameObjects.Sprite;
public val: any;
public dir: SortDirection = SortDirection.ASC;
public offStateLabel: string; // label for OFF state in TRI dropdown
public includeStateLabel: string; // label for INCLUDE state in TRI dropdown
public excludeStateLabel: string; // label for EXCLUDE state in TRI dropdown
private onColor = 0x55ff55;
private offColor = 0x272727;
private includeColor = 0x55ff55;
private excludeColor = 0xff5555;
constructor(scene: SceneBase, val: any, text: string, sprite?: Phaser.GameObjects.Sprite, state: DropDownState = DropDownState.ON) {
constructor(scene: SceneBase, val: any, text: string | string[], sprite?: Phaser.GameObjects.Sprite, state: DropDownState = DropDownState.ON) {
super(scene);
this.val = val;
this.state = state;
if (text) {
if (Array.isArray(text)) {
this.offStateLabel = text[0];
this.includeStateLabel = text[1];
this.excludeStateLabel = text[2];
text = text[0];
} else {
this.offStateLabel = undefined;
this.includeStateLabel = undefined;
this.excludeStateLabel = undefined;
}
this.text = addTextObject(scene, 0, 0, text, TextStyle.TOOLTIP_CONTENT);
this.text.setOrigin(0, 0.5);
this.add(this.text);
@ -40,11 +62,10 @@ export class DropDownOption extends Phaser.GameObjects.Container {
this.sprite = sprite.setOrigin(0, 0.5);
this.add(this.sprite);
}
this.state = state;
}
public setupToggle(type: DropDownType): void {
if (type === DropDownType.MULTI) {
if (type === DropDownType.MULTI || type === DropDownType.TRI) {
this.toggle = this.scene.add.sprite(0, 0, "candy");
this.toggle.setScale(0.3);
this.toggle.setOrigin(0, 0.5);
@ -57,18 +78,54 @@ export class DropDownOption extends Phaser.GameObjects.Container {
this.add(this.toggle);
}
public setOptionState(state: DropDownState): DropDownState {
this.state = state % 2;
if (this.state === DropDownState.OFF) {
this.toggle.setTint(0x272727);
} else {
this.toggle.setTint(0x55ff55);
public setOptionState(type: DropDownType, state: DropDownState): DropDownState {
this.state = state;
// if type is MULTI or SINGLE, set the color of the toggle based on the state
if (type === DropDownType.MULTI || type === DropDownType.SINGLE) {
if (this.state === DropDownState.OFF) {
this.toggle.setTint(this.offColor);
} else if (this.state === DropDownState.ON) {
this.toggle.setTint(this.onColor);
}
} else if (type === DropDownType.TRI) {
if (this.state === DropDownState.OFF) {
this.text.setText(this.offStateLabel);
this.toggle.setTint(this.offColor);
} else if (this.state === DropDownState.INCLUDE) {
this.text.setText(this.includeStateLabel);
this.toggle.setTint(this.includeColor);
} else if (this.state === DropDownState.EXCLUDE) {
this.text.setText(this.excludeStateLabel);
this.toggle.setTint(this.excludeColor);
}
}
return this.state;
}
public toggleOptionState(): DropDownState {
return this.setOptionState(this.state + 1);
public toggleOptionState(type: DropDownType): DropDownState {
if (type === DropDownType.TRI) {
switch (this.state) {
case DropDownState.OFF:
this.state = DropDownState.INCLUDE;
break;
case DropDownState.INCLUDE:
this.state = DropDownState.EXCLUDE;
break;
case DropDownState.EXCLUDE:
this.state = DropDownState.OFF;
break;
}
} else {
switch (this.state) {
case DropDownState.ON:
this.state = DropDownState.OFF;
break;
case DropDownState.OFF:
this.state = DropDownState.ON;
break;
}
}
return this.setOptionState(type, this.state);
}
public setDirection(dir: SortDirection): void {
@ -117,7 +174,7 @@ export class DropDown extends Phaser.GameObjects.Container {
if (type === DropDownType.SINGLE && option.state === DropDownState.OFF) {
option.toggle.setVisible(false);
}
option.setOptionState(option.state);
option.setOptionState(type, option.state);
option.width = optionWidth;
option.y = index * optionHeight + index * optionSpacing + optionPaddingY;
@ -130,8 +187,13 @@ export class DropDown extends Phaser.GameObjects.Container {
option.sprite.x = cursorOffset + optionPaddingX + 3 + 8;
option.sprite.y = optionHeight / 2;
}
option.toggle.x = cursorOffset + optionPaddingX + 3 + (type === DropDownType.MULTI ? 0 : 3);
option.toggle.y = optionHeight / 2 + (type === DropDownType.MULTI ? 0 : 1);
if (type === DropDownType.SINGLE) {
option.toggle.x = cursorOffset + optionPaddingX + 3 + 3;
option.toggle.y = optionHeight / 2 + 1;
} else {
option.toggle.x = cursorOffset + optionPaddingX + 3;
option.toggle.y = optionHeight / 2;
}
});
this.window = addWindow(scene, 0, 0, optionWidth, options[options.length - 1].y + optionHeight + optionPaddingY, false, false, null, null, WindowVariant.XTHIN);
this.add(this.window);
@ -164,37 +226,39 @@ export class DropDown extends Phaser.GameObjects.Container {
toggleOptionState(): void {
if (this.dropDownType === DropDownType.MULTI) {
const newState = this.options[this.cursor].toggleOptionState();
const newState = this.options[this.cursor].toggleOptionState(this.dropDownType);
if (this.cursor === 0) {
this.options.forEach((option, index) => {
if (index !== this.cursor) {
option.setOptionState(newState);
option.setOptionState(this.dropDownType, newState);
}
});
} else {
if (this.checkForAllOff()) {
this.options[0].setOptionState(DropDownState.OFF);
this.options[0].setOptionState(this.dropDownType, DropDownState.OFF);
} else if (this.checkForAllOn()) {
this.options[0].setOptionState(DropDownState.ON);
this.options[0].setOptionState(this.dropDownType, DropDownState.ON);
} else {
this.options[0].setOptionState(DropDownState.OFF);
this.options[0].setOptionState(this.dropDownType, DropDownState.OFF);
}
}
} else {
} else if (this.dropDownType === DropDownType.SINGLE) {
if (this.options[this.cursor].state === DropDownState.OFF) {
this.options.forEach((option) => {
option.setOptionState(DropDownState.OFF);
option.setOptionState(this.dropDownType, DropDownState.OFF);
option.setDirection(SortDirection.ASC);
option.toggle.setVisible(false);
});
this.options[this.cursor].setOptionState(DropDownState.ON);
this.options[this.cursor].setOptionState(this.dropDownType, DropDownState.ON);
this.options[this.cursor].setDirection(this.lastDir);
this.options[this.cursor].toggle.setVisible(true);
} else {
this.options[this.cursor].toggleDirection();
this.lastDir = this.options[this.cursor].dir;
}
} else if (this.dropDownType === DropDownType.TRI) {
this.options[this.cursor].toggleOptionState(this.dropDownType);
this.autoSize();
}
this.onChange();
}
@ -220,6 +284,11 @@ export class DropDown extends Phaser.GameObjects.Container {
getVals(): any[] {
if (this.dropDownType === DropDownType.MULTI) {
return this.options.filter((option, i) => i > 0 && option.state === DropDownState.ON).map((option) => option.val);
// in TRI dropdown, if state is ON, return the "ON" with the value, if state is OFF, return the "OFF" with the value, if state is TRI, return the "TRI" with the value
} else if (this.dropDownType === DropDownType.TRI) {
return this.options.filter((option, i) => option.state === DropDownState.OFF || option.state === DropDownState.INCLUDE || option.state === DropDownState.EXCLUDE).map((option) => {
return {val: option.val, state: option.state};
});
} else {
return this.options.filter((option, i) => option.state === DropDownState.ON).map((option) => {
return {val: option.val, dir: option.dir};

View File

@ -7,8 +7,9 @@ import { addWindow, WindowVariant } from "./ui-theme";
export enum DropDownColumn {
GEN,
TYPES,
SHINY,
UNLOCKS,
WIN,
MISC,
SORT
}
@ -22,8 +23,9 @@ export class FilterBar extends Phaser.GameObjects.Container {
private lastCursor: number = -1;
public defaultGenVals: any[] = [];
public defaultTypeVals: any[] = [];
public defaultUnlockVals: any[] = [];
public defaultWinVals: any[] = [];
public defaultShinyVals: any[] = [];
public defaultUnlocksVals: any[] = [];
public defaultMiscVals: any[] = [];
public defaultSortVals: any[] = [];
constructor(scene: BattleScene, x: number, y: number, width: number, height: number) {
@ -57,43 +59,51 @@ export class FilterBar extends Phaser.GameObjects.Container {
updateFilterLabels(): void {
const genVals = this.getVals(DropDownColumn.GEN);
const typeVals = this.getVals(DropDownColumn.TYPES);
const unlockVals = this.getVals(DropDownColumn.UNLOCKS);
const winVals = this.getVals(DropDownColumn.WIN);
const shinyVals = this.getVals(DropDownColumn.SHINY);
const unlocksVals = this.getVals(DropDownColumn.UNLOCKS);
const miscVals = this.getVals(DropDownColumn.MISC);
const sortVals = this.getVals(DropDownColumn.SORT);
// onColor is Yellow, offColor is White
const onColor = 0xffef5c;
const offColor = 0xffffff;
// if genVals and defaultGenVals has same elements, set the label to White else set it to Green
// if genVals and defaultGenVals has same elements, set the label to offColor else set it to onColor
if (genVals.length === this.defaultGenVals.length && genVals.every((value, index) => value === this.defaultGenVals[index])) {
this.labels[DropDownColumn.GEN].setTint(offColor);
} else {
this.labels[DropDownColumn.GEN].setTint(onColor);
}
// if typeVals and defaultTypeVals has same elements, set the label to White else set it to Green
// if typeVals and defaultTypeVals has same elements, set the label to offColor else set it to onColor
if (typeVals.length === this.defaultTypeVals.length && typeVals.every((value, index) => value === this.defaultTypeVals[index])) {
this.labels[DropDownColumn.TYPES].setTint(offColor);
} else {
this.labels[DropDownColumn.TYPES].setTint(onColor);
}
// if unlockVals and defaultUnlockVals has same elements, set the label to White else set it to Green
if (unlockVals.length === this.defaultUnlockVals.length && unlockVals.every((value, index) => value === this.defaultUnlockVals[index])) {
// if shinyVals and defaultShinyVals has same elements, set the label to offColor else set it to onColor
if (shinyVals.length === this.defaultShinyVals.length && shinyVals.every((value, index) => value === this.defaultShinyVals[index])) {
this.labels[DropDownColumn.SHINY].setTint(offColor);
} else {
this.labels[DropDownColumn.SHINY].setTint(onColor);
}
// if unlocksVals and defaultUnlocksVals has same elements, set the label to offColor else set it to onColor
if (unlocksVals.every((value, index) => value["val"] === this.defaultUnlocksVals[index]["val"] && value["state"] === this.defaultUnlocksVals[index]["state"])) {
this.labels[DropDownColumn.UNLOCKS].setTint(offColor);
} else {
this.labels[DropDownColumn.UNLOCKS].setTint(onColor);
}
// if winVals and defaultWinVals has same elements, set the label to White else set it to Green
if (winVals.length === this.defaultWinVals.length && winVals.every((value, index) => value === this.defaultWinVals[index])) {
this.labels[DropDownColumn.WIN].setTint(offColor);
// if miscVals and defaultMiscVals has same elements, set the label to offColor else set it to onColor
if (miscVals.every((value, index) => value["val"] === this.defaultMiscVals[index]["val"] && value["state"] === this.defaultMiscVals[index]["state"])) {
this.labels[DropDownColumn.MISC].setTint(offColor);
} else {
this.labels[DropDownColumn.WIN].setTint(onColor);
this.labels[DropDownColumn.MISC].setTint(onColor);
}
// if sortVals and defaultSortVals has same value and dir, set the label to White else set it to Green
// if sortVals and defaultSortVals has same value and dir, set the label to offColor else set it to onColor
if (sortVals[0]["dir"] === this.defaultSortVals[0]["dir"] && sortVals[0]["val"] === this.defaultSortVals[0]["val"]) {
this.labels[DropDownColumn.SORT].setTint(offColor);
} else {

View File

@ -327,7 +327,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
// set gen filter to all off except for the I GEN
for (const option of genOptions) {
if (option.val !== 1) {
option.setOptionState(DropDownState.OFF);
option.setOptionState(DropDownType.MULTI ,DropDownState.OFF);
}
}
@ -346,7 +346,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.filterBar.addFilter(i18next.t("filterBar:typeFilter"), new DropDown(this.scene, 0, 0, typeOptions, this.updateStarters, DropDownType.MULTI, 0.5));
this.filterBar.defaultTypeVals = this.filterBar.getVals(DropDownColumn.TYPES);
// Unlocks filter
// shiny filter
const shiny1Sprite = this.scene.add.sprite(0, 0, "shiny_icons");
shiny1Sprite.setOrigin(0.15, 0.2);
shiny1Sprite.setScale(0.6);
@ -363,24 +363,32 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
shiny3Sprite.setFrame(getVariantIcon(2));
shiny3Sprite.setTint(getVariantTint(2));
const unlocksOptions = [
const shinyOptions = [
new DropDownOption(this.scene, "SHINY3", null, shiny3Sprite),
new DropDownOption(this.scene, "SHINY2", null, shiny2Sprite),
new DropDownOption(this.scene, "SHINY", null, shiny1Sprite),
new DropDownOption(this.scene, "NORMAL", i18next.t("filterBar:normal")),
new DropDownOption(this.scene, "UNCAUGHT", i18next.t("filterBar:uncaught")),
new DropDownOption(this.scene, "PASSIVEUNLOCKED", i18next.t("filterBar:passiveUnlocked")),
new DropDownOption(this.scene, "PASSIVELOCKED", i18next.t("filterBar:passiveLocked"))];
];
this.filterBar.addFilter(i18next.t("filterBar:unlocksFilter"), new DropDown(this.scene, 0, 0, unlocksOptions, this.updateStarters, DropDownType.MULTI));
this.filterBar.defaultUnlockVals = this.filterBar.getVals(DropDownColumn.UNLOCKS);
this.filterBar.addFilter("Owned", new DropDown(this.scene, 0, 0, shinyOptions, this.updateStarters, DropDownType.MULTI));
this.filterBar.defaultShinyVals = this.filterBar.getVals(DropDownColumn.SHINY);
// win filter
const winOptions = [
new DropDownOption(this.scene, "WIN", i18next.t("filterBar:hasWon")),
new DropDownOption(this.scene, "NOTWIN", i18next.t("filterBar:hasNotWon"))];
this.filterBar.addFilter(i18next.t("filterBar:winFilter"), new DropDown(this.scene, 0, 0, winOptions, this.updateStarters, DropDownType.MULTI));
this.filterBar.defaultWinVals = this.filterBar.getVals(DropDownColumn.WIN);
// unlocks filter
const unlocksOptions = [
new DropDownOption(this.scene, "PASSIVE", ["Passive", i18next.t("filterBar:passiveUnlocked"), i18next.t("filterBar:passiveLocked")], null, DropDownState.OFF),
];
this.filterBar.addFilter(i18next.t("filterBar:unlocksFilter"), new DropDown(this.scene, 0, 0, unlocksOptions, this.updateStarters, DropDownType.TRI));
this.filterBar.defaultUnlocksVals = this.filterBar.getVals(DropDownColumn.UNLOCKS);
// misc filter
const miscOptions = [
new DropDownOption(this.scene, "WIN", ["Win", "Win - Yes", "Win - No"], null, DropDownState.OFF),
];
this.filterBar.addFilter("Misc", new DropDown(this.scene, 0, 0, miscOptions, this.updateStarters, DropDownType.TRI));
this.filterBar.defaultMiscVals = this.filterBar.getVals(DropDownColumn.MISC);
// sort filter
const sortOptions = [
@ -1956,11 +1964,15 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
const isCaught = !!(caughtVariants & DexAttr.NON_SHINY);
const isUncaught = !isCaught && !isVariantCaught && !isVariant2Caught && !isVariant3Caught;
const isPassiveUnlocked = this.scene.gameData.starterData[container.species.speciesId].passiveAttr > 0;
const isWin = this.scene.gameData.starterData[container.species.speciesId].classicWinCount > 0;
const isNotWin = this.scene.gameData.starterData[container.species.speciesId].classicWinCount === 0;
const isUndefined = this.scene.gameData.starterData[container.species.speciesId].classicWinCount === undefined;
const fitsGen = this.filterBar.getVals(DropDownColumn.GEN).includes(container.species.generation);
const fitsType = this.filterBar.getVals(DropDownColumn.TYPES).some(type => container.species.isOfType((type as number) - 1));
const fitsShiny = this.filterBar.getVals(DropDownColumn.UNLOCKS).some(variant => {
const fitsShiny = this.filterBar.getVals(DropDownColumn.SHINY).some(variant => {
if (variant === "SHINY3") {
return isVariant3Caught;
} else if (variant === "SHINY2") {
@ -1973,22 +1985,26 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
return isUncaught;
}
});
const fitsPassive = this.filterBar.getVals(DropDownColumn.UNLOCKS).some(variant => {
if (variant === "PASSIVEUNLOCKED") {
const fitsPassive = this.filterBar.getVals(DropDownColumn.UNLOCKS).some(unlocks => {
if (unlocks.val === "PASSIVE" && unlocks.state === DropDownState.INCLUDE) {
return isPassiveUnlocked;
} else if (variant === "PASSIVELOCKED") {
} else if (unlocks.val === "PASSIVE" && unlocks.state === DropDownState.EXCLUDE) {
return !isPassiveUnlocked;
} else if (unlocks.val === "PASSIVE" && unlocks.state === DropDownState.OFF) {
return true;
}
});
const isWin = this.scene.gameData.starterData[container.species.speciesId].classicWinCount > 0;
const isNotWin = this.scene.gameData.starterData[container.species.speciesId].classicWinCount === 0;
const isUndefined = this.scene.gameData.starterData[container.species.speciesId].classicWinCount === undefined;
const fitsWin = this.filterBar.getVals(DropDownColumn.WIN).some(win => {
if (win === "WIN") {
const fitsWin = this.filterBar.getVals(DropDownColumn.MISC).some(misc => {
if (container.species.speciesId < 10) {
}
if (misc.val === "WIN" && misc.state === DropDownState.INCLUDE) {
return isWin;
} else if (win === "NOTWIN") {
} else if (misc.val === "WIN" && misc.state === DropDownState.EXCLUDE) {
return isNotWin || isUndefined;
} else if (misc.val === "WIN" && misc.state === DropDownState.OFF) {
return true;
}
});