Add feature decorator system #13
|
@ -1,4 +1,5 @@
|
|||
import AchievementComponent from "features/achievements/Achievement.vue";
|
||||
import { Decorator } from "features/decorators";
|
||||
import {
|
||||
CoercableComponent,
|
||||
Component,
|
||||
|
@ -72,20 +73,28 @@ export type GenericAchievement = Replace<
|
|||
>;
|
||||
|
||||
export function createAchievement<T extends AchievementOptions>(
|
||||
optionsFunc?: OptionsFunc<T, BaseAchievement, GenericAchievement>
|
||||
optionsFunc?: OptionsFunc<T, BaseAchievement, GenericAchievement>,
|
||||
...decorators: Decorator<T, BaseAchievement, GenericAchievement>[]
|
||||
): Achievement<T> {
|
||||
const earned = persistent<boolean>(false);
|
||||
const decoratedData = decorators.reduce((current, next) => Object.assign(current, next.getPersistentData?.()), {});
|
||||
return createLazyProxy(() => {
|
||||
const achievement = optionsFunc?.() ?? ({} as ReturnType<NonNullable<typeof optionsFunc>>);
|
||||
achievement.id = getUniqueID("achievement-");
|
||||
achievement.type = AchievementType;
|
||||
achievement[Component] = AchievementComponent;
|
||||
|
||||
for (const decorator of decorators) {
|
||||
decorator.preConstruct?.(achievement);
|
||||
}
|
||||
|
||||
achievement.earned = earned;
|
||||
achievement.complete = function () {
|
||||
earned.value = true;
|
||||
};
|
||||
|
||||
Object.assign(achievement, decoratedData);
|
||||
|
||||
processComputable(achievement as T, "visibility");
|
||||
setDefault(achievement, "visibility", Visibility.Visible);
|
||||
processComputable(achievement as T, "display");
|
||||
|
@ -94,9 +103,14 @@ export function createAchievement<T extends AchievementOptions>(
|
|||
processComputable(achievement as T, "style");
|
||||
processComputable(achievement as T, "classes");
|
||||
|
||||
for (const decorator of decorators) {
|
||||
decorator.postConstruct?.(achievement);
|
||||
}
|
||||
|
||||
const decoratedProps = decorators.reduce((current, next) => Object.assign(current, next.getGatheredProps?.(achievement)), {});
|
||||
achievement[GatherProps] = function (this: GenericAchievement) {
|
||||
const { visibility, display, earned, image, style, classes, mark, id } = this;
|
||||
return { visibility, display, earned, image, style: unref(style), classes, mark, id };
|
||||
return { visibility, display, earned, image, style: unref(style), classes, mark, id, ...decoratedProps };
|
||||
};
|
||||
|
||||
if (achievement.shouldEarn) {
|
||||
|
|
|
@ -31,6 +31,7 @@ import { coerceComponent, isCoercableComponent, render } from "util/vue";
|
|||
import { computed, Ref, ref, unref } from "vue";
|
||||
import { BarOptions, createBar, GenericBar } from "./bars/bar";
|
||||
import { ClickableOptions } from "./clickables/clickable";
|
||||
import { Decorator } from "./decorators";
|
||||
|
||||
export const ActionType = Symbol("Action");
|
||||
|
||||
|
@ -77,9 +78,11 @@ export type GenericAction = Replace<
|
|||
>;
|
||||
|
||||
export function createAction<T extends ActionOptions>(
|
||||
optionsFunc?: OptionsFunc<T, BaseAction, GenericAction>
|
||||
optionsFunc?: OptionsFunc<T, BaseAction, GenericAction>,
|
||||
...decorators: Decorator<T, BaseAction, GenericAction>[]
|
||||
): Action<T> {
|
||||
const progress = persistent<DecimalSource>(0);
|
||||
const decoratedData = decorators.reduce((current, next) => Object.assign(current, next.getPersistentData?.()), {});
|
||||
return createLazyProxy(() => {
|
||||
const action = optionsFunc?.() ?? ({} as ReturnType<NonNullable<typeof optionsFunc>>);
|
||||
action.id = getUniqueID("action-");
|
||||
|
@ -89,8 +92,13 @@ export function createAction<T extends ActionOptions>(
|
|||
// Required because of display changing types
|
||||
const genericAction = action as unknown as GenericAction;
|
||||
|
||||
for (const decorator of decorators) {
|
||||
decorator.preConstruct?.(action);
|
||||
}
|
||||
|
||||
action.isHolding = ref(false);
|
||||
action.progress = progress;
|
||||
Object.assign(action, decoratedData);
|
||||
|
||||
processComputable(action as T, "visibility");
|
||||
setDefault(action, "visibility", Visibility.Visible);
|
||||
|
@ -202,6 +210,11 @@ export function createAction<T extends ActionOptions>(
|
|||
}
|
||||
};
|
||||
|
||||
for (const decorator of decorators) {
|
||||
decorator.postConstruct?.(action);
|
||||
}
|
||||
|
||||
const decoratedProps = decorators.reduce((current, next) => Object.assign(current, next.getGatheredProps?.(action)));
|
||||
action[GatherProps] = function (this: GenericAction) {
|
||||
const {
|
||||
display,
|
||||
|
@ -225,7 +238,8 @@ export function createAction<T extends ActionOptions>(
|
|||
canClick,
|
||||
small,
|
||||
mark,
|
||||
id
|
||||
id,
|
||||
...decoratedProps
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import BarComponent from "features/bars/Bar.vue";
|
||||
import { Decorator } from "features/decorators";
|
||||
import type {
|
||||
CoercableComponent,
|
||||
GenericComponent,
|
||||
|
@ -71,14 +72,22 @@ export type GenericBar = Replace<
|
|||
>;
|
||||
|
||||
export function createBar<T extends BarOptions>(
|
||||
optionsFunc: OptionsFunc<T, BaseBar, GenericBar>
|
||||
optionsFunc: OptionsFunc<T, BaseBar, GenericBar>,
|
||||
...decorators: Decorator<T, BaseBar, GenericBar>[]
|
||||
): Bar<T> {
|
||||
const decoratedData = decorators.reduce((current, next) => Object.assign(current, next.getPersistentData?.()), {});
|
||||
return createLazyProxy(() => {
|
||||
const bar = optionsFunc();
|
||||
bar.id = getUniqueID("bar-");
|
||||
bar.type = BarType;
|
||||
bar[Component] = BarComponent;
|
||||
|
||||
for (const decorator of decorators) {
|
||||
decorator.preConstruct?.(bar);
|
||||
}
|
||||
|
||||
Object.assign(bar, decoratedData);
|
||||
|
||||
processComputable(bar as T, "visibility");
|
||||
setDefault(bar, "visibility", Visibility.Visible);
|
||||
processComputable(bar as T, "width");
|
||||
|
@ -94,6 +103,11 @@ export function createBar<T extends BarOptions>(
|
|||
processComputable(bar as T, "display");
|
||||
processComputable(bar as T, "mark");
|
||||
|
||||
for (const decorator of decorators) {
|
||||
decorator.postConstruct?.(bar);
|
||||
}
|
||||
|
||||
const decoratedProps = decorators.reduce((current, next) => Object.assign(current, next.getGatheredProps?.(bar)), {});
|
||||
bar[GatherProps] = function (this: GenericBar) {
|
||||
const {
|
||||
progress,
|
||||
|
@ -125,7 +139,8 @@ export function createBar<T extends BarOptions>(
|
|||
baseStyle,
|
||||
fillStyle,
|
||||
mark,
|
||||
id
|
||||
id,
|
||||
...decoratedProps
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -1,269 +0,0 @@
|
|||
import ClickableComponent from "features/clickables/Clickable.vue";
|
||||
import type {
|
||||
CoercableComponent,
|
||||
GenericComponent,
|
||||
OptionsFunc,
|
||||
Replace,
|
||||
StyleValue
|
||||
} from "features/feature";
|
||||
import { Component, GatherProps, getUniqueID, jsx, setDefault, Visibility } from "features/feature";
|
||||
import type { Resource } from "features/resources/resource";
|
||||
import { DefaultValue, Persistent } from "game/persistence";
|
||||
import { persistent } from "game/persistence";
|
||||
import type { DecimalSource } from "util/bignum";
|
||||
import Decimal, { format, formatWhole } from "util/bignum";
|
||||
import type {
|
||||
Computable,
|
||||
GetComputableType,
|
||||
GetComputableTypeWithDefault,
|
||||
ProcessedComputable
|
||||
} from "util/computed";
|
||||
import { processComputable } from "util/computed";
|
||||
import { createLazyProxy } from "util/proxies";
|
||||
import { coerceComponent, isCoercableComponent } from "util/vue";
|
||||
import type { Ref } from "vue";
|
||||
import { computed, unref } from "vue";
|
||||
import { Decorator } from "./decorators";
|
||||
|
||||
export const BuyableType = Symbol("Buyable");
|
||||
|
||||
export type BuyableDisplay =
|
||||
| CoercableComponent
|
||||
| {
|
||||
title?: CoercableComponent;
|
||||
description?: CoercableComponent;
|
||||
effectDisplay?: CoercableComponent;
|
||||
showAmount?: boolean;
|
||||
};
|
||||
|
||||
export interface BuyableOptions {
|
||||
visibility?: Computable<Visibility>;
|
||||
cost?: Computable<DecimalSource>;
|
||||
resource?: Resource;
|
||||
canPurchase?: Computable<boolean>;
|
||||
purchaseLimit?: Computable<DecimalSource>;
|
||||
initialValue?: DecimalSource;
|
||||
classes?: Computable<Record<string, boolean>>;
|
||||
style?: Computable<StyleValue>;
|
||||
mark?: Computable<boolean | string>;
|
||||
small?: Computable<boolean>;
|
||||
display?: Computable<BuyableDisplay>;
|
||||
onPurchase?: (cost: DecimalSource | undefined) => void;
|
||||
}
|
||||
|
||||
export interface BaseBuyable {
|
||||
id: string;
|
||||
amount: Persistent<DecimalSource>;
|
||||
maxed: Ref<boolean>;
|
||||
canAfford: Ref<boolean>;
|
||||
canClick: ProcessedComputable<boolean>;
|
||||
onClick: VoidFunction;
|
||||
purchase: VoidFunction;
|
||||
type: typeof BuyableType;
|
||||
[Component]: typeof ClickableComponent;
|
||||
[GatherProps]: () => Record<string, unknown>;
|
||||
}
|
||||
|
||||
export type Buyable<T extends BuyableOptions> = Replace<
|
||||
T & BaseBuyable,
|
||||
{
|
||||
visibility: GetComputableTypeWithDefault<T["visibility"], Visibility.Visible>;
|
||||
cost: GetComputableType<T["cost"]>;
|
||||
resource: GetComputableType<T["resource"]>;
|
||||
canPurchase: GetComputableTypeWithDefault<T["canPurchase"], Ref<boolean>>;
|
||||
purchaseLimit: GetComputableTypeWithDefault<T["purchaseLimit"], Decimal>;
|
||||
classes: GetComputableType<T["classes"]>;
|
||||
style: GetComputableType<T["style"]>;
|
||||
mark: GetComputableType<T["mark"]>;
|
||||
small: GetComputableType<T["small"]>;
|
||||
display: Ref<CoercableComponent>;
|
||||
}
|
||||
>;
|
||||
|
||||
export type GenericBuyable = Replace<
|
||||
Buyable<BuyableOptions>,
|
||||
{
|
||||
visibility: ProcessedComputable<Visibility>;
|
||||
canPurchase: ProcessedComputable<boolean>;
|
||||
purchaseLimit: ProcessedComputable<DecimalSource>;
|
||||
}
|
||||
>;
|
||||
|
||||
export function createBuyable<T extends BuyableOptions>(
|
||||
optionsFunc: OptionsFunc<T, BaseBuyable, GenericBuyable>,
|
||||
...decorators: Decorator<T, BaseBuyable, GenericBuyable>[]
|
||||
): Buyable<T> {
|
||||
const amount = persistent<DecimalSource>(0);
|
||||
|
||||
const persistents = decorators.reduce((current, next) => Object.assign(current, next.getPersistents?.()), {});
|
||||
|
||||
return createLazyProxy(() => {
|
||||
const buyable = optionsFunc();
|
||||
|
||||
if (buyable.canPurchase == null && (buyable.resource == null || buyable.cost == null)) {
|
||||
console.warn(
|
||||
"Cannot create buyable without a canPurchase property or a resource and cost property",
|
||||
buyable
|
||||
);
|
||||
throw "Cannot create buyable without a canPurchase property or a resource and cost property";
|
||||
}
|
||||
|
||||
buyable.id = getUniqueID("buyable-");
|
||||
buyable.type = BuyableType;
|
||||
buyable[Component] = ClickableComponent;
|
||||
|
||||
for (const decorator of decorators) {
|
||||
decorator.preConstruct?.(buyable);
|
||||
}
|
||||
|
||||
buyable.amount = amount;
|
||||
buyable.amount[DefaultValue] = buyable.initialValue ?? 0;
|
||||
|
||||
Object.assign(buyable, persistents);
|
||||
|
||||
buyable.canAfford = computed(() => {
|
||||
const genericBuyable = buyable as GenericBuyable;
|
||||
const cost = unref(genericBuyable.cost);
|
||||
return (
|
||||
genericBuyable.resource != null &&
|
||||
cost != null &&
|
||||
Decimal.gte(genericBuyable.resource.value, cost)
|
||||
);
|
||||
});
|
||||
if (buyable.canPurchase == null) {
|
||||
buyable.canPurchase = computed(
|
||||
() =>
|
||||
unref((buyable as GenericBuyable).visibility) === Visibility.Visible &&
|
||||
unref((buyable as GenericBuyable).canAfford) &&
|
||||
Decimal.lt(
|
||||
(buyable as GenericBuyable).amount.value,
|
||||
unref((buyable as GenericBuyable).purchaseLimit)
|
||||
)
|
||||
);
|
||||
}
|
||||
buyable.maxed = computed(() =>
|
||||
Decimal.gte(
|
||||
(buyable as GenericBuyable).amount.value,
|
||||
unref((buyable as GenericBuyable).purchaseLimit)
|
||||
)
|
||||
);
|
||||
processComputable(buyable as T, "classes");
|
||||
const classes = buyable.classes as ProcessedComputable<Record<string, boolean>> | undefined;
|
||||
buyable.classes = computed(() => {
|
||||
const currClasses = unref(classes) || {};
|
||||
if ((buyable as GenericBuyable).maxed.value) {
|
||||
currClasses.bought = true;
|
||||
}
|
||||
return currClasses;
|
||||
});
|
||||
processComputable(buyable as T, "canPurchase");
|
||||
buyable.canClick = buyable.canPurchase as ProcessedComputable<boolean>;
|
||||
buyable.onClick = buyable.purchase =
|
||||
buyable.onClick ??
|
||||
buyable.purchase ??
|
||||
function (this: GenericBuyable) {
|
||||
const genericBuyable = buyable as GenericBuyable;
|
||||
if (!unref(genericBuyable.canPurchase)) {
|
||||
return;
|
||||
}
|
||||
const cost = unref(genericBuyable.cost);
|
||||
if (genericBuyable.cost != null && genericBuyable.resource != null) {
|
||||
genericBuyable.resource.value = Decimal.sub(
|
||||
genericBuyable.resource.value,
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
cost!
|
||||
);
|
||||
genericBuyable.amount.value = Decimal.add(genericBuyable.amount.value, 1);
|
||||
}
|
||||
genericBuyable.onPurchase?.(cost);
|
||||
};
|
||||
processComputable(buyable as T, "display");
|
||||
const display = buyable.display;
|
||||
buyable.display = jsx(() => {
|
||||
// TODO once processComputable types correctly, remove this "as X"
|
||||
const currDisplay = unref(display) as BuyableDisplay;
|
||||
if (isCoercableComponent(currDisplay)) {
|
||||
const CurrDisplay = coerceComponent(currDisplay);
|
||||
return <CurrDisplay />;
|
||||
}
|
||||
if (currDisplay != null && buyable.cost != null && buyable.resource != null) {
|
||||
const genericBuyable = buyable as GenericBuyable;
|
||||
const Title = coerceComponent(currDisplay.title ?? "", "h3");
|
||||
const Description = coerceComponent(currDisplay.description ?? "");
|
||||
const EffectDisplay = coerceComponent(currDisplay.effectDisplay ?? "");
|
||||
|
||||
return (
|
||||
<span>
|
||||
{currDisplay.title == null ? null : (
|
||||
<div>
|
||||
<Title />
|
||||
</div>
|
||||
)}
|
||||
{currDisplay.description == null ? null : <Description />}
|
||||
{currDisplay.showAmount === false ? null : (
|
||||
<div>
|
||||
<br />
|
||||
{unref(genericBuyable.purchaseLimit) === Decimal.dInf ? (
|
||||
<>Amount: {formatWhole(genericBuyable.amount.value)}</>
|
||||
) : (
|
||||
<>
|
||||
Amount: {formatWhole(genericBuyable.amount.value)} /{" "}
|
||||
{formatWhole(unref(genericBuyable.purchaseLimit))}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{currDisplay.effectDisplay == null ? null : (
|
||||
<div>
|
||||
<br />
|
||||
Currently: <EffectDisplay />
|
||||
</div>
|
||||
)}
|
||||
{genericBuyable.cost != null && !genericBuyable.maxed.value ? (
|
||||
<div>
|
||||
<br />
|
||||
Cost: {format(unref(genericBuyable.cost))}{" "}
|
||||
{buyable.resource.displayName}
|
||||
</div>
|
||||
) : null}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return "";
|
||||
});
|
||||
|
||||
processComputable(buyable as T, "visibility");
|
||||
setDefault(buyable, "visibility", Visibility.Visible);
|
||||
processComputable(buyable as T, "cost");
|
||||
processComputable(buyable as T, "resource");
|
||||
processComputable(buyable as T, "purchaseLimit");
|
||||
setDefault(buyable, "purchaseLimit", Decimal.dInf);
|
||||
processComputable(buyable as T, "style");
|
||||
processComputable(buyable as T, "mark");
|
||||
processComputable(buyable as T, "small");
|
||||
|
||||
const gatheredProps = decorators.reduce((current, next) => Object.assign(current, next.getGatheredProps?.(buyable)), {});
|
||||
buyable[GatherProps] = function (this: GenericBuyable) {
|
||||
const { display, visibility, style, classes, onClick, canClick, small, mark, id } =
|
||||
this;
|
||||
return {
|
||||
display,
|
||||
visibility,
|
||||
style: unref(style),
|
||||
classes,
|
||||
onClick,
|
||||
canClick,
|
||||
small,
|
||||
mark,
|
||||
id,
|
||||
...gatheredProps
|
||||
};
|
||||
};
|
||||
|
||||
for (const decorator of decorators) {
|
||||
decorator.postConstruct?.(buyable);
|
||||
}
|
||||
|
||||
return buyable as unknown as Buyable<T>;
|
||||
});
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
import { isArray } from "@vue/shared";
|
||||
import Toggle from "components/fields/Toggle.vue";
|
||||
import ChallengeComponent from "features/challenges/Challenge.vue";
|
||||
import { Decorator } from "features/decorators";
|
||||
import type { CoercableComponent, OptionsFunc, Replace, StyleValue } from "features/feature";
|
||||
import {
|
||||
Component,
|
||||
|
@ -98,10 +99,12 @@ export type GenericChallenge = Replace<
|
|||
>;
|
||||
|
||||
export function createChallenge<T extends ChallengeOptions>(
|
||||
optionsFunc: OptionsFunc<T, BaseChallenge, GenericChallenge>
|
||||
optionsFunc: OptionsFunc<T, BaseChallenge, GenericChallenge>,
|
||||
...decorators: Decorator<T, BaseChallenge, GenericChallenge>[]
|
||||
): Challenge<T> {
|
||||
const completions = persistent(0);
|
||||
const active = persistent(false);
|
||||
const decoratedData = decorators.reduce((current, next) => Object.assign(current, next.getPersistentData?.()), {});
|
||||
return createLazyProxy(() => {
|
||||
const challenge = optionsFunc();
|
||||
|
||||
|
@ -120,8 +123,14 @@ export function createChallenge<T extends ChallengeOptions>(
|
|||
challenge.type = ChallengeType;
|
||||
challenge[Component] = ChallengeComponent;
|
||||
|
||||
for (const decorator of decorators) {
|
||||
decorator.preConstruct?.(challenge);
|
||||
}
|
||||
|
||||
challenge.completions = completions;
|
||||
challenge.active = active;
|
||||
Object.assign(challenge, decoratedData);
|
||||
|
||||
challenge.completed = computed(() =>
|
||||
Decimal.gt((challenge as GenericChallenge).completions.value, 0)
|
||||
);
|
||||
|
@ -234,6 +243,11 @@ export function createChallenge<T extends ChallengeOptions>(
|
|||
});
|
||||
}
|
||||
|
||||
for (const decorator of decorators) {
|
||||
decorator.postConstruct?.(challenge);
|
||||
}
|
||||
|
||||
const decoratedProps = decorators.reduce((current, next) => Object.assign(current, next.getGatheredProps?.(challenge)), {});
|
||||
challenge[GatherProps] = function (this: GenericChallenge) {
|
||||
const {
|
||||
active,
|
||||
|
@ -261,7 +275,8 @@ export function createChallenge<T extends ChallengeOptions>(
|
|||
canStart,
|
||||
mark,
|
||||
id,
|
||||
toggle
|
||||
toggle,
|
||||
...decoratedProps
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import ClickableComponent from "features/clickables/Clickable.vue";
|
||||
import { Decorator } from "features/decorators";
|
||||
import type {
|
||||
CoercableComponent,
|
||||
GenericComponent,
|
||||
|
@ -67,14 +68,22 @@ export type GenericClickable = Replace<
|
|||
>;
|
||||
|
||||
export function createClickable<T extends ClickableOptions>(
|
||||
optionsFunc?: OptionsFunc<T, BaseClickable, GenericClickable>
|
||||
optionsFunc?: OptionsFunc<T, BaseClickable, GenericClickable>,
|
||||
...decorators: Decorator<T, BaseClickable, GenericClickable>[]
|
||||
): Clickable<T> {
|
||||
const decoratedData = decorators.reduce((current, next) => Object.assign(current, next.getPersistentData?.()), {});
|
||||
return createLazyProxy(() => {
|
||||
const clickable = optionsFunc?.() ?? ({} as ReturnType<NonNullable<typeof optionsFunc>>);
|
||||
clickable.id = getUniqueID("clickable-");
|
||||
clickable.type = ClickableType;
|
||||
clickable[Component] = ClickableComponent;
|
||||
|
||||
for (const decorator of decorators) {
|
||||
decorator.preConstruct?.(clickable);
|
||||
}
|
||||
|
||||
Object.assign(clickable, decoratedData);
|
||||
|
||||
processComputable(clickable as T, "visibility");
|
||||
setDefault(clickable, "visibility", Visibility.Visible);
|
||||
processComputable(clickable as T, "canClick");
|
||||
|
@ -101,6 +110,11 @@ export function createClickable<T extends ClickableOptions>(
|
|||
};
|
||||
}
|
||||
|
||||
for (const decorator of decorators) {
|
||||
decorator.postConstruct?.(clickable);
|
||||
}
|
||||
|
||||
const decoratedProps = decorators.reduce((current, next) => Object.assign(current, next.getGatheredProps?.(clickable)), {});
|
||||
clickable[GatherProps] = function (this: GenericClickable) {
|
||||
const {
|
||||
display,
|
||||
|
@ -124,7 +138,8 @@ export function createClickable<T extends ClickableOptions>(
|
|||
canClick,
|
||||
small,
|
||||
mark,
|
||||
id
|
||||
id,
|
||||
...decoratedProps
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import { convertComputable, processComputable } from "util/computed";
|
|||
import { createLazyProxy } from "util/proxies";
|
||||
import type { Ref } from "vue";
|
||||
import { computed, unref } from "vue";
|
||||
import { Decorator } from "./decorators";
|
||||
|
||||
/** An object that configures a {@link Conversion}. */
|
||||
export interface ConversionOptions {
|
||||
|
@ -135,11 +136,16 @@ export type GenericConversion = Replace<
|
|||
* @see {@link createIndependentConversion}.
|
||||
*/
|
||||
export function createConversion<T extends ConversionOptions>(
|
||||
optionsFunc: OptionsFunc<T, BaseConversion, GenericConversion>
|
||||
optionsFunc: OptionsFunc<T, BaseConversion, GenericConversion>,
|
||||
...decorators: Decorator<T, BaseConversion, GenericConversion>[]
|
||||
): Conversion<T> {
|
||||
return createLazyProxy(() => {
|
||||
const conversion = optionsFunc();
|
||||
|
||||
for (const decorator of decorators) {
|
||||
decorator.preConstruct?.(conversion);
|
||||
}
|
||||
|
||||
if (conversion.currentGain == null) {
|
||||
conversion.currentGain = computed(() => {
|
||||
let gain = conversion.gainModifier
|
||||
|
@ -201,6 +207,10 @@ export function createConversion<T extends ConversionOptions>(
|
|||
processComputable(conversion as T, "roundUpCost");
|
||||
setDefault(conversion, "roundUpCost", true);
|
||||
|
||||
for (const decorator of decorators) {
|
||||
decorator.postConstruct?.(conversion);
|
||||
}
|
||||
|
||||
return conversion as unknown as Conversion<T>;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,49 +1,35 @@
|
|||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
import { Replace, OptionsObject } from "./feature";
|
||||
import Decimal, { DecimalSource } from "util/bignum";
|
||||
import { Computable, GetComputableType, processComputable, ProcessedComputable } from "util/computed";
|
||||
import { AchievementOptions, BaseAchievement, GenericAchievement } from "./achievements/achievement";
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
import { BarOptions, BaseBar, GenericBar } from "./bars/bar";
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
import { BaseBuyable, BuyableOptions, GenericBuyable } from "./buyable";
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
import { BaseChallenge, ChallengeOptions, GenericChallenge } from "./challenges/challenge";
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
import { BaseClickable, ClickableOptions, GenericClickable } from "./clickables/clickable";
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
import { BaseMilestone, GenericMilestone, MilestoneOptions } from "./milestones/milestone";
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
import { BaseUpgrade, GenericUpgrade, UpgradeOptions } from "./upgrades/upgrade";
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
import { Persistent, State } from "game/persistence";
|
||||
import { computed, Ref, unref } from "vue";
|
||||
|
||||
type FeatureOptions = AchievementOptions | BarOptions | BuyableOptions | ChallengeOptions | ClickableOptions | MilestoneOptions | UpgradeOptions;
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
type BaseFeature = BaseAchievement | BaseBar | BaseBuyable | BaseChallenge | BaseClickable | BaseMilestone | BaseUpgrade;
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
type GenericFeature = GenericAchievement | GenericBar | GenericBuyable | GenericChallenge | GenericClickable | GenericMilestone | GenericUpgrade;
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
/*----====----*/
|
||||
|
||||
export type Decorator<Options extends FeatureOptions, Base extends BaseFeature, Generic extends GenericFeature, S extends State = State> = {
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
getPersistents?(): Record<string, Persistent<S>>;
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
preConstruct?(feature: OptionsObject<Options,Base,Generic>): void;
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
postConstruct?(feature: OptionsObject<Options,Base,Generic>): void;
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
getGatheredProps?(feature: OptionsObject<Options,Base,Generic>): Partial<OptionsObject<Options,Base,Generic>>
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
export type Decorator<FeatureOptions, BaseFeature = {}, GenericFeature = {}, S extends State = State> = {
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
getPersistentData?(): Record<string, Persistent<S>>;
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
preConstruct?(feature: OptionsObject<FeatureOptions,BaseFeature,GenericFeature>): void;
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
postConstruct?(feature: OptionsObject<FeatureOptions,BaseFeature,GenericFeature>): void;
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
getGatheredProps?(feature: OptionsObject<FeatureOptions,BaseFeature,GenericFeature>): Partial<OptionsObject<FeatureOptions,BaseFeature,GenericFeature>>
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
}
|
||||
|
||||
/*----====----*/
|
||||
|
||||
// #region Effect Decorator
|
||||
export type EffectFeatureOptions = {
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
export interface EffectFeatureOptions {
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
effect: Computable<any>;
|
||||
}
|
||||
|
||||
export type EffectFeature<T extends EffectFeatureOptions, U extends BaseFeature> = Replace<
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
T & U,
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
{ effect: GetComputableType<T["effect"]>; }
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
export type EffectFeature<T extends EffectFeatureOptions> = Replace<
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
T, { effect: GetComputableType<T["effect"]>; }
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
>;
|
||||
|
||||
export type GenericEffectFeature<T extends GenericFeature> = T & Replace<
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
EffectFeature<EffectFeatureOptions, BaseFeature>,
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
export type GenericEffectFeature = Replace<
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
EffectFeature<EffectFeatureOptions>,
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
{ effect: ProcessedComputable<any>; }
|
||||
>;
|
||||
|
||||
export const effectDecorator: Decorator<FeatureOptions & EffectFeatureOptions, BaseFeature, GenericFeature & BaseFeature> = {
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
export const effectDecorator: Decorator<EffectFeatureOptions, {}, GenericEffectFeature> = {
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
postConstruct(feature) {
|
||||
processComputable(feature, "effect");
|
||||
}
|
||||
|
@ -52,31 +38,46 @@ export const effectDecorator: Decorator<FeatureOptions & EffectFeatureOptions, B
|
|||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
|
||||
/*----====----*/
|
||||
|
||||
// #region Bonus Amount Decorator
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
export interface BonusFeatureOptions {
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
// #region Bonus Amount/Completions Decorator
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
export interface BonusAmountFeatureOptions {
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
bonusAmount: Computable<DecimalSource>;
|
||||
}
|
||||
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
export type BaseBonusFeature = BaseFeature & {
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
totalAmount: Ref<DecimalSource>;
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
export interface BonusCompletionsFeatureOptions {
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
bonusCompletions: Computable<DecimalSource>;
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
}
|
||||
|
||||
export type BonusAmountFeature<T extends BonusFeatureOptions, U extends BaseBonusFeature> = Replace<
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
T & U,
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
{
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
bonusAmount: GetComputableType<T["bonusAmount"]>;
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
}
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
export interface BaseBonusAmountFeature {
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
amount: Ref<DecimalSource>;
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
totalAmount: Ref<DecimalSource>;
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
}
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
export interface BaseBonusCompletionsFeature {
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
completions: Ref<DecimalSource>;
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
totalCompletions: Ref<DecimalSource>;
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
}
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
export type BonusAmountFeature<T extends BonusAmountFeatureOptions> = Replace<
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
T, { bonusAmount: GetComputableType<T["bonusAmount"]>; }
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
>;
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
export type BonusCompletionsFeature<T extends BonusCompletionsFeatureOptions> = Replace<
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
T, { bonusAmount: GetComputableType<T["bonusCompletions"]>; }
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
>;
|
||||
|
||||
export type GenericBonusFeature<T extends GenericFeature> = Replace<
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
T & BonusAmountFeature<BonusFeatureOptions, BaseBonusFeature>,
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
export type GenericBonusAmountFeature = Replace<
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
BonusAmountFeature<BonusAmountFeatureOptions>,
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
{
|
||||
bonusAmount: ProcessedComputable<DecimalSource>;
|
||||
totalAmount: ProcessedComputable<DecimalSource>;
|
||||
}
|
||||
>;
|
||||
export type GenericBonusCompletionsFeature = Replace<
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
BonusCompletionsFeature<BonusCompletionsFeatureOptions>,
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
{
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
bonusCompletions: ProcessedComputable<DecimalSource>;
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
totalCompletions: ProcessedComputable<DecimalSource>;
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
}
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
>;
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
|
||||
export const bonusAmountDecorator: Decorator<FeatureOptions & BonusFeatureOptions, BaseBonusFeature & {amount: ProcessedComputable<DecimalSource>}, GenericFeature & BaseBonusFeature & {amount: ProcessedComputable<DecimalSource>}> = {
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
export const bonusAmountDecorator: Decorator<BonusAmountFeatureOptions, BaseBonusAmountFeature, GenericBonusAmountFeature> = {
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
postConstruct(feature) {
|
||||
processComputable(feature, "bonusAmount");
|
||||
if (feature.totalAmount === undefined) {
|
||||
|
@ -87,30 +88,17 @@ export const bonusAmountDecorator: Decorator<FeatureOptions & BonusFeatureOption
|
|||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
export const bonusCompletionsDecorator: Decorator<FeatureOptions & BonusFeatureOptions, BaseBonusFeature & {completions: ProcessedComputable<DecimalSource>}, GenericFeature & BaseBonusFeature & {completions: ProcessedComputable<DecimalSource>}> = {
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
export const bonusCompletionsDecorator: Decorator<BonusAmountFeatureOptions, BaseBonusCompletionsFeature, GenericBonusCompletionsFeature> = {
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
postConstruct(feature) {
|
||||
processComputable(feature, "bonusAmount");
|
||||
if (feature.totalAmount === undefined) {
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
feature.totalAmount = computed(() => Decimal.add(
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
if (feature.totalCompletions === undefined) {
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
feature.totalCompletions = computed(() => Decimal.add(
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
unref(feature.completions ?? 0),
|
||||
unref(feature.bonusAmount as ProcessedComputable<DecimalSource>)
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
export const bonusEarnedDecorator: Decorator<FeatureOptions & BonusFeatureOptions, BaseBonusFeature & {earned: ProcessedComputable<boolean>}, GenericFeature & BaseBonusFeature & {earned: ProcessedComputable<boolean>}> = {
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
postConstruct(feature) {
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
processComputable(feature, "bonusAmount");
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
if (feature.totalAmount === undefined) {
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
feature.totalAmount = computed(() => unref(feature.earned ?? false)
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
? Decimal.add(unref(feature.bonusAmount as ProcessedComputable<DecimalSource>), 1)
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
: unref(feature.bonusAmount as ProcessedComputable<DecimalSource>)
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
);
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
}
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
}
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
}
|
||||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
||||
// #endregion
|
||||
|
||||
/*----====----*/
|
||||
|
|
|||
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
![]() I think the specific decorators probably belong in their own files, or in I think the specific decorators probably belong in their own files, or in `common.tsx` if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).
|
|
@ -1,4 +1,5 @@
|
|||
import Select from "components/fields/Select.vue";
|
||||
import { Decorator } from "features/decorators";
|
||||
import type {
|
||||
CoercableComponent,
|
||||
GenericComponent,
|
||||
|
@ -92,16 +93,23 @@ export type GenericMilestone = Replace<
|
|||
>;
|
||||
|
||||
export function createMilestone<T extends MilestoneOptions>(
|
||||
optionsFunc?: OptionsFunc<T, BaseMilestone, GenericMilestone>
|
||||
optionsFunc?: OptionsFunc<T, BaseMilestone, GenericMilestone>,
|
||||
...decorators: Decorator<T, BaseMilestone, GenericMilestone>[]
|
||||
): Milestone<T> {
|
||||
const earned = persistent<boolean>(false);
|
||||
const decoratedData = decorators.reduce((current, next) => Object.assign(current, next.getPersistentData?.()), {});
|
||||
return createLazyProxy(() => {
|
||||
const milestone = optionsFunc?.() ?? ({} as ReturnType<NonNullable<typeof optionsFunc>>);
|
||||
milestone.id = getUniqueID("milestone-");
|
||||
milestone.type = MilestoneType;
|
||||
milestone[Component] = MilestoneComponent;
|
||||
|
||||
for (const decorator of decorators) {
|
||||
decorator.preConstruct?.(milestone);
|
||||
}
|
||||
|
||||
milestone.earned = earned;
|
||||
Object.assign(milestone, decoratedData);
|
||||
milestone.complete = function () {
|
||||
const genericMilestone = milestone as GenericMilestone;
|
||||
earned.value = true;
|
||||
|
@ -160,9 +168,14 @@ export function createMilestone<T extends MilestoneOptions>(
|
|||
processComputable(milestone as T, "display");
|
||||
processComputable(milestone as T, "showPopups");
|
||||
|
||||
for (const decorator of decorators) {
|
||||
decorator.postConstruct?.(milestone);
|
||||
}
|
||||
|
||||
const decoratedProps = decorators.reduce((current, next) => Object.assign(current, next?.getGatheredProps?.(milestone)), {});
|
||||
milestone[GatherProps] = function (this: GenericMilestone) {
|
||||
const { visibility, display, style, classes, earned, id } = this;
|
||||
return { visibility, display, style: unref(style), classes, earned, id };
|
||||
return { visibility, display, style: unref(style), classes, earned, id, ...decoratedProps };
|
||||
};
|
||||
|
||||
if (milestone.shouldEarn) {
|
||||
|
|
|
@ -24,6 +24,7 @@ import { createLazyProxy } from "util/proxies";
|
|||
import { coerceComponent, isCoercableComponent } from "util/vue";
|
||||
import type { Ref } from "vue";
|
||||
import { computed, unref } from "vue";
|
||||
import { Decorator, GenericBonusAmountFeature } from "./decorators";
|
||||
|
||||
/** A symbol used to identify {@link Repeatable} features. */
|
||||
export const RepeatableType = Symbol("Repeatable");
|
||||
|
@ -118,9 +119,11 @@ export type GenericRepeatable = Replace<
|
|||
* @param optionsFunc Repeatable options.
|
||||
*/
|
||||
export function createRepeatable<T extends RepeatableOptions>(
|
||||
optionsFunc: OptionsFunc<T, BaseRepeatable, GenericRepeatable>
|
||||
optionsFunc: OptionsFunc<T, BaseRepeatable, GenericRepeatable>,
|
||||
...decorators: Decorator<T, BaseRepeatable, GenericRepeatable>[]
|
||||
): Repeatable<T> {
|
||||
const amount = persistent<DecimalSource>(0);
|
||||
const decoratedData = decorators.reduce((current, next) => Object.assign(current, next.getPersistentData?.()), {});
|
||||
return createLazyProxy(() => {
|
||||
const repeatable = optionsFunc();
|
||||
|
||||
|
@ -128,9 +131,15 @@ export function createRepeatable<T extends RepeatableOptions>(
|
|||
repeatable.type = RepeatableType;
|
||||
repeatable[Component] = ClickableComponent;
|
||||
|
||||
for (const decorator of decorators) {
|
||||
decorator.preConstruct?.(repeatable);
|
||||
}
|
||||
|
||||
repeatable.amount = amount;
|
||||
repeatable.amount[DefaultValue] = repeatable.initialAmount ?? 0;
|
||||
|
||||
Object.assign(repeatable, decoratedData);
|
||||
|
||||
const limitRequirement = {
|
||||
requirementMet: computed(() =>
|
||||
Decimal.sub(
|
||||
|
@ -212,14 +221,17 @@ export function createRepeatable<T extends RepeatableOptions>(
|
|||
{currDisplay.showAmount === false ? null : (
|
||||
<div>
|
||||
<br />
|
||||
{unref(genericRepeatable.limit) === Decimal.dInf ? (
|
||||
<>Amount: {formatWhole(genericRepeatable.amount.value)}</>
|
||||
) : (
|
||||
<>
|
||||
Amount: {formatWhole(genericRepeatable.amount.value)} /{" "}
|
||||
{formatWhole(unref(genericRepeatable.limit))}
|
||||
</>
|
||||
)}
|
||||
joinJSX(
|
||||
<>Amount: {formatWhole(genericRepeatable.amount.value)}</>,
|
||||
{unref(genericRepeatable.limit) !== Decimal.dInf ? (
|
||||
<> / {formatWhole(unref(genericRepeatable.limit))}</>
|
||||
) : undefined},
|
||||
{(genericRepeatable as GenericRepeatable & GenericBonusAmountFeature).bonusAmount == null ? null : (
|
||||
Decimal.gt(unref((genericRepeatable as GenericRepeatable & GenericBonusAmountFeature).bonusAmount), 0) ? (
|
||||
<> + {formatWhole(unref((genericRepeatable as GenericRepeatable & GenericBonusAmountFeature).bonusAmount))}</>
|
||||
) : undefined)
|
||||
}
|
||||
)
|
||||
</div>
|
||||
)}
|
||||
{currDisplay.effectDisplay == null ? null : (
|
||||
|
@ -254,6 +266,11 @@ export function createRepeatable<T extends RepeatableOptions>(
|
|||
processComputable(repeatable as T, "small");
|
||||
processComputable(repeatable as T, "maximize");
|
||||
|
||||
for (const decorator of decorators) {
|
||||
decorator.postConstruct?.(repeatable);
|
||||
}
|
||||
|
||||
const decoratedProps = decorators.reduce((current, next) => Object.assign(current, next.getGatheredProps?.(repeatable)), {});
|
||||
repeatable[GatherProps] = function (this: GenericRepeatable) {
|
||||
const { display, visibility, style, classes, onClick, canClick, small, mark, id } =
|
||||
this;
|
||||
|
@ -266,7 +283,8 @@ export function createRepeatable<T extends RepeatableOptions>(
|
|||
canClick,
|
||||
small,
|
||||
mark,
|
||||
id
|
||||
id,
|
||||
...decoratedProps
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { Decorator } from "features/decorators";
|
||||
import type { CoercableComponent, OptionsFunc, Replace, StyleValue } from "features/feature";
|
||||
import { Component, GatherProps, getUniqueID, setDefault, Visibility } from "features/feature";
|
||||
import type { Link } from "features/links/links";
|
||||
|
@ -66,14 +67,22 @@ export type GenericTreeNode = Replace<
|
|||
>;
|
||||
|
||||
export function createTreeNode<T extends TreeNodeOptions>(
|
||||
optionsFunc?: OptionsFunc<T, BaseTreeNode, GenericTreeNode>
|
||||
optionsFunc?: OptionsFunc<T, BaseTreeNode, GenericTreeNode>,
|
||||
...decorators: Decorator<T, BaseTreeNode, GenericTreeNode>[]
|
||||
): TreeNode<T> {
|
||||
const decoratedData = decorators.reduce((current, next) => Object.assign(current, next.getPersistentData?.()), {});
|
||||
return createLazyProxy(() => {
|
||||
const treeNode = optionsFunc?.() ?? ({} as ReturnType<NonNullable<typeof optionsFunc>>);
|
||||
treeNode.id = getUniqueID("treeNode-");
|
||||
treeNode.type = TreeNodeType;
|
||||
treeNode[Component] = TreeNodeComponent;
|
||||
|
||||
for (const decorator of decorators) {
|
||||
decorator.preConstruct?.(treeNode);
|
||||
}
|
||||
|
||||
Object.assign(decoratedData);
|
||||
|
||||
processComputable(treeNode as T, "visibility");
|
||||
setDefault(treeNode, "visibility", Visibility.Visible);
|
||||
processComputable(treeNode as T, "canClick");
|
||||
|
@ -85,6 +94,10 @@ export function createTreeNode<T extends TreeNodeOptions>(
|
|||
processComputable(treeNode as T, "style");
|
||||
processComputable(treeNode as T, "mark");
|
||||
|
||||
for (const decorator of decorators) {
|
||||
decorator.postConstruct?.(treeNode);
|
||||
}
|
||||
|
||||
if (treeNode.onClick) {
|
||||
const onClick = treeNode.onClick.bind(treeNode);
|
||||
treeNode.onClick = function (e) {
|
||||
|
@ -102,6 +115,7 @@ export function createTreeNode<T extends TreeNodeOptions>(
|
|||
};
|
||||
}
|
||||
|
||||
const decoratedProps = decorators.reduce((current, next) => Object.assign(current, next.getGatheredProps?.(treeNode)), {});
|
||||
treeNode[GatherProps] = function (this: GenericTreeNode) {
|
||||
const {
|
||||
display,
|
||||
|
@ -127,7 +141,8 @@ export function createTreeNode<T extends TreeNodeOptions>(
|
|||
glowColor,
|
||||
canClick,
|
||||
mark,
|
||||
id
|
||||
id,
|
||||
...decoratedProps
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { isArray } from "@vue/shared";
|
||||
import { Decorator } from "features/decorators";
|
||||
import type {
|
||||
CoercableComponent,
|
||||
GenericComponent,
|
||||
|
@ -85,16 +86,24 @@ export type GenericUpgrade = Replace<
|
|||
>;
|
||||
|
||||
export function createUpgrade<T extends UpgradeOptions>(
|
||||
optionsFunc: OptionsFunc<T, BaseUpgrade, GenericUpgrade>
|
||||
optionsFunc: OptionsFunc<T, BaseUpgrade, GenericUpgrade>,
|
||||
...decorators: Decorator<T, BaseUpgrade, GenericUpgrade>[]
|
||||
): Upgrade<T> {
|
||||
const bought = persistent<boolean>(false);
|
||||
const decoratedData = decorators.reduce((current, next) => Object.assign(current, next.getPersistentData?.()), {});
|
||||
return createLazyProxy(() => {
|
||||
const upgrade = optionsFunc();
|
||||
upgrade.id = getUniqueID("upgrade-");
|
||||
upgrade.type = UpgradeType;
|
||||
upgrade[Component] = UpgradeComponent;
|
||||
|
||||
for (const decorator of decorators) {
|
||||
decorator.preConstruct?.(upgrade);
|
||||
}
|
||||
|
||||
upgrade.bought = bought;
|
||||
Object.assign(upgrade, decoratedData);
|
||||
|
||||
upgrade.canPurchase = computed(() => requirementsMet(upgrade.requirements));
|
||||
upgrade.purchase = function () {
|
||||
const genericUpgrade = upgrade as GenericUpgrade;
|
||||
|
@ -120,6 +129,11 @@ export function createUpgrade<T extends UpgradeOptions>(
|
|||
processComputable(upgrade as T, "display");
|
||||
processComputable(upgrade as T, "mark");
|
||||
|
||||
for (const decorator of decorators) {
|
||||
decorator.preConstruct?.(upgrade);
|
||||
}
|
||||
|
||||
const decoratedProps = decorators.reduce((current, next) => Object.assign(current, next.getGatheredProps?.(upgrade)), {});
|
||||
upgrade[GatherProps] = function (this: GenericUpgrade) {
|
||||
const {
|
||||
display,
|
||||
|
@ -143,7 +157,8 @@ export function createUpgrade<T extends UpgradeOptions>(
|
|||
bought,
|
||||
mark,
|
||||
id,
|
||||
purchase
|
||||
purchase,
|
||||
...decoratedProps
|
||||
};
|
||||
};
|
||||
|
||||
|
|
I think the specific decorators probably belong in their own files, or in
common.tsx
if they're particularly small and useful enough to be included as part of the "base" project (as effects would be, but maybe not the bonusAmounts/completions).