import { Clickable, ClickableOptions, createClickable, GenericClickable } from "features/clickables/clickable"; import { GenericConversion } from "features/conversion"; import { CoercableComponent, jsx, JSXFunction, OptionsFunc, Replace, setDefault } from "features/feature"; import { displayResource } from "features/resources/resource"; import { createTreeNode, GenericTree, GenericTreeNode, TreeNode, TreeNodeOptions } from "features/trees/tree"; import { Modifier } from "game/modifiers"; import { Persistent, persistent } from "game/persistence"; import player from "game/player"; import Decimal, { DecimalSource, format } from "util/bignum"; import { Computable, convertComputable, GetComputableType, GetComputableTypeWithDefault, processComputable, ProcessedComputable } from "util/computed"; import { renderJSX } from "util/vue"; import { computed, Ref, unref } from "vue"; import "./common.css"; export interface ResetButtonOptions extends ClickableOptions { conversion: GenericConversion; tree: GenericTree; treeNode: GenericTreeNode; resetDescription?: Computable; showNextAt?: Computable; display?: Computable; canClick?: Computable; minimumGain?: Computable; } export type ResetButton = Replace< Clickable, { resetDescription: GetComputableTypeWithDefault>; showNextAt: GetComputableTypeWithDefault; display: GetComputableTypeWithDefault>; canClick: GetComputableTypeWithDefault>; minimumGain: GetComputableTypeWithDefault; onClick: VoidFunction; } >; export type GenericResetButton = Replace< GenericClickable & ResetButton, { resetDescription: ProcessedComputable; showNextAt: ProcessedComputable; display: ProcessedComputable; canClick: ProcessedComputable; minimumGain: ProcessedComputable; } >; export function createResetButton( optionsFunc: OptionsFunc ): ResetButton { return createClickable(() => { const resetButton = optionsFunc(); processComputable(resetButton as T, "showNextAt"); setDefault(resetButton, "showNextAt", true); setDefault(resetButton, "minimumGain", 1); if (resetButton.resetDescription == null) { resetButton.resetDescription = computed(() => Decimal.lt(resetButton.conversion.gainResource.value, 1e3) ? "Reset for " : "" ); } else { processComputable(resetButton as T, "resetDescription"); } if (resetButton.display == null) { resetButton.display = jsx(() => ( {unref(resetButton.resetDescription as ProcessedComputable)} {displayResource( resetButton.conversion.gainResource, Decimal.max( unref(resetButton.conversion.actualGain), unref(resetButton.minimumGain as ProcessedComputable) ) )} {" "} {resetButton.conversion.gainResource.displayName}

{resetButton.conversion.buyMax ? "Next:" : "Req:"}{" "} {displayResource( resetButton.conversion.baseResource, resetButton.conversion.buyMax || Decimal.floor(unref(resetButton.conversion.actualGain)).neq(1) ? unref(resetButton.conversion.nextAt) : unref(resetButton.conversion.currentAt) )}{" "} {resetButton.conversion.baseResource.displayName}
)); } if (resetButton.canClick == null) { resetButton.canClick = computed(() => Decimal.gte( unref(resetButton.conversion.actualGain), unref(resetButton.minimumGain as ProcessedComputable) ) ); } const onClick = resetButton.onClick; resetButton.onClick = function () { if (!unref(resetButton.canClick)) { return; } resetButton.conversion.convert(); resetButton.tree.reset(resetButton.treeNode); onClick?.(); }; return resetButton; }) as unknown as ResetButton; } export interface LayerTreeNodeOptions extends TreeNodeOptions { layerID: string; color: Computable; // marking as required display?: Computable; append?: Computable; } export type LayerTreeNode = Replace< TreeNode, { display: GetComputableTypeWithDefault; append: GetComputableType; } >; export type GenericLayerTreeNode = Replace< LayerTreeNode, { display: ProcessedComputable; append?: ProcessedComputable; } >; export function createLayerTreeNode( optionsFunc: OptionsFunc ): LayerTreeNode { return createTreeNode(() => { const options = optionsFunc(); processComputable(options as T, "display"); setDefault(options, "display", options.layerID); processComputable(options as T, "append"); return { ...options, display: options.display, onClick: unref((options as unknown as GenericLayerTreeNode).append) ? function () { if (player.tabs.includes(options.layerID)) { const index = player.tabs.lastIndexOf(options.layerID); player.tabs.splice(index, 1); } else { player.tabs.push(options.layerID); } } : function () { player.tabs.splice(1, 1, options.layerID); } }; }) as unknown as LayerTreeNode; } export function createCollapsibleModifierSections( sections: { title: string; subtitle?: string; modifier: Required; base?: Computable; unit?: string; baseText?: Computable; visible?: Computable; }[] ): [JSXFunction, Persistent[]] { const processedBase = sections.map(s => convertComputable(s.base)); const processedBaseText = sections.map(s => convertComputable(s.baseText)); const processedVisible = sections.map(s => convertComputable(s.visible)); const collapsed = sections.map(() => persistent(false)); const jsxFunc = jsx(() => { const sectionJSX = sections.map((s, i) => { if (unref(processedVisible[i]) === false) return null; const header = (

(collapsed[i].value = !collapsed[i].value)} style="cursor: pointer" > {s.title} {s.subtitle ? ({s.subtitle}) : null}

); const modifiers = unref(collapsed[i]) ? null : ( <>
{format(unref(processedBase[i]) ?? 1)} {s.unit} {renderJSX(unref(processedBaseText[i]) ?? "Base")}
{renderJSX(unref(s.modifier.description))} ); return ( <> {i === 0 ? null :
}
{header}
{modifiers}
Total: {format(s.modifier.apply(unref(processedBase[i]) ?? 1))} {s.unit}
); }); return <>{sectionJSX}; }); return [jsxFunc, collapsed]; }