From 5766d344a5a1c5e9c8ad0544a61d14ab78dd8e38 Mon Sep 17 00:00:00 2001 From: Oliver Salzburg Date: Tue, 22 Oct 2024 18:39:06 +0200 Subject: [PATCH] feat(core): Individual triggers for build items Contributes to #451 --- .../source/BonfireManager.ts | 3 +- .../source/helper/BulkPurchaseHelper.ts | 18 +- .../kitten-scientists/source/i18n/en-US.json | 31 +- .../source/settings/BonfireSettings.ts | 19 +- .../source/settings/ReligionSettings.ts | 16 +- .../source/settings/ResetBonfireSettings.ts | 9 +- .../source/settings/ResetReligionSettings.ts | 4 +- .../source/settings/ResetSpaceSettings.ts | 4 +- .../source/settings/ResetTimeSettings.ts | 4 +- .../source/settings/Settings.ts | 4 +- .../source/settings/SpaceSettings.ts | 11 +- .../source/settings/TimeSettings.ts | 7 +- .../source/ui/BonfireSettingsUi.ts | 287 ++++++++++-------- .../source/ui/BuildSectionTools.ts | 72 +++++ .../source/ui/InternalsUi.ts | 3 +- .../source/ui/LogFilterSettingsUi.ts | 4 +- .../source/ui/ReligionSettingsUi.ts | 86 +++++- .../source/ui/ScienceSettingsUi.ts | 4 +- .../source/ui/SettingsSectionUi.ts | 32 -- .../source/ui/SpaceSettingsUi.ts | 53 +++- .../source/ui/TimeControlSettingsUi.ts | 4 +- .../source/ui/TimeSettingsUi.ts | 76 +++-- .../source/ui/TradeSettingsUi.ts | 3 +- .../source/ui/UserInterface.ts | 18 ++ .../source/ui/VillageSettingsUi.ts | 4 +- .../source/ui/WorkshopSettingsUi.ts | 4 +- .../source/ui/components/Button.ts | 37 ++- .../source/ui/components/Dialog.ts | 30 +- .../source/ui/components/IconButton.ts | 35 ++- .../source/ui/components/Input.ts | 10 +- .../ui/components/SettingTriggerListItem.ts | 12 +- .../components/SettingTriggerMaxListItem.ts | 38 +++ .../source/ui/components/SettingsList.ts | 10 +- .../source/ui/components/TextButton.ts | 35 ++- .../source/ui/components/UiComponent.ts | 14 +- .../components/buttons-icon/ConsumeButton.ts | 14 +- .../ui/components/buttons-icon/StockButton.ts | 14 +- .../components/buttons-icon/TriggerButton.ts | 37 ++- .../ui/components/buttons-text/BuyButton.ts | 8 +- .../ui/components/buttons-text/MaxButton.ts | 8 +- .../ui/components/buttons-text/SellButton.ts | 8 +- .../buttons-text/TriggerLimitButton.ts | 6 +- 42 files changed, 753 insertions(+), 343 deletions(-) create mode 100644 packages/kitten-scientists/source/ui/BuildSectionTools.ts delete mode 100644 packages/kitten-scientists/source/ui/SettingsSectionUi.ts create mode 100644 packages/kitten-scientists/source/ui/components/SettingTriggerMaxListItem.ts diff --git a/packages/kitten-scientists/source/BonfireManager.ts b/packages/kitten-scientists/source/BonfireManager.ts index 7c78d055b..6f1fee45b 100644 --- a/packages/kitten-scientists/source/BonfireManager.ts +++ b/packages/kitten-scientists/source/BonfireManager.ts @@ -67,7 +67,6 @@ export class BonfireManager implements Automation { builds: Partial> = this.settings.buildings, ) { const bulkManager = this._bulkManager; - const trigger = this.settings.trigger; // Get the current metadata for all the referenced buildings. const metaData: Partial> = {}; @@ -78,7 +77,7 @@ export class BonfireManager implements Automation { } // Let the bulkmanager determine the builds we can make. - const buildList = bulkManager.bulk(builds, metaData, trigger, "Bonfire"); + const buildList = bulkManager.bulk(builds, metaData, this.settings.trigger, "Bonfire"); // Build all entries in the build list, where we can build any items. for (const build of buildList.filter(item => 0 < item.count)) { diff --git a/packages/kitten-scientists/source/helper/BulkPurchaseHelper.ts b/packages/kitten-scientists/source/helper/BulkPurchaseHelper.ts index 0fcae4e3a..287ae36ea 100644 --- a/packages/kitten-scientists/source/helper/BulkPurchaseHelper.ts +++ b/packages/kitten-scientists/source/helper/BulkPurchaseHelper.ts @@ -71,10 +71,10 @@ export class BulkPurchaseHelper { /** * Take a hash of potential builds and determine how many of them can be built. * - * @param builds All potential builds. - * @param metaData The metadata for the potential builds. - * @param trigger The configured trigger threshold for these builds. - * @param sourceTab The tab these builds originate from. + * @param builds - All potential builds. + * @param metaData - The metadata for the potential builds. + * @param sectionTrigger - The configured trigger threshold for the section of these builds. + * @param sourceTab - The tab these builds originate from. * @returns All the possible builds. */ bulk( @@ -88,6 +88,7 @@ export class BulkPurchaseHelper { baseBuilding?: Building; building?: AllBuildings | BonfireItem; stage?: number; + trigger: number; variant?: TimeItemVariant | UnicornItemVariant; } > @@ -104,7 +105,7 @@ export class BulkPurchaseHelper { | ZiggurathUpgradeInfo > >, - trigger: number, + sectionTrigger: number, sourceTab: TabId, ): Array { const buildsPerformed: Array = []; @@ -172,8 +173,11 @@ export class BulkPurchaseHelper { .map(price => this._workshopManager.getResource(price.name)) .filter(material => 0 < material.maxValue); const allMaterialsAboveTrigger = - requiredMaterials.filter(material => material.value / material.maxValue < trigger) - .length === 0; + requiredMaterials.filter( + material => + material.value / material.maxValue < + (build.trigger < 0 ? sectionTrigger : build.trigger), + ).length === 0; if (allMaterialsAboveTrigger) { // If the build is for a stage that the building isn't currently at, skip it. diff --git a/packages/kitten-scientists/source/i18n/en-US.json b/packages/kitten-scientists/source/i18n/en-US.json index a0607ac71..5ead99504 100644 --- a/packages/kitten-scientists/source/i18n/en-US.json +++ b/packages/kitten-scientists/source/i18n/en-US.json @@ -18,17 +18,17 @@ "act.sacrificeUnicorns": "Sacrificed {0} unicorns", "act.sun.discover": "Kittens have discovered {0}", "act.sun.discovers": "Kittens have discovered {0} {1} times", - "act.time.activeHeatTransferStart": "Start active Heat Transfer", "act.time.activeHeatTransferEnd": "End active Heat Transfer", + "act.time.activeHeatTransferStart": "Start active Heat Transfer", "act.time.getTemporalFlux": "Burn crystals to obtain time flux to maintain time acceleration", "act.time.skip": "Kittens combust a time crystal, {0} years skipped!", "act.trade": "Kittens have traded {0} times with {1}", "act.transcend": "Spent {0} epiphany, Transcended to T-level: {1}", - "blackcoin.buy": "Kittens sold your Relics and bought {0} Blackcoins", "blackcoin.buy.threshold": "Buy Blackcoins below this price:", "blackcoin.buy.trigger": "buying Blackcoins (in relics)", - "blackcoin.sell": "Kittens sold your Blackcoins and bought {0} Relics", + "blackcoin.buy": "Kittens sold your Relics and bought {0} Blackcoins", "blackcoin.sell.threshold": "Sell Blackcoins above this price:", + "blackcoin.sell": "Kittens sold your Blackcoins and bought {0} Relics", "build.embassies": "Built {0} embassies for {1}", "build.embassy": "Built {0} embassy for {1}", "copy": "Copy to clipboard", @@ -64,16 +64,16 @@ "option.catnip": "Gather Catnip", "option.chronofurnace": "Turn on Chrono Furnaces", "option.crypto": "Trade Blackcoin", - "option.elect": "Elect leader", - "option.elect.job": "Job", "option.elect.job.any": "Any", + "option.elect.job": "Job", "option.elect.trait": "Trait", + "option.elect": "Elect leader", "option.embassies": "Build Embassies", "option.faith.adore": "Adore the Galaxy", "option.faith.autoPraise": "Praise the Sun", "option.faith.best.unicorn": "Build Best Unicorn Building First", - "option.faith.refineTimeCrystals": "Refine TCs", "option.faith.refineTears": "Refine Tears", + "option.faith.refineTimeCrystals": "Refine TCs", "option.faith.sacrificeAlicorns": "Sacrifice Alicorns", "option.faith.sacrificeUnicorns": "Sacrifice Unicorns", "option.faith.transcend": "Transcend", @@ -92,7 +92,6 @@ "option.time.reset": "Reset Timeline (Danger!)", "option.time.skip.ignoreOverheat": "Ignore overheat", "option.time.skip": "Time Skip", - "reset": "Reset", "reset.after": "Nice to meet you, the cute Kitten Scientists will serve you", "reset.cancel.activity": "Meoston, We Have a Problem", "reset.cancel.message": "Timeline Reset canceled", @@ -111,6 +110,7 @@ "reset.countdown.9": "9 - Sacrificing Unicorns", "reset.last.message": "See you next poincaré recurrence", "reset.tip": "You can cancel this reset by disabling \"Enable Kitten Scientists\" at the top of the settings.", + "reset": "Reset", "resources.consume.set": "Consume rate for {0}", "resources.consume": "Consume: {0}", "resources.stock.set": "Stock for {0}", @@ -221,9 +221,10 @@ "ui.engine": "Enable Kitten Scientists", "ui.faith": "Religion", "ui.filter": "Log Filters", - "ui.internals": "Internals", - "ui.internals.interval": "Interval: {0}", + "ui.infinity": "∞", "ui.internals.interval.input": "Enter a new interval at which KS should run (in milliseconds):", + "ui.internals.interval": "Interval: {0}", + "ui.internals": "Internals", "ui.itemsHide": "Hide options", "ui.itemsShow": "Show options", "ui.ksColumn": "Display KS in fourth column in UI", @@ -241,8 +242,16 @@ "ui.time": "Time", "ui.timeCtrl": "Time Control", "ui.trade": "Trade", - "ui.trigger.setinteger": "Enter a new trigger value for {0}. Should be in the range of 0 to Infinity (-1).", - "ui.trigger.setpercentage": "Enter a new trigger value for {0}. Should be in the range of 0 to 1.", + "ui.trigger.build.blocked": "∞\n🛈 The {0} section has no trigger set. This build will not be triggered!", + "ui.trigger.build.inherited": "inherited from section", + "ui.trigger.build.prompt": "Trigger for {0} (Current: {1})", + "ui.trigger.build.promptExplainer": "If you submit an empty value, or a negative value, the section trigger is used instead. If you submit an invalid value, it will be treated as if you hit Cancel.", + "ui.trigger.prompt.percentage": "Enter the new trigger value as a percentage between 0.0 and 100.0.", + "ui.trigger.section.inactive": "∞ (nothing is built automatically)", + "ui.trigger.section.prompt": "Trigger for section {0} (Current: {1})", + "ui.trigger.section.promptExplainer": "If you submit an empty value, or a negative value, the section trigger will be set to infinity (∞), which disables all builds, unless they have their own trigger set.\nIf you submit an invalid value, it will be treated as if you hit Cancel.", + "ui.trigger.setinteger": "Enter a new trigger value for {0}. Should be in the range from 0 up to any number. -1 means infinity.", + "ui.trigger.setpercentage": "Enter a new trigger value for {0}. Should be in the range from 0 to 100.", "ui.trigger": "Trigger: {0}", "ui.upgrade.buildings": "Upgrade buildings", "ui.upgrade.missions": "Start missions", diff --git a/packages/kitten-scientists/source/settings/BonfireSettings.ts b/packages/kitten-scientists/source/settings/BonfireSettings.ts index 14d404510..11e34ca34 100644 --- a/packages/kitten-scientists/source/settings/BonfireSettings.ts +++ b/packages/kitten-scientists/source/settings/BonfireSettings.ts @@ -2,7 +2,7 @@ import { Maybe, isNil } from "@oliversalzburg/js-utils/data/nil.js"; import { consumeEntriesPedantic } from "../tools/Entries.js"; import { Building, Buildings, StagedBuilding, StagedBuildings } from "../types/index.js"; import { BuildingUpgradeSettings } from "./BuildingUpgradeSettings.js"; -import { Setting, SettingMax, SettingTrigger } from "./Settings.js"; +import { Setting, SettingTrigger, SettingTriggerMax } from "./Settings.js"; /** * One of the building options in the KS menu. @@ -11,7 +11,7 @@ import { Setting, SettingMax, SettingTrigger } from "./Settings.js"; */ export type BonfireItem = Building | StagedBuilding; -export class BonfireBuildingSetting extends SettingMax { +export class BonfireBuildingSetting extends SettingTriggerMax { /** * In case this is an upgrade of another building, this is the name of the * base building. @@ -35,8 +35,14 @@ export class BonfireBuildingSetting extends SettingMax { return this.#stage; } - constructor(building: BonfireItem, enabled = false, max = -1, baseStage?: Building | false) { - super(enabled, max); + constructor( + building: BonfireItem, + enabled = false, + trigger = -1, + max = -1, + baseStage?: Building | false, + ) { + super(enabled, trigger, max); this.#building = building; if (baseStage) { @@ -62,7 +68,7 @@ export class BonfireSettings extends SettingTrigger { constructor( enabled = false, - trigger = 0, + trigger = -1, gatherCatnip = new Setting(true), turnOnSteamworks = new Setting(true), turnOnMagnetos = new Setting(false), @@ -93,7 +99,7 @@ export class BonfireSettings extends SettingTrigger { items[item] = new BonfireBuildingSetting(item); }); StagedBuildings.forEach(item => { - items[item] = new BonfireBuildingSetting(item, false, -1, baseStage[item]); + items[item] = new BonfireBuildingSetting(item, false, -1, -1, baseStage[item]); }); return items; @@ -109,6 +115,7 @@ export class BonfireSettings extends SettingTrigger { consumeEntriesPedantic(this.buildings, settings.buildings, (building, item) => { building.enabled = item?.enabled ?? building.enabled; building.max = item?.max ?? building.max; + building.trigger = item?.trigger ?? building.trigger; }); this.gatherCatnip.enabled = settings.gatherCatnip?.enabled ?? this.gatherCatnip.enabled; diff --git a/packages/kitten-scientists/source/settings/ReligionSettings.ts b/packages/kitten-scientists/source/settings/ReligionSettings.ts index 0a0623f68..dafdb6407 100644 --- a/packages/kitten-scientists/source/settings/ReligionSettings.ts +++ b/packages/kitten-scientists/source/settings/ReligionSettings.ts @@ -9,7 +9,7 @@ import { ZiggurathUpgrade, ZiggurathUpgrades, } from "../types/index.js"; -import { Setting, SettingMax, SettingThreshold, SettingTrigger } from "./Settings.js"; +import { Setting, SettingThreshold, SettingTrigger, SettingTriggerMax } from "./Settings.js"; export type FaithItem = Exclude; @@ -38,7 +38,7 @@ export const ReligionOptions = [ ] as const; export type ReligionOption = (typeof ReligionOptions)[number]; -export class ReligionSettingsItem extends SettingMax { +export class ReligionSettingsItem extends SettingTriggerMax { readonly #building: FaithItem | UnicornItem; readonly #variant: UnicornItemVariant; @@ -49,13 +49,8 @@ export class ReligionSettingsItem extends SettingMax { return this.#variant; } - constructor( - building: FaithItem | UnicornItem, - variant: UnicornItemVariant, - enabled = false, - max = -1, - ) { - super(enabled, max); + constructor(building: FaithItem | UnicornItem, variant: UnicornItemVariant, enabled = false) { + super(enabled); this.#building = building; this.#variant = variant; } @@ -108,7 +103,7 @@ export class ReligionSettings extends SettingTrigger { constructor( enabled = false, - trigger = 1, + trigger = -1, bestUnicornBuilding = new Setting(false), sacrificeAlicorns = new SettingThreshold(false, 25), sacrificeUnicorns = new SettingThreshold(false, 1000000), @@ -164,6 +159,7 @@ export class ReligionSettings extends SettingTrigger { consumeEntriesPedantic(this.buildings, settings.buildings, (building, item) => { building.enabled = item?.enabled ?? building.enabled; building.max = item?.max ?? building.max; + building.trigger = item?.trigger ?? building.trigger; }); this.bestUnicornBuilding.load(settings.bestUnicornBuilding); diff --git a/packages/kitten-scientists/source/settings/ResetBonfireSettings.ts b/packages/kitten-scientists/source/settings/ResetBonfireSettings.ts index 8054440d7..38b43abeb 100644 --- a/packages/kitten-scientists/source/settings/ResetBonfireSettings.ts +++ b/packages/kitten-scientists/source/settings/ResetBonfireSettings.ts @@ -29,8 +29,13 @@ export class ResetBonfireBuildingSetting extends SettingThreshold { return this.#stage; } - constructor(building: BonfireItem, enabled = false, trigger = -1, baseStage?: Building | false) { - super(enabled, trigger); + constructor( + building: BonfireItem, + enabled = false, + threshold = -1, + baseStage?: Building | false, + ) { + super(enabled, threshold); this.#building = building; if (baseStage) { diff --git a/packages/kitten-scientists/source/settings/ResetReligionSettings.ts b/packages/kitten-scientists/source/settings/ResetReligionSettings.ts index bd8de299a..0e4b6cffd 100644 --- a/packages/kitten-scientists/source/settings/ResetReligionSettings.ts +++ b/packages/kitten-scientists/source/settings/ResetReligionSettings.ts @@ -24,9 +24,9 @@ export class ResetReligionBuildingSetting extends SettingThreshold { building: FaithItem | UnicornItem, variant: UnicornItemVariant, enabled = false, - trigger = -1, + threshold = -1, ) { - super(enabled, trigger); + super(enabled, threshold); this.#building = building; this.#variant = variant; } diff --git a/packages/kitten-scientists/source/settings/ResetSpaceSettings.ts b/packages/kitten-scientists/source/settings/ResetSpaceSettings.ts index 2a0111ca4..7c3350cac 100644 --- a/packages/kitten-scientists/source/settings/ResetSpaceSettings.ts +++ b/packages/kitten-scientists/source/settings/ResetSpaceSettings.ts @@ -10,8 +10,8 @@ export class ResetSpaceBuildingSetting extends SettingThreshold { return this.#building; } - constructor(building: SpaceBuilding, enabled = false, trigger = -1) { - super(enabled, trigger); + constructor(building: SpaceBuilding, enabled = false, threshold = -1) { + super(enabled, threshold); this.#building = building; } } diff --git a/packages/kitten-scientists/source/settings/ResetTimeSettings.ts b/packages/kitten-scientists/source/settings/ResetTimeSettings.ts index c9c9a21f0..42ffe501b 100644 --- a/packages/kitten-scientists/source/settings/ResetTimeSettings.ts +++ b/packages/kitten-scientists/source/settings/ResetTimeSettings.ts @@ -15,8 +15,8 @@ export class ResetTimeBuildingSetting extends SettingThreshold { return this.#variant; } - constructor(id: TimeItem, variant: TimeItemVariant, enabled = false, trigger = -1) { - super(enabled, trigger); + constructor(id: TimeItem, variant: TimeItemVariant, enabled = false, threshold = -1) { + super(enabled, threshold); this.#building = id; this.#variant = variant; } diff --git a/packages/kitten-scientists/source/settings/Settings.ts b/packages/kitten-scientists/source/settings/Settings.ts index 9db652ec3..d1fa51757 100644 --- a/packages/kitten-scientists/source/settings/Settings.ts +++ b/packages/kitten-scientists/source/settings/Settings.ts @@ -78,9 +78,9 @@ export class SettingTrigger extends Setting { export class SettingThreshold extends Setting { trigger: number; - constructor(enabled = false, trigger = 1) { + constructor(enabled = false, threshold = 1) { super(enabled); - this.trigger = trigger; + this.trigger = threshold; } load(setting: Maybe>) { diff --git a/packages/kitten-scientists/source/settings/SpaceSettings.ts b/packages/kitten-scientists/source/settings/SpaceSettings.ts index 8c2cb60e4..202fb8a43 100644 --- a/packages/kitten-scientists/source/settings/SpaceSettings.ts +++ b/packages/kitten-scientists/source/settings/SpaceSettings.ts @@ -2,17 +2,17 @@ import { Maybe, isNil } from "@oliversalzburg/js-utils/data/nil.js"; import { consumeEntriesPedantic } from "../tools/Entries.js"; import { Game, SpaceBuilding, SpaceBuildings } from "../types/index.js"; import { MissionSettings } from "./MissionSettings.js"; -import { SettingMax, SettingTrigger } from "./Settings.js"; +import { SettingTrigger, SettingTriggerMax } from "./Settings.js"; -export class SpaceBuildingSetting extends SettingMax { +export class SpaceBuildingSetting extends SettingTriggerMax { readonly #building: SpaceBuilding; get building() { return this.#building; } - constructor(building: SpaceBuilding, enabled = false) { - super(enabled); + constructor(building: SpaceBuilding) { + super(); this.#building = building; } } @@ -24,7 +24,7 @@ export class SpaceSettings extends SettingTrigger { unlockMissions: MissionSettings; - constructor(enabled = false, trigger = 0, unlockMissions = new MissionSettings()) { + constructor(enabled = false, trigger = -1, unlockMissions = new MissionSettings()) { super(enabled, trigger); this.buildings = this.initBuildings(); this.unlockMissions = unlockMissions; @@ -52,6 +52,7 @@ export class SpaceSettings extends SettingTrigger { consumeEntriesPedantic(this.buildings, settings.buildings, (building, item) => { building.enabled = item?.enabled ?? building.enabled; building.max = item?.max ?? building.max; + building.trigger = item?.trigger ?? building.trigger; }); this.unlockMissions.load(settings.unlockMissions); diff --git a/packages/kitten-scientists/source/settings/TimeSettings.ts b/packages/kitten-scientists/source/settings/TimeSettings.ts index fd22e0959..72951c318 100644 --- a/packages/kitten-scientists/source/settings/TimeSettings.ts +++ b/packages/kitten-scientists/source/settings/TimeSettings.ts @@ -7,14 +7,14 @@ import { VoidSpaceUpgrade, VoidSpaceUpgrades, } from "../types/index.js"; -import { Setting, SettingMax, SettingTrigger } from "./Settings.js"; +import { Setting, SettingTrigger, SettingTriggerMax } from "./Settings.js"; /** * The upgrades on the Time tab that we have options for. */ export type TimeItem = Exclude; -export class TimeSettingsItem extends SettingMax { +export class TimeSettingsItem extends SettingTriggerMax { readonly #building: TimeItem; readonly #variant: TimeItemVariant; @@ -42,7 +42,7 @@ export class TimeSettings extends SettingTrigger { constructor( enabled = false, - trigger = 1, + trigger = -1, fixCryochambers = new Setting(false), turnOnChronoFurnace = new Setting(false), ) { @@ -74,6 +74,7 @@ export class TimeSettings extends SettingTrigger { consumeEntriesPedantic(this.buildings, settings.buildings, (building, item) => { building.enabled = item?.enabled ?? building.enabled; building.max = item?.max ?? building.max; + building.trigger = item?.trigger ?? building.trigger; }); this.fixCryochambers.load(settings.fixCryochambers); diff --git a/packages/kitten-scientists/source/ui/BonfireSettingsUi.ts b/packages/kitten-scientists/source/ui/BonfireSettingsUi.ts index 6c6b3292c..9e01650ba 100644 --- a/packages/kitten-scientists/source/ui/BonfireSettingsUi.ts +++ b/packages/kitten-scientists/source/ui/BonfireSettingsUi.ts @@ -1,18 +1,20 @@ -import { isNil } from "@oliversalzburg/js-utils/data/nil.js"; +import { coalesceArray, isNil } from "@oliversalzburg/js-utils/data/nil.js"; +import { redirectErrorsToConsole } from "@oliversalzburg/js-utils/errors/console.js"; import { KittenScientists } from "../KittenScientists.js"; import { BonfireSettings } from "../settings/BonfireSettings.js"; -import { StagedBuilding } from "../types/index.js"; +import { Building, StagedBuilding } from "../types/index.js"; +import { BuildSectionTools } from "./BuildSectionTools.js"; import { BuildingUpgradeSettingsUi } from "./BuildingUpgradeSettingsUi.js"; -import { AbstractBuildSettingsPanel } from "./SettingsSectionUi.js"; +import { Delimiter } from "./components/Delimiter.js"; +import { Dialog } from "./components/Dialog.js"; import { HeaderListItem } from "./components/HeaderListItem.js"; import { SettingListItem } from "./components/SettingListItem.js"; -import { SettingMaxListItem } from "./components/SettingMaxListItem.js"; import { SettingTriggerListItem } from "./components/SettingTriggerListItem.js"; import { SettingsList } from "./components/SettingsList.js"; +import { SettingsPanel } from "./components/SettingsPanel.js"; +import { UiComponent } from "./components/UiComponent.js"; -export class BonfireSettingsUi extends AbstractBuildSettingsPanel { - private readonly _buildings: Array; - +export class BonfireSettingsUi extends SettingsPanel { constructor(host: KittenScientists, settings: BonfireSettings) { const label = host.engine.i18n("ui.build"); super( @@ -25,137 +27,162 @@ export class BonfireSettingsUi extends AbstractBuildSettingsPanel { host.engine.imessage("status.auto.disable", [label]); }, - }), - ); - - this._buildings = []; - for (const buildingGroup of this._host.game.bld.buildingGroups) { - this._buildings.push(new HeaderListItem(this._host, buildingGroup.title)); - for (const building of buildingGroup.buildings) { - if (building === "unicornPasture" || isNil(this.setting.buildings[building])) { - continue; - } - - const meta = this._host.game.bld.getBuildingExt(building).meta; - if (!isNil(meta.stages)) { - const name = Object.values(this.setting.buildings).find( - item => item.baseBuilding === building, - )?.building as StagedBuilding; - this._buildings.push( - this._getBuildOption(this.setting.buildings[building], meta.stages[0].label), - this._getBuildOption(this.setting.buildings[name], meta.stages[1].label, false, true), - ); - } else if (!isNil(meta.label)) { - this._buildings.push(this._getBuildOption(this.setting.buildings[building], meta.label)); - } - } - - // Add padding after each group. Except for the last group, which ends the list. - if ( - buildingGroup !== - this._host.game.bld.buildingGroups[this._host.game.bld.buildingGroups.length - 1] - ) { - this._buildings.at(-1)?.element.addClass("ks-delimiter"); - } - } - - const listBuildings = new SettingsList(this._host, { - children: this._buildings, - onReset: () => { - this.setting.load({ buildings: new BonfireSettings().buildings }); - this.refreshUi(); - }, - }); - this.addChild(listBuildings); - - const listAddition = new SettingsList(this._host, { - hasDisableAll: false, - hasEnableAll: false, - }); - listAddition.addChild(new HeaderListItem(this._host, this._host.engine.i18n("ui.additional"))); + onRefresh: item => { + (item as SettingTriggerListItem).triggerButton.inactive = settings.trigger < 0; + }, + onRefreshTrigger: item => { + item.triggerButton.element[0].title = host.engine.i18n("ui.trigger", [ + settings.trigger < 0 + ? host.engine.i18n("ui.trigger.section.inactive") + : `${UiComponent.renderPercentage(settings.trigger)}%`, + ]); + }, + onSetTrigger: () => { + Dialog.prompt( + host, + host.engine.i18n("ui.trigger.prompt.percentage"), + host.engine.i18n("ui.trigger.section.prompt", [ + label, + settings.trigger !== -1 + ? `${UiComponent.renderPercentage(settings.trigger)}%` + : host.engine.i18n("ui.infinity"), + ]), + settings.trigger !== -1 ? UiComponent.renderPercentage(settings.trigger) : "", + host.engine.i18n("ui.trigger.section.promptExplainer"), + ) + .then(value => { + if (value === undefined) { + return; + } - listAddition.addChild(new BuildingUpgradeSettingsUi(this._host, this.setting.upgradeBuildings)); + if (value === "" || value.startsWith("-")) { + settings.trigger = -1; + return; + } - listAddition.addChild( - new SettingListItem( - this._host, - this._host.engine.i18n("option.catnip"), - this.setting.gatherCatnip, - { - onCheck: () => { - this._host.engine.imessage("status.sub.enable", [ - this._host.engine.i18n("option.catnip"), - ]); - }, - onUnCheck: () => { - this._host.engine.imessage("status.sub.disable", [ - this._host.engine.i18n("option.catnip"), - ]); - }, + settings.trigger = UiComponent.parsePercentage(value); + }) + .then(() => { + this.refreshUi(); + }) + .catch(redirectErrorsToConsole(console)); }, - ), + }), ); - listAddition.addChild( - new SettingListItem( - this._host, - this._host.engine.i18n("option.steamworks"), - this.setting.turnOnSteamworks, - { - onCheck: () => { - this._host.engine.imessage("status.sub.enable", [ - this._host.engine.i18n("option.steamworks"), - ]); - }, - onUnCheck: () => { - this._host.engine.imessage("status.sub.disable", [ - this._host.engine.i18n("option.steamworks"), - ]); - }, + // Post-super() child insertion, so we can use this._getBuildOptions(). + // We want the ability to use `this` in our callbacks, to construct more complex + // usage scenarios where we need access to the entire UI section. + this.addChildren([ + new SettingsList(host, { + children: coalesceArray( + host.game.bld.buildingGroups.flatMap(buildingGroup => [ + new HeaderListItem(host, buildingGroup.title), + ...buildingGroup.buildings.flatMap(building => + this._getBuildOptions(host, settings, label, building), + ), + buildingGroup !== host.game.bld.buildingGroups[host.game.bld.buildingGroups.length - 1] + ? new Delimiter(host) + : undefined, + ]), + ), + onReset: () => { + this.setting.load({ buildings: new BonfireSettings().buildings }); + this.refreshUi(); }, - ), - ); + }), + new SettingsList(host, { + children: [ + new HeaderListItem(host, host.engine.i18n("ui.additional")), + new BuildingUpgradeSettingsUi(host, settings.upgradeBuildings), + new SettingListItem(host, host.engine.i18n("option.catnip"), settings.gatherCatnip, { + onCheck: () => { + host.engine.imessage("status.sub.enable", [host.engine.i18n("option.catnip")]); + }, + onUnCheck: () => { + host.engine.imessage("status.sub.disable", [host.engine.i18n("option.catnip")]); + }, + }), + new SettingListItem( + host, + host.engine.i18n("option.steamworks"), + settings.turnOnSteamworks, + { + onCheck: () => { + host.engine.imessage("status.sub.enable", [host.engine.i18n("option.steamworks")]); + }, + onUnCheck: () => { + host.engine.imessage("status.sub.disable", [host.engine.i18n("option.steamworks")]); + }, + }, + ), + new SettingListItem(host, host.engine.i18n("option.magnetos"), settings.turnOnMagnetos, { + onCheck: () => { + host.engine.imessage("status.sub.enable", [host.engine.i18n("option.magnetos")]); + }, + onUnCheck: () => { + host.engine.imessage("status.sub.disable", [host.engine.i18n("option.magnetos")]); + }, + }), + new SettingListItem(host, host.engine.i18n("option.reactors"), settings.turnOnReactors, { + onCheck: () => { + host.engine.imessage("status.sub.enable", [host.engine.i18n("option.reactors")]); + }, + onUnCheck: () => { + host.engine.imessage("status.sub.disable", [host.engine.i18n("option.reactors")]); + }, + }), + ], + hasDisableAll: false, + hasEnableAll: false, + }), + ]); + } - listAddition.addChild( - new SettingListItem( - this._host, - this._host.engine.i18n("option.magnetos"), - this.setting.turnOnMagnetos, - { - onCheck: () => { - this._host.engine.imessage("status.sub.enable", [ - this._host.engine.i18n("option.magnetos"), - ]); - }, - onUnCheck: () => { - this._host.engine.imessage("status.sub.disable", [ - this._host.engine.i18n("option.magnetos"), - ]); - }, - }, - ), - ); + private _getBuildOptions( + host: KittenScientists, + settings: BonfireSettings, + sectionLabel: string, + building: Building, + ) { + if (building === "unicornPasture" || isNil(settings.buildings[building])) { + return []; + } - listAddition.addChild( - new SettingListItem( - this._host, - this._host.engine.i18n("option.reactors"), - this.setting.turnOnReactors, - { - onCheck: () => { - this._host.engine.imessage("status.sub.enable", [ - this._host.engine.i18n("option.reactors"), - ]); - }, - onUnCheck: () => { - this._host.engine.imessage("status.sub.disable", [ - this._host.engine.i18n("option.reactors"), - ]); - }, - }, - ), - ); + const meta = host.game.bld.getBuildingExt(building).meta; + if (!isNil(meta.stages)) { + const name = Object.values(settings.buildings).find(item => item.baseBuilding === building) + ?.building as StagedBuilding; + return [ + BuildSectionTools.getBuildOption( + host, + settings.buildings[building], + settings, + meta.stages[0].label, + sectionLabel, + ), + BuildSectionTools.getBuildOption( + host, + settings.buildings[name], + settings, + meta.stages[1].label, + sectionLabel, + false, + true, + ), + ]; + } else if (!isNil(meta.label)) { + return [ + BuildSectionTools.getBuildOption( + host, + settings.buildings[building], + settings, + meta.label, + sectionLabel, + ), + ]; + } - this.addChild(listAddition); + return []; } } diff --git a/packages/kitten-scientists/source/ui/BuildSectionTools.ts b/packages/kitten-scientists/source/ui/BuildSectionTools.ts new file mode 100644 index 000000000..9a909c04a --- /dev/null +++ b/packages/kitten-scientists/source/ui/BuildSectionTools.ts @@ -0,0 +1,72 @@ +import { redirectErrorsToConsole } from "@oliversalzburg/js-utils/errors/console.js"; +import { KittenScientists } from "../KittenScientists.js"; +import { SettingTrigger, SettingTriggerMax } from "../settings/Settings.js"; +import { Dialog } from "./components/Dialog.js"; +import { SettingTriggerMaxListItem } from "./components/SettingTriggerMaxListItem.js"; +import { UiComponent } from "./components/UiComponent.js"; + +export const BuildSectionTools = { + getBuildOption: ( + host: KittenScientists, + option: SettingTriggerMax, + sectionSetting: SettingTrigger, + label: string, + sectionLabel: string, + delimiter = false, + upgradeIndicator = false, + ) => { + const buildOption = new SettingTriggerMaxListItem(host, label, option, { + delimiter, + onCheck: () => { + host.engine.imessage("status.sub.enable", [label]); + }, + onUnCheck: () => { + host.engine.imessage("status.sub.disable", [label]); + }, + onRefresh: () => { + buildOption.triggerButton.inactive = option.trigger === -1; + }, + onRefreshTrigger: () => { + buildOption.triggerButton.element[0].title = host.engine.i18n("ui.trigger", [ + option.trigger < 0 + ? sectionSetting.trigger < 0 + ? host.engine.i18n("ui.trigger.build.blocked", [sectionLabel]) + : `${UiComponent.renderPercentage(sectionSetting.trigger)}% (${host.engine.i18n("ui.trigger.build.inherited")})` + : `${UiComponent.renderPercentage(option.trigger)}%`, + ]); + }, + onSetTrigger: () => { + Dialog.prompt( + host, + host.engine.i18n("ui.trigger.prompt.percentage"), + host.engine.i18n("ui.trigger.section.prompt", [ + label, + option.trigger !== -1 + ? `${Dialog.renderPercentage(option.trigger)}%` + : host.engine.i18n("ui.trigger.build.inherited"), + ]), + option.trigger !== -1 ? Dialog.renderPercentage(option.trigger) : "", + host.engine.i18n("ui.trigger.build.promptExplainer"), + ) + .then(value => { + if (value === undefined) { + return; + } + + if (value === "" || value.startsWith("-")) { + option.trigger = -1; + return; + } + + option.trigger = UiComponent.parsePercentage(value); + }) + .then(() => { + buildOption.refreshUi(); + }) + .catch(redirectErrorsToConsole(console)); + }, + upgradeIndicator, + }); + return buildOption; + }, +}; diff --git a/packages/kitten-scientists/source/ui/InternalsUi.ts b/packages/kitten-scientists/source/ui/InternalsUi.ts index 25b83caf6..649d0b093 100644 --- a/packages/kitten-scientists/source/ui/InternalsUi.ts +++ b/packages/kitten-scientists/source/ui/InternalsUi.ts @@ -2,7 +2,6 @@ import { isNil } from "@oliversalzburg/js-utils/data/nil.js"; import { KittenScientists, ksVersion } from "../KittenScientists.js"; import { Icons } from "../images/Icons.js"; import { EngineSettings } from "../settings/EngineSettings.js"; -import { AbstractBuildSettingsPanel } from "./SettingsSectionUi.js"; import { ButtonListItem } from "./components/ButtonListItem.js"; import { Delimiter } from "./components/Delimiter.js"; import { LabelListItem } from "./components/LabelListItem.js"; @@ -32,7 +31,7 @@ export class InternalsUi extends SettingsPanel { host.engine.i18n("ui.internals.interval", [settings.interval]), { onClick: () => { - const newInterval = AbstractBuildSettingsPanel.promptLimit( + const newInterval = UiComponent.promptLimit( host.engine.i18n("ui.internals.interval.input"), settings.interval.toString(), ); diff --git a/packages/kitten-scientists/source/ui/LogFilterSettingsUi.ts b/packages/kitten-scientists/source/ui/LogFilterSettingsUi.ts index dd31e95ef..4adc44175 100644 --- a/packages/kitten-scientists/source/ui/LogFilterSettingsUi.ts +++ b/packages/kitten-scientists/source/ui/LogFilterSettingsUi.ts @@ -3,9 +3,9 @@ import { FilterItems, LogFilterSettings } from "../settings/LogFilterSettings.js import { ExplainerListItem } from "./components/ExplainerListItem.js"; import { SettingListItem } from "./components/SettingListItem.js"; import { SettingsList } from "./components/SettingsList.js"; -import { AbstractBuildSettingsPanel } from "./SettingsSectionUi.js"; +import { SettingsPanel } from "./components/SettingsPanel.js"; -export class LogFiltersSettingsUi extends AbstractBuildSettingsPanel { +export class LogFiltersSettingsUi extends SettingsPanel { constructor(host: KittenScientists, settings: LogFilterSettings) { const label = host.engine.i18n("ui.filter"); super( diff --git a/packages/kitten-scientists/source/ui/ReligionSettingsUi.ts b/packages/kitten-scientists/source/ui/ReligionSettingsUi.ts index 9d624b7d0..3a3d43115 100644 --- a/packages/kitten-scientists/source/ui/ReligionSettingsUi.ts +++ b/packages/kitten-scientists/source/ui/ReligionSettingsUi.ts @@ -1,17 +1,21 @@ import { isNil } from "@oliversalzburg/js-utils/data/nil.js"; +import { redirectErrorsToConsole } from "@oliversalzburg/js-utils/errors/console.js"; import { KittenScientists } from "../KittenScientists.js"; import { ReligionOptions, ReligionSettings, UnicornItems } from "../settings/ReligionSettings.js"; import { ZiggurathUpgrade } from "../types/index.js"; -import { AbstractBuildSettingsPanel } from "./SettingsSectionUi.js"; +import { BuildSectionTools } from "./BuildSectionTools.js"; import { Delimiter } from "./components/Delimiter.js"; +import { Dialog } from "./components/Dialog.js"; import { HeaderListItem } from "./components/HeaderListItem.js"; import { SettingListItem } from "./components/SettingListItem.js"; -import { SettingMaxListItem } from "./components/SettingMaxListItem.js"; import { SettingTriggerListItem } from "./components/SettingTriggerListItem.js"; +import { SettingTriggerMaxListItem } from "./components/SettingTriggerMaxListItem.js"; import { SettingsList } from "./components/SettingsList.js"; +import { SettingsPanel } from "./components/SettingsPanel.js"; +import { UiComponent } from "./components/UiComponent.js"; -export class ReligionSettingsUi extends AbstractBuildSettingsPanel { - private readonly _unicornBuildings: Array; +export class ReligionSettingsUi extends SettingsPanel { + private readonly _unicornBuildings: Array; private readonly _bestUnicornBuilding: SettingListItem; constructor(host: KittenScientists, settings: ReligionSettings) { @@ -26,6 +30,46 @@ export class ReligionSettingsUi extends AbstractBuildSettingsPanel { host.engine.imessage("status.auto.disable", [label]); }, + onRefresh: item => { + (item as SettingTriggerListItem).triggerButton.inactive = settings.trigger < 0; + }, + onRefreshTrigger: item => { + item.triggerButton.element[0].title = host.engine.i18n("ui.trigger", [ + settings.trigger < 0 + ? host.engine.i18n("ui.trigger.section.inactive") + : `${UiComponent.renderPercentage(settings.trigger)}%`, + ]); + }, + onSetTrigger: () => { + Dialog.prompt( + host, + host.engine.i18n("ui.trigger.prompt.percentage"), + host.engine.i18n("ui.trigger.section.prompt", [ + label, + settings.trigger !== -1 + ? `${UiComponent.renderPercentage(settings.trigger)}%` + : host.engine.i18n("ui.infinity"), + ]), + settings.trigger !== -1 ? UiComponent.renderPercentage(settings.trigger) : "", + host.engine.i18n("ui.trigger.section.promptExplainer"), + ) + .then(value => { + if (value === undefined) { + return; + } + + if (value === "" || value.startsWith("-")) { + settings.trigger = -1; + return; + } + + settings.trigger = UiComponent.parsePercentage(value); + }) + .then(() => { + this.refreshUi(); + }) + .catch(redirectErrorsToConsole(console)); + }, }), ); @@ -36,7 +80,13 @@ export class ReligionSettingsUi extends AbstractBuildSettingsPanel unicornsArray.includes(item.name) && !isNil(this.setting.buildings[item.name]), ) .map(zigguratUpgrade => - this._getBuildOption(this.setting.buildings[zigguratUpgrade.name], zigguratUpgrade.label), + BuildSectionTools.getBuildOption( + host, + this.setting.buildings[zigguratUpgrade.name], + this.setting, + zigguratUpgrade.label, + label, + ), ); this._bestUnicornBuilding = new SettingListItem( @@ -68,9 +118,12 @@ export class ReligionSettingsUi extends AbstractBuildSettingsPanel - this._getBuildOption(this.setting.buildings[upgrade.name], upgrade.label), + BuildSectionTools.getBuildOption( + host, + this.setting.buildings[upgrade.name], + this.setting, + upgrade.label, + label, + ), ), new Delimiter(host), @@ -91,9 +150,12 @@ export class ReligionSettingsUi extends AbstractBuildSettingsPanel !isNil(this.setting.buildings[item.name])) .map(upgrade => - this._getBuildOption( + BuildSectionTools.getBuildOption( + host, this.setting.buildings[upgrade.name], + this.setting, upgrade.label, + label, upgrade.name === host.game.religion.religionUpgrades.at(-1)?.name, ), ), @@ -102,7 +164,13 @@ export class ReligionSettingsUi extends AbstractBuildSettingsPanel !isNil(this.setting.buildings[item.name])) .map(upgrade => - this._getBuildOption(this.setting.buildings[upgrade.name], upgrade.label), + BuildSectionTools.getBuildOption( + host, + this.setting.buildings[upgrade.name], + this.setting, + upgrade.label, + label, + ), ), ], onEnableAll: () => { diff --git a/packages/kitten-scientists/source/ui/ScienceSettingsUi.ts b/packages/kitten-scientists/source/ui/ScienceSettingsUi.ts index d544301a9..869f4d9ff 100644 --- a/packages/kitten-scientists/source/ui/ScienceSettingsUi.ts +++ b/packages/kitten-scientists/source/ui/ScienceSettingsUi.ts @@ -3,12 +3,12 @@ import { KittenScientists } from "../KittenScientists.js"; import { ScienceSettings } from "../settings/ScienceSettings.js"; import { SettingOptions } from "../settings/Settings.js"; import { PolicySettingsUi } from "./PolicySettingsUi.js"; -import { AbstractBuildSettingsPanel } from "./SettingsSectionUi.js"; import { TechSettingsUi } from "./TechSettingsUi.js"; import { SettingListItem } from "./components/SettingListItem.js"; import { SettingsList } from "./components/SettingsList.js"; +import { SettingsPanel } from "./components/SettingsPanel.js"; -export class ScienceSettingsUi extends AbstractBuildSettingsPanel { +export class ScienceSettingsUi extends SettingsPanel { private readonly _items: Array; private readonly _policiesUi: PolicySettingsUi; private readonly _techsUi: TechSettingsUi; diff --git a/packages/kitten-scientists/source/ui/SettingsSectionUi.ts b/packages/kitten-scientists/source/ui/SettingsSectionUi.ts deleted file mode 100644 index 4dc52f775..000000000 --- a/packages/kitten-scientists/source/ui/SettingsSectionUi.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { BonfireBuildingSetting } from "../settings/BonfireSettings.js"; -import { ReligionSettingsItem } from "../settings/ReligionSettings.js"; -import { Setting, SettingMax } from "../settings/Settings.js"; -import { SettingMaxListItem } from "./components/SettingMaxListItem.js"; -import { SettingsPanel } from "./components/SettingsPanel.js"; - -/** - * Base class for UI sections. - * This exists mostly for historic reasons. New implementations likely want to use - * `SettingsPanel` or `Panel` directly. - */ -export abstract class AbstractBuildSettingsPanel< - TSetting extends Setting = Setting, -> extends SettingsPanel { - protected _getBuildOption( - option: BonfireBuildingSetting | ReligionSettingsItem | SettingMax, - label: string, - delimiter = false, - upgradeIndicator = false, - ) { - return new SettingMaxListItem(this._host, label, option, { - delimiter, - onCheck: () => { - this._host.engine.imessage("status.sub.enable", [label]); - }, - onUnCheck: () => { - this._host.engine.imessage("status.sub.disable", [label]); - }, - upgradeIndicator, - }); - } -} diff --git a/packages/kitten-scientists/source/ui/SpaceSettingsUi.ts b/packages/kitten-scientists/source/ui/SpaceSettingsUi.ts index e57772922..b38a211d7 100644 --- a/packages/kitten-scientists/source/ui/SpaceSettingsUi.ts +++ b/packages/kitten-scientists/source/ui/SpaceSettingsUi.ts @@ -1,13 +1,17 @@ import { isNil } from "@oliversalzburg/js-utils/data/nil.js"; +import { redirectErrorsToConsole } from "@oliversalzburg/js-utils/errors/console.js"; import { KittenScientists } from "../KittenScientists.js"; import { SpaceSettings } from "../settings/SpaceSettings.js"; +import { BuildSectionTools } from "./BuildSectionTools.js"; import { MissionSettingsUi } from "./MissionSettingsUi.js"; -import { AbstractBuildSettingsPanel } from "./SettingsSectionUi.js"; +import { Dialog } from "./components/Dialog.js"; import { HeaderListItem } from "./components/HeaderListItem.js"; import { SettingTriggerListItem } from "./components/SettingTriggerListItem.js"; import { SettingsList } from "./components/SettingsList.js"; +import { SettingsPanel } from "./components/SettingsPanel.js"; +import { UiComponent } from "./components/UiComponent.js"; -export class SpaceSettingsUi extends AbstractBuildSettingsPanel { +export class SpaceSettingsUi extends SettingsPanel { private readonly _missionsUi: MissionSettingsUi; constructor(host: KittenScientists, settings: SpaceSettings) { @@ -22,6 +26,46 @@ export class SpaceSettingsUi extends AbstractBuildSettingsPanel { onUnCheck: () => { host.engine.imessage("status.auto.disable", [label]); }, + onRefresh: item => { + (item as SettingTriggerListItem).triggerButton.inactive = settings.trigger < 0; + }, + onRefreshTrigger: item => { + item.triggerButton.element[0].title = host.engine.i18n("ui.trigger", [ + settings.trigger < 0 + ? host.engine.i18n("ui.trigger.section.inactive") + : `${UiComponent.renderPercentage(settings.trigger)}%`, + ]); + }, + onSetTrigger: () => { + Dialog.prompt( + host, + host.engine.i18n("ui.trigger.prompt.percentage"), + host.engine.i18n("ui.trigger.section.prompt", [ + label, + settings.trigger !== -1 + ? `${UiComponent.renderPercentage(settings.trigger)}%` + : host.engine.i18n("ui.infinity"), + ]), + settings.trigger !== -1 ? UiComponent.renderPercentage(settings.trigger) : "", + host.engine.i18n("ui.trigger.section.promptExplainer"), + ) + .then(value => { + if (value === undefined) { + return; + } + + if (value === "" || value.startsWith("-")) { + settings.trigger = -1; + return; + } + + settings.trigger = UiComponent.parsePercentage(value); + }) + .then(() => { + this.refreshUi(); + }) + .catch(redirectErrorsToConsole(console)); + }, }), ); @@ -34,9 +78,12 @@ export class SpaceSettingsUi extends AbstractBuildSettingsPanel { ...planet.buildings .filter(item => !isNil(this.setting.buildings[item.name])) .map((building, indexBuilding, arrayBuilding) => - this._getBuildOption( + BuildSectionTools.getBuildOption( + host, this.setting.buildings[building.name], + this.setting, building.label, + label, indexPlanet < arrayPlant.length - 1 && indexBuilding === arrayBuilding.length - 1, ), ), diff --git a/packages/kitten-scientists/source/ui/TimeControlSettingsUi.ts b/packages/kitten-scientists/source/ui/TimeControlSettingsUi.ts index cb65d9277..2d9e06305 100644 --- a/packages/kitten-scientists/source/ui/TimeControlSettingsUi.ts +++ b/packages/kitten-scientists/source/ui/TimeControlSettingsUi.ts @@ -5,12 +5,12 @@ import { TimeControlSettings } from "../settings/TimeControlSettings.js"; import { PaddingButton } from "./components/buttons-icon/PaddingButton.js"; import { SettingListItem } from "./components/SettingListItem.js"; import { SettingsList } from "./components/SettingsList.js"; +import { SettingsPanel } from "./components/SettingsPanel.js"; import { SettingTriggerListItem } from "./components/SettingTriggerListItem.js"; import { ResetSettingsUi } from "./ResetSettingsUi.js"; -import { AbstractBuildSettingsPanel } from "./SettingsSectionUi.js"; import { TimeSkipSettingsUi } from "./TimeSkipSettingsUi.js"; -export class TimeControlSettingsUi extends AbstractBuildSettingsPanel { +export class TimeControlSettingsUi extends SettingsPanel { protected readonly _items: Array; private readonly _accelerateTime: SettingTriggerListItem; diff --git a/packages/kitten-scientists/source/ui/TimeSettingsUi.ts b/packages/kitten-scientists/source/ui/TimeSettingsUi.ts index 4aa6b8d85..d4918c2ec 100644 --- a/packages/kitten-scientists/source/ui/TimeSettingsUi.ts +++ b/packages/kitten-scientists/source/ui/TimeSettingsUi.ts @@ -1,14 +1,17 @@ import { isNil } from "@oliversalzburg/js-utils/data/nil.js"; +import { redirectErrorsToConsole } from "@oliversalzburg/js-utils/errors/console.js"; import { KittenScientists } from "../KittenScientists.js"; -import { TimeSettings, TimeSettingsItem } from "../settings/TimeSettings.js"; -import { AbstractBuildSettingsPanel } from "./SettingsSectionUi.js"; +import { TimeSettings } from "../settings/TimeSettings.js"; +import { BuildSectionTools } from "./BuildSectionTools.js"; +import { Dialog } from "./components/Dialog.js"; import { HeaderListItem } from "./components/HeaderListItem.js"; import { SettingListItem } from "./components/SettingListItem.js"; -import { SettingMaxListItem } from "./components/SettingMaxListItem.js"; import { SettingTriggerListItem } from "./components/SettingTriggerListItem.js"; import { SettingsList } from "./components/SettingsList.js"; +import { SettingsPanel } from "./components/SettingsPanel.js"; +import { UiComponent } from "./components/UiComponent.js"; -export class TimeSettingsUi extends AbstractBuildSettingsPanel { +export class TimeSettingsUi extends SettingsPanel { constructor(host: KittenScientists, settings: TimeSettings) { const label = host.engine.i18n("ui.time"); super( @@ -21,6 +24,46 @@ export class TimeSettingsUi extends AbstractBuildSettingsPanel { onUnCheck: () => { host.engine.imessage("status.auto.disable", [label]); }, + onRefresh: item => { + (item as SettingTriggerListItem).triggerButton.inactive = settings.trigger < 0; + }, + onRefreshTrigger: item => { + item.triggerButton.element[0].title = host.engine.i18n("ui.trigger", [ + settings.trigger < 0 + ? host.engine.i18n("ui.trigger.section.inactive") + : `${UiComponent.renderPercentage(settings.trigger)}%`, + ]); + }, + onSetTrigger: () => { + Dialog.prompt( + host, + host.engine.i18n("ui.trigger.prompt.percentage"), + host.engine.i18n("ui.trigger.section.prompt", [ + label, + settings.trigger !== -1 + ? `${UiComponent.renderPercentage(settings.trigger)}%` + : host.engine.i18n("ui.infinity"), + ]), + settings.trigger !== -1 ? UiComponent.renderPercentage(settings.trigger) : "", + host.engine.i18n("ui.trigger.section.promptExplainer"), + ) + .then(value => { + if (value === undefined) { + return; + } + + if (value === "" || value.startsWith("-")) { + settings.trigger = -1; + return; + } + + settings.trigger = UiComponent.parsePercentage(value); + }) + .then(() => { + this.refreshUi(); + }) + .catch(redirectErrorsToConsole(console)); + }, }), ); @@ -31,9 +74,12 @@ export class TimeSettingsUi extends AbstractBuildSettingsPanel { ...this._host.game.time.chronoforgeUpgrades .filter(item => !isNil(this.setting.buildings[item.name])) .map(building => - this._getTimeSetting( + BuildSectionTools.getBuildOption( + host, this.setting.buildings[building.name], + this.setting, building.label, + label, building.name === this._host.game.time.chronoforgeUpgrades.at(-1)?.name, ), ), @@ -42,7 +88,13 @@ export class TimeSettingsUi extends AbstractBuildSettingsPanel { ...this._host.game.time.voidspaceUpgrades .filter(item => !isNil(this.setting.buildings[item.name])) .map(building => - this._getTimeSetting(this.setting.buildings[building.name], building.label), + BuildSectionTools.getBuildOption( + host, + this.setting.buildings[building.name], + this.setting, + building.label, + label, + ), ), ], }), @@ -90,16 +142,4 @@ export class TimeSettingsUi extends AbstractBuildSettingsPanel { }), ]); } - - private _getTimeSetting(setting: TimeSettingsItem, label: string, delimiter = false) { - return new SettingMaxListItem(this._host, label, setting, { - delimiter, - onCheck: () => { - this._host.engine.imessage("status.sub.enable", [label]); - }, - onUnCheck: () => { - this._host.engine.imessage("status.sub.disable", [label]); - }, - }); - } } diff --git a/packages/kitten-scientists/source/ui/TradeSettingsUi.ts b/packages/kitten-scientists/source/ui/TradeSettingsUi.ts index aa11216dd..ae8897942 100644 --- a/packages/kitten-scientists/source/ui/TradeSettingsUi.ts +++ b/packages/kitten-scientists/source/ui/TradeSettingsUi.ts @@ -13,9 +13,8 @@ import { SettingsList } from "./components/SettingsList.js"; import { SettingsPanel } from "./components/SettingsPanel.js"; import { SettingTriggerListItem } from "./components/SettingTriggerListItem.js"; import { EmbassySettingsUi } from "./EmbassySettingsUi.js"; -import { AbstractBuildSettingsPanel } from "./SettingsSectionUi.js"; -export class TradeSettingsUi extends AbstractBuildSettingsPanel { +export class TradeSettingsUi extends SettingsPanel { constructor(host: KittenScientists, settings: TradeSettings) { const label = host.engine.i18n("ui.trade"); super( diff --git a/packages/kitten-scientists/source/ui/UserInterface.ts b/packages/kitten-scientists/source/ui/UserInterface.ts index e1423345e..896bc1562 100644 --- a/packages/kitten-scientists/source/ui/UserInterface.ts +++ b/packages/kitten-scientists/source/ui/UserInterface.ts @@ -206,6 +206,8 @@ export class UserInterface extends UiComponent { ); this._addRule( `.ks-head .ks-label { + align-items: flex-start; + display: flex; flex: 1; white-space: nowrap; }`, @@ -335,6 +337,12 @@ export class UserInterface extends UiComponent { padding-right: 3px; line-height: 0; opacity: 0.8; + transition: .3s; + }`, + ); + this._addRule( + `.ks-icon-button.ks-inactive { + opacity: 0.2; }`, ); this._addRule( @@ -342,6 +350,11 @@ export class UserInterface extends UiComponent { opacity: 1; }`, ); + this._addRule( + `.ks-icon-button.ks-inactive:hover { + opacity: 0.4; + }`, + ); this._addRule( `.ks-icon-label { display: inline-block; @@ -392,6 +405,11 @@ export class UserInterface extends UiComponent { white-space: break-spaces; }`, ); + this._addRule( + `.ks-explainer p { + margin: 0; + }`, + ); // Setting: List this._addRule( diff --git a/packages/kitten-scientists/source/ui/VillageSettingsUi.ts b/packages/kitten-scientists/source/ui/VillageSettingsUi.ts index 5757e7e4e..b389951ff 100644 --- a/packages/kitten-scientists/source/ui/VillageSettingsUi.ts +++ b/packages/kitten-scientists/source/ui/VillageSettingsUi.ts @@ -2,15 +2,15 @@ import { isNil } from "@oliversalzburg/js-utils/data/nil.js"; import { KittenScientists } from "../KittenScientists.js"; import { SettingMax } from "../settings/Settings.js"; import { VillageSettings } from "../settings/VillageSettings.js"; -import { AbstractBuildSettingsPanel } from "./SettingsSectionUi.js"; import { HeaderListItem } from "./components/HeaderListItem.js"; import { OptionsListItem } from "./components/OptionsListItem.js"; import { SettingListItem } from "./components/SettingListItem.js"; import { SettingMaxListItem } from "./components/SettingMaxListItem.js"; import { SettingTriggerListItem } from "./components/SettingTriggerListItem.js"; import { SettingsList } from "./components/SettingsList.js"; +import { SettingsPanel } from "./components/SettingsPanel.js"; -export class VillageSettingsUi extends AbstractBuildSettingsPanel { +export class VillageSettingsUi extends SettingsPanel { private readonly _hunt: SettingTriggerListItem; private readonly _festivals: SettingListItem; private readonly _promoteKittens: SettingTriggerListItem; diff --git a/packages/kitten-scientists/source/ui/WorkshopSettingsUi.ts b/packages/kitten-scientists/source/ui/WorkshopSettingsUi.ts index 37d13fd92..86d67f6bf 100644 --- a/packages/kitten-scientists/source/ui/WorkshopSettingsUi.ts +++ b/packages/kitten-scientists/source/ui/WorkshopSettingsUi.ts @@ -5,14 +5,14 @@ import { SettingOptions } from "../settings/Settings.js"; import { CraftSettingsItem, WorkshopSettings } from "../settings/WorkshopSettings.js"; import { ucfirst } from "../tools/Format.js"; import { ResourceCraftable } from "../types/index.js"; -import { AbstractBuildSettingsPanel } from "./SettingsSectionUi.js"; import { UpgradeSettingsUi } from "./UpgradeSettingsUi.js"; import { SettingLimitedMaxListItem } from "./components/SettingLimitedMaxListItem.js"; import { SettingListItem } from "./components/SettingListItem.js"; import { SettingTriggerListItem } from "./components/SettingTriggerListItem.js"; import { SettingsList } from "./components/SettingsList.js"; +import { SettingsPanel } from "./components/SettingsPanel.js"; -export class WorkshopSettingsUi extends AbstractBuildSettingsPanel { +export class WorkshopSettingsUi extends SettingsPanel { private readonly _crafts: Array; constructor( diff --git a/packages/kitten-scientists/source/ui/components/Button.ts b/packages/kitten-scientists/source/ui/components/Button.ts index c3ded6fcb..8f22c3ba4 100644 --- a/packages/kitten-scientists/source/ui/components/Button.ts +++ b/packages/kitten-scientists/source/ui/components/Button.ts @@ -1,7 +1,8 @@ import { KittenScientists } from "../../KittenScientists.js"; -import { UiComponent, UiComponentOptions } from "./UiComponent.js"; +import { IconButtonOptions } from "./IconButton.js"; +import { UiComponent } from "./UiComponent.js"; -export type ButtonOptions = UiComponentOptions & { +export type ButtonOptions = IconButtonOptions & { readonly title: string; }; @@ -12,6 +13,7 @@ export class Button extends UiComponent { protected readonly _iconElement: JQuery | undefined; readonly element: JQuery; readOnly: boolean; + inactive: boolean; /** * Constructs a `Button`. @@ -49,7 +51,12 @@ export class Button extends UiComponent { }); this.addChildren(options?.children); - this.readOnly = false; + this.readOnly = options?.readOnly ?? false; + this.inactive = options?.inactive ?? false; + + this.element.on("click", () => { + this.click(); + }); } updateLabel(label: string) { @@ -61,4 +68,28 @@ export class Button extends UiComponent { updateTitle(title: string) { this.element.prop("title", title); } + + override click() { + if (this.readOnly) { + return; + } + + super.click(); + } + + override refreshUi(): void { + super.refreshUi(); + + if (this.readOnly) { + this.element.addClass("ks-readonly"); + } else { + this.element.removeClass("ks-readonly"); + } + + if (this.inactive) { + this.element.addClass("ks-inactive"); + } else { + this.element.removeClass("ks-inactive"); + } + } } diff --git a/packages/kitten-scientists/source/ui/components/Dialog.ts b/packages/kitten-scientists/source/ui/components/Dialog.ts index a715e58b7..27daeea04 100644 --- a/packages/kitten-scientists/source/ui/components/Dialog.ts +++ b/packages/kitten-scientists/source/ui/components/Dialog.ts @@ -3,6 +3,7 @@ import { KittenScientists } from "../../KittenScientists.js"; import { Button } from "./Button.js"; import { Container } from "./Container.js"; import { Delimiter } from "./Delimiter.js"; +import { HeaderListItem } from "./HeaderListItem.js"; import { Input } from "./Input.js"; import { Paragraph } from "./Paragraph.js"; import { UiComponent, UiComponentOptions } from "./UiComponent.js"; @@ -11,9 +12,10 @@ export type DialogOptions = UiComponentOptions & { readonly hasCancel?: boolean; readonly hasClose?: boolean; readonly onCancel?: () => void; - readonly onConfirm?: (result: string | undefined) => void; + readonly onConfirm?: (result: string) => void; readonly prompt?: boolean; readonly promptValue?: string; + readonly childrenAfterPrompt?: Array; }; export class Dialog extends UiComponent { @@ -27,7 +29,7 @@ export class Dialog extends UiComponent { * @param options - Options for the dialog. */ constructor(host: KittenScientists, options?: Partial) { - super(host, options); + super(host, { ...options, children: [] }); this.element = $("") .addClass("dialog") @@ -63,10 +65,15 @@ export class Dialog extends UiComponent { this.close(); options.onConfirm?.(this.returnValue); }, + onEscape: (_value: string) => { + this.close(); + options.onCancel?.(); + }, selected: true, value: options.promptValue, }) : undefined, + ...(options?.childrenAfterPrompt ?? []), new Delimiter(host), new Container(host, { children: coalesceArray([ @@ -109,15 +116,30 @@ export class Dialog extends UiComponent { static async prompt( host: KittenScientists, text: string, + title?: string, initialValue?: string, + explainer?: string, ): Promise { return new Promise(resolve => { new Dialog(host, { - children: [new Paragraph(host, text)], + children: coalesceArray([ + title ? new HeaderListItem(host, title) : undefined, + new Paragraph(host, text), + ]), + childrenAfterPrompt: explainer + ? [ + new Container(host, { + children: [new Paragraph(host, explainer)], + classes: ["ks-explainer"], + }), + ] + : [], + hasCancel: true, + hasClose: false, onCancel: () => { resolve(undefined); }, - onConfirm: (result: string | undefined) => { + onConfirm: (result: string) => { resolve(result); }, prompt: true, diff --git a/packages/kitten-scientists/source/ui/components/IconButton.ts b/packages/kitten-scientists/source/ui/components/IconButton.ts index 5150ac725..8909a5b15 100644 --- a/packages/kitten-scientists/source/ui/components/IconButton.ts +++ b/packages/kitten-scientists/source/ui/components/IconButton.ts @@ -1,11 +1,18 @@ import { KittenScientists } from "../../KittenScientists.js"; import { UiComponent, UiComponentOptions } from "./UiComponent.js"; +export type IconButtonOptions = UiComponentOptions & { + readonly readOnly: boolean; + readonly inactive: boolean; +}; + /** * A button that is visually represented through an SVG element. */ export class IconButton extends UiComponent { readonly element: JQuery; + readOnly: boolean; + inactive: boolean; /** * Constructs an `IconButton`. @@ -19,7 +26,7 @@ export class IconButton extends UiComponent { host: KittenScientists, pathData: string, title: string, - options?: Partial, + options?: Partial, ) { super(host, options); @@ -30,9 +37,35 @@ export class IconButton extends UiComponent { this.element = element; this.addChildren(options?.children); + this.readOnly = options?.readOnly ?? false; + this.inactive = options?.inactive ?? false; this.element.on("click", () => { this.click(); }); } + + override click() { + if (this.readOnly) { + return; + } + + super.click(); + } + + override refreshUi(): void { + super.refreshUi(); + + if (this.readOnly) { + this.element.addClass("ks-readonly"); + } else { + this.element.removeClass("ks-readonly"); + } + + if (this.inactive) { + this.element.addClass("ks-inactive"); + } else { + this.element.removeClass("ks-inactive"); + } + } } diff --git a/packages/kitten-scientists/source/ui/components/Input.ts b/packages/kitten-scientists/source/ui/components/Input.ts index 518bff81c..1510a593e 100644 --- a/packages/kitten-scientists/source/ui/components/Input.ts +++ b/packages/kitten-scientists/source/ui/components/Input.ts @@ -4,6 +4,7 @@ import { UiComponent, UiComponentOptions } from "./UiComponent.js"; export type InputOptions = UiComponentOptions & { readonly onChange?: (value: string) => void; readonly onEnter?: (currentValue: string) => void; + readonly onEscape?: (currentValue: string) => void; readonly selected?: boolean; readonly value?: string; }; @@ -39,8 +40,13 @@ export class Input extends UiComponent { } this.element.on("keyup", (event: JQuery.KeyUpEvent) => { - if (event.key === "Enter") { - options?.onEnter?.(this.element[0].value); + switch (event.key) { + case "Enter": + options?.onEnter?.(this.element[0].value); + break; + case "Escape": + options?.onEscape?.(this.element[0].value); + break; } }); diff --git a/packages/kitten-scientists/source/ui/components/SettingTriggerListItem.ts b/packages/kitten-scientists/source/ui/components/SettingTriggerListItem.ts index 14c0f75fc..aa02f0948 100644 --- a/packages/kitten-scientists/source/ui/components/SettingTriggerListItem.ts +++ b/packages/kitten-scientists/source/ui/components/SettingTriggerListItem.ts @@ -1,10 +1,11 @@ import { KittenScientists } from "../../KittenScientists.js"; import { SettingThreshold, SettingTrigger } from "../../settings/Settings.js"; -import { TriggerButton, TriggerButtonBehavior } from "./buttons-icon/TriggerButton.js"; +import { TriggerButton } from "./buttons-icon/TriggerButton.js"; import { SettingListItem, SettingListItemOptions } from "./SettingListItem.js"; export type SettingTriggerListItemOptions = SettingListItemOptions & { - readonly behavior: TriggerButtonBehavior; + readonly onRefreshTrigger: (subject: SettingTriggerListItem) => void; + readonly onSetTrigger: (subject: SettingTriggerListItem) => void; }; export class SettingTriggerListItem extends SettingListItem { @@ -18,7 +19,12 @@ export class SettingTriggerListItem extends SettingListItem { ) { super(host, label, setting, options); - this.triggerButton = new TriggerButton(host, label, setting); + this.triggerButton = new TriggerButton(host, label, setting, { + onClick: options?.onSetTrigger ? () => options.onSetTrigger?.(this) : undefined, + onRefreshTitle: options?.onRefreshTrigger + ? () => options.onRefreshTrigger?.(this) + : undefined, + }); this.head.addChild(this.triggerButton); } diff --git a/packages/kitten-scientists/source/ui/components/SettingTriggerMaxListItem.ts b/packages/kitten-scientists/source/ui/components/SettingTriggerMaxListItem.ts new file mode 100644 index 000000000..c9f1ee851 --- /dev/null +++ b/packages/kitten-scientists/source/ui/components/SettingTriggerMaxListItem.ts @@ -0,0 +1,38 @@ +import { KittenScientists } from "../../KittenScientists.js"; +import { SettingTriggerMax } from "../../settings/Settings.js"; +import { PaddingButton } from "./buttons-icon/PaddingButton.js"; +import { TriggerButton } from "./buttons-icon/TriggerButton.js"; +import { MaxButton } from "./buttons-text/MaxButton.js"; +import { SettingListItem, SettingListItemOptions } from "./SettingListItem.js"; + +export type SettingTriggerMaxListItemOptions = SettingListItemOptions & { + readonly onRefreshTrigger: (subject: SettingTriggerMaxListItem) => void; + readonly onSetMax: (subject: SettingTriggerMaxListItem) => void; + readonly onSetTrigger: (subject: SettingTriggerMaxListItem) => void; +}; + +export class SettingTriggerMaxListItem extends SettingListItem { + readonly maxButton: MaxButton; + readonly triggerButton: TriggerButton; + + constructor( + host: KittenScientists, + label: string, + setting: SettingTriggerMax, + options?: Partial, + ) { + super(host, label, setting, options); + + this.maxButton = new MaxButton(host, label, setting, { + onClick: options?.onSetMax ? () => options.onSetMax?.(this) : undefined, + }); + this.triggerButton = new TriggerButton(host, label, setting, { + onClick: options?.onSetTrigger ? () => options.onSetTrigger?.(this) : undefined, + onRefreshTitle: options?.onRefreshTrigger + ? () => options.onRefreshTrigger?.(this) + : undefined, + }); + + this.head.addChildren([this.maxButton, this.triggerButton, new PaddingButton(host)]); + } +} diff --git a/packages/kitten-scientists/source/ui/components/SettingsList.ts b/packages/kitten-scientists/source/ui/components/SettingsList.ts index c22ad141f..ba0faac7b 100644 --- a/packages/kitten-scientists/source/ui/components/SettingsList.ts +++ b/packages/kitten-scientists/source/ui/components/SettingsList.ts @@ -6,11 +6,11 @@ import { SettingListItem } from "./SettingListItem.js"; import { UiComponent, UiComponentOptions } from "./UiComponent.js"; export type SettingsListOptions = UiComponentOptions & { - readonly hasEnableAll?: boolean; - readonly hasDisableAll?: boolean; - readonly onEnableAll?: () => void; - readonly onDisableAll?: () => void; - readonly onReset?: () => void; + readonly hasEnableAll: boolean; + readonly hasDisableAll: boolean; + readonly onEnableAll: () => void; + readonly onDisableAll: () => void; + readonly onReset: () => void; }; /** diff --git a/packages/kitten-scientists/source/ui/components/TextButton.ts b/packages/kitten-scientists/source/ui/components/TextButton.ts index 897f309d3..909c53bd4 100644 --- a/packages/kitten-scientists/source/ui/components/TextButton.ts +++ b/packages/kitten-scientists/source/ui/components/TextButton.ts @@ -24,21 +24,30 @@ export class TextButton extends UiComponent { element.prop("title", title); } - const onClick = options?.onClick; - if (!isNil(onClick)) { - element.on("click", () => { - if (this.readOnly) { - return; - } - - if (!isNil(onClick)) { - onClick(); - } - }); - } - this.element = element; this.addChildren(options?.children); this.readOnly = false; + + this.element.on("click", () => { + this.click(); + }); + } + + override click() { + if (this.readOnly) { + return; + } + + super.click(); + } + + override refreshUi(): void { + super.refreshUi(); + + if (this.readOnly) { + this.element.addClass("ks-readonly"); + } else { + this.element.removeClass("ks-readonly"); + } } } diff --git a/packages/kitten-scientists/source/ui/components/UiComponent.ts b/packages/kitten-scientists/source/ui/components/UiComponent.ts index 36c776b73..03730c4de 100644 --- a/packages/kitten-scientists/source/ui/components/UiComponent.ts +++ b/packages/kitten-scientists/source/ui/components/UiComponent.ts @@ -1,3 +1,4 @@ +import { roundTo } from "@oliversalzburg/js-utils/math/core.js"; import { KittenScientists } from "../../KittenScientists.js"; import { cerror } from "../../tools/Log.js"; @@ -21,8 +22,8 @@ export abstract class UiComponent extends EventTarget { readonly children = new Set(); - private readonly _onClick: UiComponentOptions["onClick"] | undefined; - private readonly _onRefresh: UiComponentOptions["onRefresh"] | undefined; + protected readonly _onClick: UiComponentOptions["onClick"] | undefined; + protected readonly _onRefresh: UiComponentOptions["onRefresh"] | undefined; /** * Constructs the base `UiComponent`. @@ -106,7 +107,12 @@ export abstract class UiComponent extends EventTarget { } // Cap value between 0 and 1. - return Math.max(0, Math.min(1, parseFloat(value) / 100)); + return UiComponent.parsePercentage(value); + } + + static parsePercentage(value: string): number { + const cleanedValue = value.trim().replace(/%$/, ""); + return Math.max(0, Math.min(1, parseFloat(cleanedValue) / 100)); } protected _renderLimit(value: number): string { @@ -122,7 +128,7 @@ export abstract class UiComponent extends EventTarget { } static renderPercentage(value: number): string { - return Math.round(100 * value).toFixed(0); + return roundTo(100 * value, 3).toString(); } static promptFloat(text: string, defaultValue: string): number | null { diff --git a/packages/kitten-scientists/source/ui/components/buttons-icon/ConsumeButton.ts b/packages/kitten-scientists/source/ui/components/buttons-icon/ConsumeButton.ts index 2a64aa9c8..566dd3b5f 100644 --- a/packages/kitten-scientists/source/ui/components/buttons-icon/ConsumeButton.ts +++ b/packages/kitten-scientists/source/ui/components/buttons-icon/ConsumeButton.ts @@ -1,9 +1,8 @@ import { ResourcesSettingsItem } from "packages/kitten-scientists/source/settings/ResourcesSettings.js"; import { Icons } from "../../../images/Icons.js"; import { KittenScientists } from "../../../KittenScientists.js"; -import { AbstractBuildSettingsPanel } from "../../SettingsSectionUi.js"; import { Button } from "../Button.js"; -import { UiComponentOptions } from "../UiComponent.js"; +import { UiComponent, UiComponentOptions } from "../UiComponent.js"; export class ConsumeButton extends Button { readonly setting: ResourcesSettingsItem; @@ -14,15 +13,10 @@ export class ConsumeButton extends Button { setting: ResourcesSettingsItem, options?: Partial, ) { - super( - host, - `${AbstractBuildSettingsPanel.renderPercentage(setting.consume)}%`, - Icons.DataUsage, - options, - ); + super(host, `${UiComponent.renderPercentage(setting.consume)}%`, Icons.DataUsage, options); this.element.on("click", () => { - const value = AbstractBuildSettingsPanel.promptPercentage( + const value = UiComponent.promptPercentage( host.engine.i18n("resources.consume.set", [label]), setting.consume, ); @@ -42,7 +36,7 @@ export class ConsumeButton extends Button { refreshUi() { super.refreshUi(); - const label = `${AbstractBuildSettingsPanel.renderPercentage(this.setting.consume)}%`; + const label = `${UiComponent.renderPercentage(this.setting.consume)}%`; this.updateTitle(this._host.engine.i18n("resources.consume", [label])); this.updateLabel(label); } diff --git a/packages/kitten-scientists/source/ui/components/buttons-icon/StockButton.ts b/packages/kitten-scientists/source/ui/components/buttons-icon/StockButton.ts index 7e2caf44f..30bed9807 100644 --- a/packages/kitten-scientists/source/ui/components/buttons-icon/StockButton.ts +++ b/packages/kitten-scientists/source/ui/components/buttons-icon/StockButton.ts @@ -2,9 +2,8 @@ import { ResetResourcesSettingsItem } from "packages/kitten-scientists/source/se import { ResourcesSettingsItem } from "packages/kitten-scientists/source/settings/ResourcesSettings.js"; import { Icons } from "../../../images/Icons.js"; import { KittenScientists } from "../../../KittenScientists.js"; -import { AbstractBuildSettingsPanel } from "../../SettingsSectionUi.js"; import { Button } from "../Button.js"; -import { UiComponentOptions } from "../UiComponent.js"; +import { UiComponent, UiComponentOptions } from "../UiComponent.js"; export class StockButton extends Button { readonly setting: ResetResourcesSettingsItem | ResourcesSettingsItem; @@ -15,15 +14,10 @@ export class StockButton extends Button { setting: ResetResourcesSettingsItem | ResourcesSettingsItem, options?: Partial, ) { - super( - host, - AbstractBuildSettingsPanel.renderLimit(setting.stock, host), - Icons.Inventory2, - options, - ); + super(host, UiComponent.renderLimit(setting.stock, host), Icons.Inventory2, options); this.element.on("click", () => { - const value = AbstractBuildSettingsPanel.promptLimit( + const value = UiComponent.promptLimit( host.engine.i18n("resources.stock.set", [label]), setting.stock.toFixed(), ); @@ -43,7 +37,7 @@ export class StockButton extends Button { refreshUi() { super.refreshUi(); - const label = AbstractBuildSettingsPanel.renderLimit(this.setting.stock, this._host); + const label = UiComponent.renderLimit(this.setting.stock, this._host); this.updateTitle(this._host.engine.i18n("resources.stock", [label])); this.updateLabel(label); } diff --git a/packages/kitten-scientists/source/ui/components/buttons-icon/TriggerButton.ts b/packages/kitten-scientists/source/ui/components/buttons-icon/TriggerButton.ts index ba2967ef9..4dc012927 100644 --- a/packages/kitten-scientists/source/ui/components/buttons-icon/TriggerButton.ts +++ b/packages/kitten-scientists/source/ui/components/buttons-icon/TriggerButton.ts @@ -1,34 +1,46 @@ +import { isNil } from "@oliversalzburg/js-utils/data/nil.js"; import { Icons } from "../../../images/Icons.js"; import { KittenScientists } from "../../../KittenScientists.js"; import { SettingThreshold, SettingTrigger } from "../../../settings/Settings.js"; -import { AbstractBuildSettingsPanel } from "../../SettingsSectionUi.js"; import { IconButton } from "../IconButton.js"; -import { UiComponentOptions } from "../UiComponent.js"; +import { UiComponent, UiComponentOptions } from "../UiComponent.js"; export type TriggerButtonBehavior = "integer" | "percentage"; +export type TriggerButtonOptions = UiComponentOptions & { + readonly onRefreshTitle: (subject: TriggerButton) => void; +}; + export class TriggerButton extends IconButton { readonly behavior: TriggerButtonBehavior; readonly setting: SettingTrigger | SettingThreshold; + protected readonly _onRefreshTitle?: (subject: TriggerButton) => void; constructor( host: KittenScientists, label: string, setting: SettingTrigger | SettingThreshold, - options?: Partial, + options?: Partial, ) { - super(host, Icons.Trigger, "", options); + super(host, Icons.Trigger, "", { ...options, onClick: undefined }); + + this._onRefreshTitle = options?.onRefreshTitle; this.behavior = setting instanceof SettingTrigger ? "percentage" : "integer"; this.element.on("click", () => { + if (!isNil(options?.onClick)) { + options.onClick(this); + return; + } + const value = this.setting instanceof SettingTrigger - ? AbstractBuildSettingsPanel.promptPercentage( + ? UiComponent.promptPercentage( host.engine.i18n("ui.trigger.setpercentage", [label]), setting.trigger, ) - : AbstractBuildSettingsPanel.promptLimit( + : UiComponent.promptLimit( host.engine.i18n("ui.trigger.setinteger", [label]), setting.trigger.toFixed(), ); @@ -37,8 +49,6 @@ export class TriggerButton extends IconButton { setting.trigger = value; this.refreshUi(); } - - this.click(); }); this.setting = setting; } @@ -46,10 +56,17 @@ export class TriggerButton extends IconButton { refreshUi() { super.refreshUi(); + if (this._onRefreshTitle) { + this._onRefreshTitle(this); + return; + } + this.element[0].title = this._host.engine.i18n("ui.trigger", [ this.behavior === "percentage" - ? `${AbstractBuildSettingsPanel.renderPercentage(this.setting.trigger)}%` - : AbstractBuildSettingsPanel.renderLimit(this.setting.trigger, this._host), + ? this.setting.trigger < 0 + ? this._host.engine.i18n("ui.infinity") + : `${UiComponent.renderPercentage(this.setting.trigger)}%` + : UiComponent.renderLimit(this.setting.trigger, this._host), ]); } } diff --git a/packages/kitten-scientists/source/ui/components/buttons-text/BuyButton.ts b/packages/kitten-scientists/source/ui/components/buttons-text/BuyButton.ts index 366f49100..b084298dd 100644 --- a/packages/kitten-scientists/source/ui/components/buttons-text/BuyButton.ts +++ b/packages/kitten-scientists/source/ui/components/buttons-text/BuyButton.ts @@ -1,7 +1,7 @@ import { KittenScientists } from "../../../KittenScientists.js"; import { SettingBuy } from "../../../settings/Settings.js"; -import { AbstractBuildSettingsPanel } from "../../SettingsSectionUi.js"; import { TextButton } from "../TextButton.js"; +import { UiComponent } from "../UiComponent.js"; export class BuyButton extends TextButton { readonly setting: SettingBuy; @@ -9,7 +9,7 @@ export class BuyButton extends TextButton { constructor(host: KittenScientists, setting: SettingBuy, handler: { onClick?: () => void } = {}) { super(host, undefined, { onClick: () => { - const value = AbstractBuildSettingsPanel.promptLimit( + const value = UiComponent.promptLimit( host.engine.i18n("blackcoin.buy.threshold"), setting.buy.toString(), ); @@ -36,9 +36,7 @@ export class BuyButton extends TextButton { this.element.prop("title", this.setting.buy.toFixed(3)); this.element.text( - this._host.engine.i18n("ui.buy", [ - AbstractBuildSettingsPanel.renderLimit(this.setting.buy, this._host), - ]), + this._host.engine.i18n("ui.buy", [UiComponent.renderLimit(this.setting.buy, this._host)]), ); } } diff --git a/packages/kitten-scientists/source/ui/components/buttons-text/MaxButton.ts b/packages/kitten-scientists/source/ui/components/buttons-text/MaxButton.ts index 809a2a433..bb4dd19f7 100644 --- a/packages/kitten-scientists/source/ui/components/buttons-text/MaxButton.ts +++ b/packages/kitten-scientists/source/ui/components/buttons-text/MaxButton.ts @@ -1,7 +1,7 @@ import { KittenScientists } from "../../../KittenScientists.js"; import { SettingMax } from "../../../settings/Settings.js"; -import { AbstractBuildSettingsPanel } from "../../SettingsSectionUi.js"; import { TextButton } from "../TextButton.js"; +import { UiComponent } from "../UiComponent.js"; export class MaxButton extends TextButton { readonly setting: SettingMax; @@ -14,7 +14,7 @@ export class MaxButton extends TextButton { ) { super(host, label, { onClick: () => { - const value = AbstractBuildSettingsPanel.promptLimit( + const value = UiComponent.promptLimit( host.engine.i18n("ui.max.set", [label]), setting.max.toString(), ); @@ -41,9 +41,7 @@ export class MaxButton extends TextButton { this.element.prop("title", this.setting.max.toFixed()); this.element.text( - this._host.engine.i18n("ui.max", [ - AbstractBuildSettingsPanel.renderLimit(this.setting.max, this._host), - ]), + this._host.engine.i18n("ui.max", [UiComponent.renderLimit(this.setting.max, this._host)]), ); } } diff --git a/packages/kitten-scientists/source/ui/components/buttons-text/SellButton.ts b/packages/kitten-scientists/source/ui/components/buttons-text/SellButton.ts index 273164cbb..e21d149eb 100644 --- a/packages/kitten-scientists/source/ui/components/buttons-text/SellButton.ts +++ b/packages/kitten-scientists/source/ui/components/buttons-text/SellButton.ts @@ -1,7 +1,7 @@ import { KittenScientists } from "../../../KittenScientists.js"; import { SettingSell } from "../../../settings/Settings.js"; -import { AbstractBuildSettingsPanel } from "../../SettingsSectionUi.js"; import { TextButton } from "../TextButton.js"; +import { UiComponent } from "../UiComponent.js"; export class SellButton extends TextButton { readonly setting: SettingSell; @@ -13,7 +13,7 @@ export class SellButton extends TextButton { ) { super(host, undefined, { onClick: () => { - const value = AbstractBuildSettingsPanel.promptLimit( + const value = UiComponent.promptLimit( host.engine.i18n("blackcoin.sell.threshold"), setting.sell.toString(), ); @@ -40,9 +40,7 @@ export class SellButton extends TextButton { this.element.prop("title", this.setting.sell.toFixed(3)); this.element.text( - this._host.engine.i18n("ui.sell", [ - AbstractBuildSettingsPanel.renderLimit(this.setting.sell, this._host), - ]), + this._host.engine.i18n("ui.sell", [UiComponent.renderLimit(this.setting.sell, this._host)]), ); } } diff --git a/packages/kitten-scientists/source/ui/components/buttons-text/TriggerLimitButton.ts b/packages/kitten-scientists/source/ui/components/buttons-text/TriggerLimitButton.ts index 11faa5f50..e9b0da0d1 100644 --- a/packages/kitten-scientists/source/ui/components/buttons-text/TriggerLimitButton.ts +++ b/packages/kitten-scientists/source/ui/components/buttons-text/TriggerLimitButton.ts @@ -1,8 +1,8 @@ import { KittenScientists } from "../../../KittenScientists.js"; import { SettingTrigger } from "../../../settings/Settings.js"; -import { AbstractBuildSettingsPanel } from "../../SettingsSectionUi.js"; import { TriggerButtonBehavior } from "../buttons-icon/TriggerButton.js"; import { TextButton } from "../TextButton.js"; +import { UiComponent } from "../UiComponent.js"; export class TriggerLimitButton extends TextButton { readonly behavior: TriggerButtonBehavior = "percentage"; @@ -16,7 +16,7 @@ export class TriggerLimitButton extends TextButton { ) { super(host, label, { onClick: () => { - const value = AbstractBuildSettingsPanel.promptLimit( + const value = UiComponent.promptLimit( host.engine.i18n("ui.trigger.setinteger", [label]), setting.trigger.toString(), ); @@ -44,7 +44,7 @@ export class TriggerLimitButton extends TextButton { this.element.prop("title", this.setting.trigger.toFixed()); this.element.text( this._host.engine.i18n("ui.trigger", [ - AbstractBuildSettingsPanel.renderLimit(this.setting.trigger, this._host), + UiComponent.renderLimit(this.setting.trigger, this._host), ]), ); }