forked from profectus/Profectus
Merge remote-tracking branch 'upstream/main'
This commit is contained in:
commit
f79359b18a
27 changed files with 132 additions and 71 deletions
|
@ -33,6 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
- Requires referencing persistent refs either through a proxy or by wrapping in `noPersist()`
|
- Requires referencing persistent refs either through a proxy or by wrapping in `noPersist()`
|
||||||
- **BREAKING** Visibility properties can now take booleans
|
- **BREAKING** Visibility properties can now take booleans
|
||||||
- Removed showIf util
|
- Removed showIf util
|
||||||
|
- **BREAKING** Lazy proxies and options functions now pass the base object in as `this` as well as the first parameter.
|
||||||
- Tweaked settings display
|
- Tweaked settings display
|
||||||
- setupPassiveGeneration will no longer lower the resource
|
- setupPassiveGeneration will no longer lower the resource
|
||||||
- displayResource now floors resource amounts
|
- displayResource now floors resource amounts
|
||||||
|
|
|
@ -5,11 +5,10 @@ import { createClickable } from "features/clickables/clickable";
|
||||||
import type { GenericConversion } from "features/conversion";
|
import type { GenericConversion } from "features/conversion";
|
||||||
import type { CoercableComponent, JSXFunction, OptionsFunc, Replace } from "features/feature";
|
import type { CoercableComponent, JSXFunction, OptionsFunc, Replace } from "features/feature";
|
||||||
import { jsx, setDefault } from "features/feature";
|
import { jsx, setDefault } from "features/feature";
|
||||||
import { displayResource, Resource } from "features/resources/resource";
|
import { Resource, displayResource } from "features/resources/resource";
|
||||||
import type { GenericTree, GenericTreeNode, TreeNode, TreeNodeOptions } from "features/trees/tree";
|
import type { GenericTree, GenericTreeNode, TreeNode, TreeNodeOptions } from "features/trees/tree";
|
||||||
import { createTreeNode } from "features/trees/tree";
|
import { createTreeNode } from "features/trees/tree";
|
||||||
import Formula from "game/formulas/formulas";
|
import type { GenericFormula } from "game/formulas/types";
|
||||||
import type { FormulaSource, GenericFormula } from "game/formulas/types";
|
|
||||||
import type { Modifier } from "game/modifiers";
|
import type { Modifier } from "game/modifiers";
|
||||||
import type { Persistent } from "game/persistence";
|
import type { Persistent } from "game/persistence";
|
||||||
import { DefaultValue, persistent } from "game/persistence";
|
import { DefaultValue, persistent } from "game/persistence";
|
||||||
|
@ -99,8 +98,8 @@ export type GenericResetButton = Replace<
|
||||||
export function createResetButton<T extends ClickableOptions & ResetButtonOptions>(
|
export function createResetButton<T extends ClickableOptions & ResetButtonOptions>(
|
||||||
optionsFunc: OptionsFunc<T>
|
optionsFunc: OptionsFunc<T>
|
||||||
): ResetButton<T> {
|
): ResetButton<T> {
|
||||||
return createClickable(() => {
|
return createClickable(feature => {
|
||||||
const resetButton = optionsFunc();
|
const resetButton = optionsFunc.call(feature, feature);
|
||||||
|
|
||||||
processComputable(resetButton as T, "showNextAt");
|
processComputable(resetButton as T, "showNextAt");
|
||||||
setDefault(resetButton, "showNextAt", true);
|
setDefault(resetButton, "showNextAt", true);
|
||||||
|
@ -213,8 +212,8 @@ export type GenericLayerTreeNode = Replace<
|
||||||
export function createLayerTreeNode<T extends LayerTreeNodeOptions>(
|
export function createLayerTreeNode<T extends LayerTreeNodeOptions>(
|
||||||
optionsFunc: OptionsFunc<T>
|
optionsFunc: OptionsFunc<T>
|
||||||
): LayerTreeNode<T> {
|
): LayerTreeNode<T> {
|
||||||
return createTreeNode(() => {
|
return createTreeNode(feature => {
|
||||||
const options = optionsFunc();
|
const options = optionsFunc.call(feature, feature);
|
||||||
processComputable(options as T, "display");
|
processComputable(options as T, "display");
|
||||||
setDefault(options, "display", options.layerID);
|
setDefault(options, "display", options.layerID);
|
||||||
processComputable(options as T, "append");
|
processComputable(options as T, "append");
|
||||||
|
|
|
@ -143,8 +143,10 @@ export function createAchievement<T extends AchievementOptions>(
|
||||||
): Achievement<T> {
|
): Achievement<T> {
|
||||||
const earned = persistent<boolean>(false, false);
|
const earned = persistent<boolean>(false, false);
|
||||||
const decoratedData = decorators.reduce((current, next) => Object.assign(current, next.getPersistentData?.()), {});
|
const decoratedData = decorators.reduce((current, next) => Object.assign(current, next.getPersistentData?.()), {});
|
||||||
return createLazyProxy(() => {
|
return createLazyProxy(feature => {
|
||||||
const achievement = optionsFunc?.() ?? ({} as ReturnType<NonNullable<typeof optionsFunc>>);
|
const achievement =
|
||||||
|
optionsFunc?.call(feature, feature) ??
|
||||||
|
({} as ReturnType<NonNullable<typeof optionsFunc>>);
|
||||||
achievement.id = getUniqueID("achievement-");
|
achievement.id = getUniqueID("achievement-");
|
||||||
achievement.type = AchievementType;
|
achievement.type = AchievementType;
|
||||||
achievement[Component] = AchievementComponent as GenericComponent;
|
achievement[Component] = AchievementComponent as GenericComponent;
|
||||||
|
|
|
@ -108,8 +108,10 @@ export function createAction<T extends ActionOptions>(
|
||||||
): Action<T> {
|
): Action<T> {
|
||||||
const progress = persistent<DecimalSource>(0);
|
const progress = persistent<DecimalSource>(0);
|
||||||
const decoratedData = decorators.reduce((current, next) => Object.assign(current, next.getPersistentData?.()), {});
|
const decoratedData = decorators.reduce((current, next) => Object.assign(current, next.getPersistentData?.()), {});
|
||||||
return createLazyProxy(() => {
|
return createLazyProxy(feature => {
|
||||||
const action = optionsFunc?.() ?? ({} as ReturnType<NonNullable<typeof optionsFunc>>);
|
const action =
|
||||||
|
optionsFunc?.call(feature, feature) ??
|
||||||
|
({} as ReturnType<NonNullable<typeof optionsFunc>>);
|
||||||
action.id = getUniqueID("action-");
|
action.id = getUniqueID("action-");
|
||||||
action.type = ActionType;
|
action.type = ActionType;
|
||||||
action[Component] = ClickableComponent as GenericComponent;
|
action[Component] = ClickableComponent as GenericComponent;
|
||||||
|
|
|
@ -106,8 +106,8 @@ export function createBar<T extends BarOptions>(
|
||||||
...decorators: Decorator<T, BaseBar, GenericBar>[]
|
...decorators: Decorator<T, BaseBar, GenericBar>[]
|
||||||
): Bar<T> {
|
): Bar<T> {
|
||||||
const decoratedData = decorators.reduce((current, next) => Object.assign(current, next.getPersistentData?.()), {});
|
const decoratedData = decorators.reduce((current, next) => Object.assign(current, next.getPersistentData?.()), {});
|
||||||
return createLazyProxy(() => {
|
return createLazyProxy(feature => {
|
||||||
const bar = optionsFunc();
|
const bar = optionsFunc.call(feature, feature);
|
||||||
bar.id = getUniqueID("bar-");
|
bar.id = getUniqueID("bar-");
|
||||||
bar.type = BarType;
|
bar.type = BarType;
|
||||||
bar[Component] = BarComponent as GenericComponent;
|
bar[Component] = BarComponent as GenericComponent;
|
||||||
|
|
|
@ -297,8 +297,8 @@ export function createBoard<T extends BoardOptions>(
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
return createLazyProxy(() => {
|
return createLazyProxy(feature => {
|
||||||
const board = optionsFunc();
|
const board = optionsFunc.call(feature, feature);
|
||||||
board.id = getUniqueID("board-");
|
board.id = getUniqueID("board-");
|
||||||
board.type = BoardType;
|
board.type = BoardType;
|
||||||
board[Component] = BoardComponent as GenericComponent;
|
board[Component] = BoardComponent as GenericComponent;
|
||||||
|
|
|
@ -155,8 +155,8 @@ export function createChallenge<T extends ChallengeOptions>(
|
||||||
const completions = persistent(0);
|
const completions = persistent(0);
|
||||||
const active = persistent(false, false);
|
const active = persistent(false, false);
|
||||||
const decoratedData = decorators.reduce((current, next) => Object.assign(current, next.getPersistentData?.()), {});
|
const decoratedData = decorators.reduce((current, next) => Object.assign(current, next.getPersistentData?.()), {});
|
||||||
return createLazyProxy(() => {
|
return createLazyProxy(feature => {
|
||||||
const challenge = optionsFunc();
|
const challenge = optionsFunc.call(feature, feature);
|
||||||
|
|
||||||
challenge.id = getUniqueID("challenge-");
|
challenge.id = getUniqueID("challenge-");
|
||||||
challenge.type = ChallengeType;
|
challenge.type = ChallengeType;
|
||||||
|
|
|
@ -100,8 +100,10 @@ export function createClickable<T extends ClickableOptions>(
|
||||||
...decorators: Decorator<T, BaseClickable, GenericClickable>[]
|
...decorators: Decorator<T, BaseClickable, GenericClickable>[]
|
||||||
): Clickable<T> {
|
): Clickable<T> {
|
||||||
const decoratedData = decorators.reduce((current, next) => Object.assign(current, next.getPersistentData?.()), {});
|
const decoratedData = decorators.reduce((current, next) => Object.assign(current, next.getPersistentData?.()), {});
|
||||||
return createLazyProxy(() => {
|
return createLazyProxy(feature => {
|
||||||
const clickable = optionsFunc?.() ?? ({} as ReturnType<NonNullable<typeof optionsFunc>>);
|
const clickable =
|
||||||
|
optionsFunc?.call(feature, feature) ??
|
||||||
|
({} as ReturnType<NonNullable<typeof optionsFunc>>);
|
||||||
clickable.id = getUniqueID("clickable-");
|
clickable.id = getUniqueID("clickable-");
|
||||||
clickable.type = ClickableType;
|
clickable.type = ClickableType;
|
||||||
clickable[Component] = ClickableComponent as GenericComponent;
|
clickable[Component] = ClickableComponent as GenericComponent;
|
||||||
|
|
|
@ -127,8 +127,8 @@ export function createConversion<T extends ConversionOptions>(
|
||||||
optionsFunc: OptionsFunc<T, BaseConversion, GenericConversion>,
|
optionsFunc: OptionsFunc<T, BaseConversion, GenericConversion>,
|
||||||
...decorators: Decorator<T, BaseConversion, GenericConversion>[]
|
...decorators: Decorator<T, BaseConversion, GenericConversion>[]
|
||||||
): Conversion<T> {
|
): Conversion<T> {
|
||||||
return createLazyProxy(() => {
|
return createLazyProxy(feature => {
|
||||||
const conversion = optionsFunc();
|
const conversion = optionsFunc.call(feature, feature);
|
||||||
|
|
||||||
for (const decorator of decorators) {
|
for (const decorator of decorators) {
|
||||||
decorator.preConstruct?.(conversion);
|
decorator.preConstruct?.(conversion);
|
||||||
|
@ -221,8 +221,8 @@ export function createCumulativeConversion<S extends ConversionOptions>(
|
||||||
export function createIndependentConversion<S extends ConversionOptions>(
|
export function createIndependentConversion<S extends ConversionOptions>(
|
||||||
optionsFunc: OptionsFunc<S, BaseConversion, GenericConversion>
|
optionsFunc: OptionsFunc<S, BaseConversion, GenericConversion>
|
||||||
): Conversion<S> {
|
): Conversion<S> {
|
||||||
return createConversion(() => {
|
return createConversion(feature => {
|
||||||
const conversion: S = optionsFunc();
|
const conversion: S = optionsFunc.call(feature, feature);
|
||||||
|
|
||||||
setDefault(conversion, "buyMax", false);
|
setDefault(conversion, "buyMax", false);
|
||||||
|
|
||||||
|
|
|
@ -42,9 +42,9 @@ export type Replace<T, S> = S & Omit<T, keyof S>;
|
||||||
* with "this" bound to what the type will eventually be processed into.
|
* with "this" bound to what the type will eventually be processed into.
|
||||||
* Intended for making lazily evaluated objects.
|
* Intended for making lazily evaluated objects.
|
||||||
*/
|
*/
|
||||||
export type OptionsFunc<T, R = Record<string, unknown>, S = R> = () => OptionsObject<T,R,S>;
|
export type OptionsFunc<T, R = unknown, S = R> = (obj: R) => OptionsObject<T,R,S>;
|
||||||
|
|
||||||
export type OptionsObject<T, R = Record<string, unknown>, S = R> = T & Partial<R> & ThisType<T & S>;
|
export type OptionsObject<T, R = unknown, S = R> = T & Partial<R> & ThisType<T & S>;
|
||||||
|
|
||||||
let id = 0;
|
let id = 0;
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -307,8 +307,8 @@ export function createGrid<T extends GridOptions>(
|
||||||
optionsFunc: OptionsFunc<T, BaseGrid, GenericGrid>
|
optionsFunc: OptionsFunc<T, BaseGrid, GenericGrid>
|
||||||
): Grid<T> {
|
): Grid<T> {
|
||||||
const cellState = persistent<Record<string | number, State>>({}, false);
|
const cellState = persistent<Record<string | number, State>>({}, false);
|
||||||
return createLazyProxy(() => {
|
return createLazyProxy(feature => {
|
||||||
const grid = optionsFunc();
|
const grid = optionsFunc.call(feature, feature);
|
||||||
grid.id = getUniqueID("grid-");
|
grid.id = getUniqueID("grid-");
|
||||||
grid[Component] = GridComponent as GenericComponent;
|
grid[Component] = GridComponent as GenericComponent;
|
||||||
|
|
||||||
|
|
|
@ -68,8 +68,8 @@ const uppercaseNumbers = [")", "!", "@", "#", "$", "%", "^", "&", "*", "("];
|
||||||
export function createHotkey<T extends HotkeyOptions>(
|
export function createHotkey<T extends HotkeyOptions>(
|
||||||
optionsFunc: OptionsFunc<T, BaseHotkey, GenericHotkey>
|
optionsFunc: OptionsFunc<T, BaseHotkey, GenericHotkey>
|
||||||
): Hotkey<T> {
|
): Hotkey<T> {
|
||||||
return createLazyProxy(() => {
|
return createLazyProxy(feature => {
|
||||||
const hotkey = optionsFunc();
|
const hotkey = optionsFunc.call(feature, feature);
|
||||||
hotkey.type = HotkeyType;
|
hotkey.type = HotkeyType;
|
||||||
|
|
||||||
processComputable(hotkey as T, "enabled");
|
processComputable(hotkey as T, "enabled");
|
||||||
|
|
|
@ -91,8 +91,8 @@ export function createInfobox<T extends InfoboxOptions>(
|
||||||
optionsFunc: OptionsFunc<T, BaseInfobox, GenericInfobox>
|
optionsFunc: OptionsFunc<T, BaseInfobox, GenericInfobox>
|
||||||
): Infobox<T> {
|
): Infobox<T> {
|
||||||
const collapsed = persistent<boolean>(false, false);
|
const collapsed = persistent<boolean>(false, false);
|
||||||
return createLazyProxy(() => {
|
return createLazyProxy(feature => {
|
||||||
const infobox = optionsFunc();
|
const infobox = optionsFunc.call(feature, feature);
|
||||||
infobox.id = getUniqueID("infobox-");
|
infobox.id = getUniqueID("infobox-");
|
||||||
infobox.type = InfoboxType;
|
infobox.type = InfoboxType;
|
||||||
infobox[Component] = InfoboxComponent as GenericComponent;
|
infobox[Component] = InfoboxComponent as GenericComponent;
|
||||||
|
|
|
@ -59,8 +59,8 @@ export type GenericLinks = Replace<
|
||||||
export function createLinks<T extends LinksOptions>(
|
export function createLinks<T extends LinksOptions>(
|
||||||
optionsFunc: OptionsFunc<T, BaseLinks, GenericLinks>
|
optionsFunc: OptionsFunc<T, BaseLinks, GenericLinks>
|
||||||
): Links<T> {
|
): Links<T> {
|
||||||
return createLazyProxy(() => {
|
return createLazyProxy(feature => {
|
||||||
const links = optionsFunc();
|
const links = optionsFunc.call(feature, feature);
|
||||||
links.type = LinksType;
|
links.type = LinksType;
|
||||||
links[Component] = LinksComponent as GenericComponent;
|
links[Component] = LinksComponent as GenericComponent;
|
||||||
|
|
||||||
|
|
|
@ -69,8 +69,10 @@ export type GenericParticles = Particles<ParticlesOptions>;
|
||||||
export function createParticles<T extends ParticlesOptions>(
|
export function createParticles<T extends ParticlesOptions>(
|
||||||
optionsFunc?: OptionsFunc<T, BaseParticles, GenericParticles>
|
optionsFunc?: OptionsFunc<T, BaseParticles, GenericParticles>
|
||||||
): Particles<T> {
|
): Particles<T> {
|
||||||
return createLazyProxy(() => {
|
return createLazyProxy(feature => {
|
||||||
const particles = optionsFunc?.() ?? ({} as ReturnType<NonNullable<typeof optionsFunc>>);
|
const particles =
|
||||||
|
optionsFunc?.call(feature, feature) ??
|
||||||
|
({} as ReturnType<NonNullable<typeof optionsFunc>>);
|
||||||
particles.id = getUniqueID("particles-");
|
particles.id = getUniqueID("particles-");
|
||||||
particles.type = ParticlesType;
|
particles.type = ParticlesType;
|
||||||
particles[Component] = ParticlesComponent as GenericComponent;
|
particles[Component] = ParticlesComponent as GenericComponent;
|
||||||
|
|
|
@ -135,8 +135,8 @@ export function createRepeatable<T extends RepeatableOptions>(
|
||||||
): Repeatable<T> {
|
): Repeatable<T> {
|
||||||
const amount = persistent<DecimalSource>(0);
|
const amount = persistent<DecimalSource>(0);
|
||||||
const decoratedData = decorators.reduce((current, next) => Object.assign(current, next.getPersistentData?.()), {});
|
const decoratedData = decorators.reduce((current, next) => Object.assign(current, next.getPersistentData?.()), {});
|
||||||
return createLazyProxy(() => {
|
return createLazyProxy(feature => {
|
||||||
const repeatable = optionsFunc();
|
const repeatable = optionsFunc.call(feature, feature);
|
||||||
|
|
||||||
repeatable.id = getUniqueID("repeatable-");
|
repeatable.id = getUniqueID("repeatable-");
|
||||||
repeatable.type = RepeatableType;
|
repeatable.type = RepeatableType;
|
||||||
|
|
|
@ -54,8 +54,8 @@ export type GenericReset = Reset<ResetOptions>;
|
||||||
export function createReset<T extends ResetOptions>(
|
export function createReset<T extends ResetOptions>(
|
||||||
optionsFunc: OptionsFunc<T, BaseReset, GenericReset>
|
optionsFunc: OptionsFunc<T, BaseReset, GenericReset>
|
||||||
): Reset<T> {
|
): Reset<T> {
|
||||||
return createLazyProxy(() => {
|
return createLazyProxy(feature => {
|
||||||
const reset = optionsFunc();
|
const reset = optionsFunc.call(feature, feature);
|
||||||
reset.id = getUniqueID("reset-");
|
reset.id = getUniqueID("reset-");
|
||||||
reset.type = ResetType;
|
reset.type = ResetType;
|
||||||
|
|
||||||
|
|
|
@ -62,8 +62,8 @@ export type GenericTab = Tab<TabOptions>;
|
||||||
export function createTab<T extends TabOptions>(
|
export function createTab<T extends TabOptions>(
|
||||||
optionsFunc: OptionsFunc<T, BaseTab, GenericTab>
|
optionsFunc: OptionsFunc<T, BaseTab, GenericTab>
|
||||||
): Tab<T> {
|
): Tab<T> {
|
||||||
return createLazyProxy(() => {
|
return createLazyProxy(feature => {
|
||||||
const tab = optionsFunc();
|
const tab = optionsFunc.call(feature, feature);
|
||||||
tab.id = getUniqueID("tab-");
|
tab.id = getUniqueID("tab-");
|
||||||
tab.type = TabType;
|
tab.type = TabType;
|
||||||
tab[Component] = TabComponent as GenericComponent;
|
tab[Component] = TabComponent as GenericComponent;
|
||||||
|
|
|
@ -156,8 +156,10 @@ export function createTabFamily<T extends TabFamilyOptions>(
|
||||||
}
|
}
|
||||||
|
|
||||||
const selected = persistent(Object.keys(tabs)[0], false);
|
const selected = persistent(Object.keys(tabs)[0], false);
|
||||||
return createLazyProxy(() => {
|
return createLazyProxy(feature => {
|
||||||
const tabFamily = optionsFunc?.() ?? ({} as ReturnType<NonNullable<typeof optionsFunc>>);
|
const tabFamily =
|
||||||
|
optionsFunc?.call(feature, feature) ??
|
||||||
|
({} as ReturnType<NonNullable<typeof optionsFunc>>);
|
||||||
|
|
||||||
tabFamily.id = getUniqueID("tabFamily-");
|
tabFamily.id = getUniqueID("tabFamily-");
|
||||||
tabFamily.type = TabFamilyType;
|
tabFamily.type = TabFamilyType;
|
||||||
|
|
|
@ -106,8 +106,10 @@ export function createTreeNode<T extends TreeNodeOptions>(
|
||||||
...decorators: Decorator<T, BaseTreeNode, GenericTreeNode>[]
|
...decorators: Decorator<T, BaseTreeNode, GenericTreeNode>[]
|
||||||
): TreeNode<T> {
|
): TreeNode<T> {
|
||||||
const decoratedData = decorators.reduce((current, next) => Object.assign(current, next.getPersistentData?.()), {});
|
const decoratedData = decorators.reduce((current, next) => Object.assign(current, next.getPersistentData?.()), {});
|
||||||
return createLazyProxy(() => {
|
return createLazyProxy(feature => {
|
||||||
const treeNode = optionsFunc?.() ?? ({} as ReturnType<NonNullable<typeof optionsFunc>>);
|
const treeNode =
|
||||||
|
optionsFunc?.call(feature, feature) ??
|
||||||
|
({} as ReturnType<NonNullable<typeof optionsFunc>>);
|
||||||
treeNode.id = getUniqueID("treeNode-");
|
treeNode.id = getUniqueID("treeNode-");
|
||||||
treeNode.type = TreeNodeType;
|
treeNode.type = TreeNodeType;
|
||||||
treeNode[Component] = TreeNodeComponent as GenericComponent;
|
treeNode[Component] = TreeNodeComponent as GenericComponent;
|
||||||
|
@ -257,8 +259,8 @@ export type GenericTree = Replace<
|
||||||
export function createTree<T extends TreeOptions>(
|
export function createTree<T extends TreeOptions>(
|
||||||
optionsFunc: OptionsFunc<T, BaseTree, GenericTree>
|
optionsFunc: OptionsFunc<T, BaseTree, GenericTree>
|
||||||
): Tree<T> {
|
): Tree<T> {
|
||||||
return createLazyProxy(() => {
|
return createLazyProxy(feature => {
|
||||||
const tree = optionsFunc();
|
const tree = optionsFunc.call(feature, feature);
|
||||||
tree.id = getUniqueID("tree-");
|
tree.id = getUniqueID("tree-");
|
||||||
tree.type = TreeType;
|
tree.type = TreeType;
|
||||||
tree[Component] = TreeComponent as GenericComponent;
|
tree[Component] = TreeComponent as GenericComponent;
|
||||||
|
|
|
@ -123,8 +123,8 @@ export function createUpgrade<T extends UpgradeOptions>(
|
||||||
): Upgrade<T> {
|
): Upgrade<T> {
|
||||||
const bought = persistent<boolean>(false, false);
|
const bought = persistent<boolean>(false, false);
|
||||||
const decoratedData = decorators.reduce((current, next) => Object.assign(current, next.getPersistentData?.()), {});
|
const decoratedData = decorators.reduce((current, next) => Object.assign(current, next.getPersistentData?.()), {});
|
||||||
return createLazyProxy(() => {
|
return createLazyProxy(feature => {
|
||||||
const upgrade = optionsFunc();
|
const upgrade = optionsFunc.call(feature, feature);
|
||||||
upgrade.id = getUniqueID("upgrade-");
|
upgrade.id = getUniqueID("upgrade-");
|
||||||
upgrade.type = UpgradeType;
|
upgrade.type = UpgradeType;
|
||||||
upgrade[Component] = UpgradeComponent as GenericComponent;
|
upgrade[Component] = UpgradeComponent as GenericComponent;
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import { Resource } from "features/resources/resource";
|
import { Resource } from "features/resources/resource";
|
||||||
|
import { NonPersistent } from "game/persistence";
|
||||||
import Decimal, { DecimalSource, format } from "util/bignum";
|
import Decimal, { DecimalSource, format } from "util/bignum";
|
||||||
import { Computable, convertComputable, ProcessedComputable } from "util/computed";
|
import { Computable, ProcessedComputable, convertComputable } from "util/computed";
|
||||||
import { computed, ComputedRef, ref, unref } from "vue";
|
import { ComputedRef, Ref, computed, ref, unref } from "vue";
|
||||||
import * as ops from "./operations";
|
import * as ops from "./operations";
|
||||||
import type {
|
import type {
|
||||||
EvaluateFunction,
|
EvaluateFunction,
|
||||||
|
@ -58,7 +59,15 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
|
||||||
|
|
||||||
constructor(options: FormulaOptions<T>) {
|
constructor(options: FormulaOptions<T>) {
|
||||||
let readonlyProperties;
|
let readonlyProperties;
|
||||||
|
if ("inputs" in options) {
|
||||||
|
options.inputs = options.inputs.map(input =>
|
||||||
|
typeof input === "object" && NonPersistent in input ? input[NonPersistent] : input
|
||||||
|
) as T | [FormulaSource];
|
||||||
|
}
|
||||||
if ("variable" in options) {
|
if ("variable" in options) {
|
||||||
|
if (typeof options.variable === "object" && NonPersistent in options.variable) {
|
||||||
|
options.variable = options.variable[NonPersistent] as Ref<DecimalSource>;
|
||||||
|
}
|
||||||
readonlyProperties = this.setupVariable(options);
|
readonlyProperties = this.setupVariable(options);
|
||||||
} else if (!("evaluate" in options)) {
|
} else if (!("evaluate" in options)) {
|
||||||
readonlyProperties = this.setupConstant(options);
|
readonlyProperties = this.setupConstant(options);
|
||||||
|
@ -364,21 +373,30 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
|
||||||
* @param value The incoming formula value
|
* @param value The incoming formula value
|
||||||
* @param condition Whether or not to apply the modifier
|
* @param condition Whether or not to apply the modifier
|
||||||
* @param formulaModifier The modifier to apply to the incoming formula if the condition is true
|
* @param formulaModifier The modifier to apply to the incoming formula if the condition is true
|
||||||
|
* @param elseFormulaModifier An optional modifier to apply to the incoming formula if the condition is false
|
||||||
*/
|
*/
|
||||||
public static if(
|
public static if(
|
||||||
value: FormulaSource,
|
value: FormulaSource,
|
||||||
condition: Computable<boolean>,
|
condition: Computable<boolean>,
|
||||||
formulaModifier: (
|
formulaModifier: (
|
||||||
value: InvertibleFormula & IntegrableFormula & InvertibleIntegralFormula
|
value: InvertibleFormula & IntegrableFormula & InvertibleIntegralFormula
|
||||||
|
) => GenericFormula,
|
||||||
|
elseFormulaModifier?: (
|
||||||
|
value: InvertibleFormula & IntegrableFormula & InvertibleIntegralFormula
|
||||||
) => GenericFormula
|
) => GenericFormula
|
||||||
): GenericFormula {
|
): GenericFormula {
|
||||||
const lhsRef = ref<DecimalSource>(0);
|
const lhsRef = ref<DecimalSource>(0);
|
||||||
const formula = formulaModifier(Formula.variable(lhsRef));
|
const variable = Formula.variable(lhsRef);
|
||||||
|
const formula = formulaModifier(variable);
|
||||||
|
const elseFormula = elseFormulaModifier?.(variable);
|
||||||
const processedCondition = convertComputable(condition);
|
const processedCondition = convertComputable(condition);
|
||||||
function evalStep(lhs: DecimalSource) {
|
function evalStep(lhs: DecimalSource) {
|
||||||
if (unref(processedCondition)) {
|
if (unref(processedCondition)) {
|
||||||
lhsRef.value = lhs;
|
lhsRef.value = lhs;
|
||||||
return formula.evaluate();
|
return formula.evaluate();
|
||||||
|
} else if (elseFormula) {
|
||||||
|
lhsRef.value = lhs;
|
||||||
|
return elseFormula.evaluate();
|
||||||
} else {
|
} else {
|
||||||
return lhs;
|
return lhs;
|
||||||
}
|
}
|
||||||
|
@ -389,6 +407,8 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
|
||||||
}
|
}
|
||||||
if (unref(processedCondition)) {
|
if (unref(processedCondition)) {
|
||||||
return lhs.invert(formula.invert(value));
|
return lhs.invert(formula.invert(value));
|
||||||
|
} else if (elseFormula) {
|
||||||
|
return lhs.invert(elseFormula.invert(value));
|
||||||
} else {
|
} else {
|
||||||
return lhs.invert(value);
|
return lhs.invert(value);
|
||||||
}
|
}
|
||||||
|
@ -399,15 +419,17 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
|
||||||
invert: formula.isInvertible() && formula.hasVariable() ? invertStep : undefined
|
invert: formula.isInvertible() && formula.hasVariable() ? invertStep : undefined
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
/** @see {@link if} */
|
|
||||||
public static conditional(
|
public static conditional(
|
||||||
value: FormulaSource,
|
value: FormulaSource,
|
||||||
condition: Computable<boolean>,
|
condition: Computable<boolean>,
|
||||||
formulaModifier: (
|
formulaModifier: (
|
||||||
value: InvertibleFormula & IntegrableFormula & InvertibleIntegralFormula
|
value: InvertibleFormula & IntegrableFormula & InvertibleIntegralFormula
|
||||||
|
) => GenericFormula,
|
||||||
|
elseFormulaModifier?: (
|
||||||
|
value: InvertibleFormula & IntegrableFormula & InvertibleIntegralFormula
|
||||||
) => GenericFormula
|
) => GenericFormula
|
||||||
) {
|
) {
|
||||||
return Formula.if(value, condition, formulaModifier);
|
return Formula.if(value, condition, formulaModifier, elseFormulaModifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static abs(value: FormulaSource): GenericFormula {
|
public static abs(value: FormulaSource): GenericFormula {
|
||||||
|
|
|
@ -220,7 +220,7 @@ export function createLayer<T extends LayerOptions>(
|
||||||
addingLayers.push(id);
|
addingLayers.push(id);
|
||||||
persistentRefs[id] = new Set();
|
persistentRefs[id] = new Set();
|
||||||
layer.minimized = persistent(false, false);
|
layer.minimized = persistent(false, false);
|
||||||
Object.assign(layer, optionsFunc.call(layer as BaseLayer));
|
Object.assign(layer, optionsFunc.call(layer, layer as BaseLayer));
|
||||||
if (
|
if (
|
||||||
addingLayers[addingLayers.length - 1] == null ||
|
addingLayers[addingLayers.length - 1] == null ||
|
||||||
addingLayers[addingLayers.length - 1] !== id
|
addingLayers[addingLayers.length - 1] !== id
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import "components/common/modifiers.css";
|
import "components/common/modifiers.css";
|
||||||
import type { CoercableComponent } from "features/feature";
|
import type { CoercableComponent, OptionsFunc } from "features/feature";
|
||||||
import { jsx } from "features/feature";
|
import { jsx } from "features/feature";
|
||||||
import settings from "game/settings";
|
import settings from "game/settings";
|
||||||
import type { DecimalSource } from "util/bignum";
|
import type { DecimalSource } from "util/bignum";
|
||||||
|
@ -66,10 +66,13 @@ export interface AdditiveModifierOptions {
|
||||||
* @param optionsFunc Additive modifier options.
|
* @param optionsFunc Additive modifier options.
|
||||||
*/
|
*/
|
||||||
export function createAdditiveModifier<T extends AdditiveModifierOptions>(
|
export function createAdditiveModifier<T extends AdditiveModifierOptions>(
|
||||||
optionsFunc: () => T
|
optionsFunc: OptionsFunc<T>
|
||||||
): ModifierFromOptionalParams<T["description"], T["enabled"]> {
|
): ModifierFromOptionalParams<T["description"], T["enabled"]> {
|
||||||
return createLazyProxy(() => {
|
return createLazyProxy(feature => {
|
||||||
const { addend, description, enabled, smallerIsBetter } = optionsFunc();
|
const { addend, description, enabled, smallerIsBetter } = optionsFunc.call(
|
||||||
|
feature,
|
||||||
|
feature
|
||||||
|
);
|
||||||
|
|
||||||
const processedAddend = convertComputable(addend);
|
const processedAddend = convertComputable(addend);
|
||||||
const processedDescription = convertComputable(description);
|
const processedDescription = convertComputable(description);
|
||||||
|
@ -128,10 +131,13 @@ export interface MultiplicativeModifierOptions {
|
||||||
* @param optionsFunc Multiplicative modifier options.
|
* @param optionsFunc Multiplicative modifier options.
|
||||||
*/
|
*/
|
||||||
export function createMultiplicativeModifier<T extends MultiplicativeModifierOptions>(
|
export function createMultiplicativeModifier<T extends MultiplicativeModifierOptions>(
|
||||||
optionsFunc: () => T
|
optionsFunc: OptionsFunc<T>
|
||||||
): ModifierFromOptionalParams<T["description"], T["enabled"]> {
|
): ModifierFromOptionalParams<T["description"], T["enabled"]> {
|
||||||
return createLazyProxy(() => {
|
return createLazyProxy(feature => {
|
||||||
const { multiplier, description, enabled, smallerIsBetter } = optionsFunc();
|
const { multiplier, description, enabled, smallerIsBetter } = optionsFunc.call(
|
||||||
|
feature,
|
||||||
|
feature
|
||||||
|
);
|
||||||
|
|
||||||
const processedMultiplier = convertComputable(multiplier);
|
const processedMultiplier = convertComputable(multiplier);
|
||||||
const processedDescription = convertComputable(description);
|
const processedDescription = convertComputable(description);
|
||||||
|
@ -191,11 +197,11 @@ export interface ExponentialModifierOptions {
|
||||||
* @param optionsFunc Exponential modifier options.
|
* @param optionsFunc Exponential modifier options.
|
||||||
*/
|
*/
|
||||||
export function createExponentialModifier<T extends ExponentialModifierOptions>(
|
export function createExponentialModifier<T extends ExponentialModifierOptions>(
|
||||||
optionsFunc: () => T
|
optionsFunc: OptionsFunc<T>
|
||||||
): ModifierFromOptionalParams<T["description"], T["enabled"]> {
|
): ModifierFromOptionalParams<T["description"], T["enabled"]> {
|
||||||
return createLazyProxy(() => {
|
return createLazyProxy(feature => {
|
||||||
const { exponent, description, enabled, supportLowNumbers, smallerIsBetter } =
|
const { exponent, description, enabled, supportLowNumbers, smallerIsBetter } =
|
||||||
optionsFunc();
|
optionsFunc.call(feature, feature);
|
||||||
|
|
||||||
const processedExponent = convertComputable(exponent);
|
const processedExponent = convertComputable(exponent);
|
||||||
const processedDescription = convertComputable(description);
|
const processedDescription = convertComputable(description);
|
||||||
|
|
|
@ -3,6 +3,7 @@ import {
|
||||||
CoercableComponent,
|
CoercableComponent,
|
||||||
isVisible,
|
isVisible,
|
||||||
jsx,
|
jsx,
|
||||||
|
OptionsFunc,
|
||||||
Replace,
|
Replace,
|
||||||
setDefault,
|
setDefault,
|
||||||
Visibility
|
Visibility
|
||||||
|
@ -108,10 +109,10 @@ export type CostRequirement = Replace<
|
||||||
* @param optionsFunc Cost requirement options.
|
* @param optionsFunc Cost requirement options.
|
||||||
*/
|
*/
|
||||||
export function createCostRequirement<T extends CostRequirementOptions>(
|
export function createCostRequirement<T extends CostRequirementOptions>(
|
||||||
optionsFunc: () => T
|
optionsFunc: OptionsFunc<T>
|
||||||
): CostRequirement {
|
): CostRequirement {
|
||||||
return createLazyProxy(() => {
|
return createLazyProxy(feature => {
|
||||||
const req = optionsFunc() as T & Partial<Requirement>;
|
const req = optionsFunc.call(feature, feature) as T & Partial<Requirement>;
|
||||||
|
|
||||||
req.partialDisplay = amount => (
|
req.partialDisplay = amount => (
|
||||||
<span
|
<span
|
||||||
|
|
|
@ -31,14 +31,14 @@ export type Proxied<T> = NonNullable<T> extends Record<PropertyKey, unknown>
|
||||||
// Takes a function that returns an object and pretends to be that object
|
// Takes a function that returns an object and pretends to be that object
|
||||||
// Note that the object is lazily calculated
|
// Note that the object is lazily calculated
|
||||||
export function createLazyProxy<T extends object, S extends T>(
|
export function createLazyProxy<T extends object, S extends T>(
|
||||||
objectFunc: (baseObject: S) => T & S,
|
objectFunc: (this: S, baseObject: S) => T & S,
|
||||||
baseObject: S = {} as S
|
baseObject: S = {} as S
|
||||||
): T {
|
): T {
|
||||||
const obj: S & Partial<T> = baseObject;
|
const obj: S & Partial<T> = baseObject;
|
||||||
let calculated = false;
|
let calculated = false;
|
||||||
function calculateObj(): T {
|
function calculateObj(): T {
|
||||||
if (!calculated) {
|
if (!calculated) {
|
||||||
Object.assign(obj, objectFunc(obj));
|
Object.assign(obj, objectFunc.call(obj, obj));
|
||||||
calculated = true;
|
calculated = true;
|
||||||
}
|
}
|
||||||
return obj as S & T;
|
return obj as S & T;
|
||||||
|
@ -73,7 +73,7 @@ export function createLazyProxy<T extends object, S extends T>(
|
||||||
},
|
},
|
||||||
getOwnPropertyDescriptor(target, key) {
|
getOwnPropertyDescriptor(target, key) {
|
||||||
if (!calculated) {
|
if (!calculated) {
|
||||||
Object.assign(obj, objectFunc(obj));
|
Object.assign(obj, objectFunc.call(obj, obj));
|
||||||
calculated = true;
|
calculated = true;
|
||||||
}
|
}
|
||||||
return Object.getOwnPropertyDescriptor(target, key);
|
return Object.getOwnPropertyDescriptor(target, key);
|
||||||
|
|
|
@ -868,6 +868,26 @@ describe("Conditionals", () => {
|
||||||
Formula.if(variable, false, value => Formula.sqrt(value)).invert(10)
|
Formula.if(variable, false, value => Formula.sqrt(value)).invert(10)
|
||||||
).compare_tolerance(10));
|
).compare_tolerance(10));
|
||||||
});
|
});
|
||||||
|
describe("Evaluates correctly with condition false and else statement", () => {
|
||||||
|
test("Evaluates correctly", () =>
|
||||||
|
expect(
|
||||||
|
Formula.if(
|
||||||
|
constant,
|
||||||
|
false,
|
||||||
|
value => Formula.sqrt(value),
|
||||||
|
value => value.times(2)
|
||||||
|
).evaluate()
|
||||||
|
).compare_tolerance(20));
|
||||||
|
test("Inverts correctly with variable in input", () =>
|
||||||
|
expect(
|
||||||
|
Formula.if(
|
||||||
|
variable,
|
||||||
|
false,
|
||||||
|
value => Formula.sqrt(value),
|
||||||
|
value => value.times(2)
|
||||||
|
).invert(20)
|
||||||
|
).compare_tolerance(10));
|
||||||
|
});
|
||||||
|
|
||||||
describe("Evaluates correctly with condition true", () => {
|
describe("Evaluates correctly with condition true", () => {
|
||||||
test("Evaluates correctly", () =>
|
test("Evaluates correctly", () =>
|
||||||
|
|
Loading…
Reference in a new issue