Advanced Decorator first draft
This commit is contained in:
parent
2122103c0e
commit
dafbcd5a6c
3 changed files with 140 additions and 5 deletions
|
@ -23,6 +23,7 @@ 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");
|
||||
|
||||
|
@ -89,9 +90,13 @@ export type GenericBuyable = Replace<
|
|||
>;
|
||||
|
||||
export function createBuyable<T extends BuyableOptions>(
|
||||
optionsFunc: OptionsFunc<T, BaseBuyable, GenericBuyable>
|
||||
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();
|
||||
|
||||
|
@ -107,8 +112,15 @@ export function createBuyable<T extends BuyableOptions>(
|
|||
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);
|
||||
|
@ -230,6 +242,7 @@ export function createBuyable<T extends BuyableOptions>(
|
|||
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;
|
||||
|
@ -242,10 +255,15 @@ export function createBuyable<T extends BuyableOptions>(
|
|||
canClick,
|
||||
small,
|
||||
mark,
|
||||
id
|
||||
id,
|
||||
...gatheredProps
|
||||
};
|
||||
};
|
||||
|
||||
for (const decorator of decorators) {
|
||||
decorator.postConstruct?.(buyable);
|
||||
}
|
||||
|
||||
return buyable as unknown as Buyable<T>;
|
||||
});
|
||||
}
|
||||
|
|
117
src/features/decorators.ts
Normal file
117
src/features/decorators.ts
Normal file
|
@ -0,0 +1,117 @@
|
|||
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";
|
||||
import { BarOptions, BaseBar, GenericBar } from "./bars/bar";
|
||||
import { BaseBuyable, BuyableOptions, GenericBuyable } from "./buyable";
|
||||
import { BaseChallenge, ChallengeOptions, GenericChallenge } from "./challenges/challenge";
|
||||
import { BaseClickable, ClickableOptions, GenericClickable } from "./clickables/clickable";
|
||||
import { BaseMilestone, GenericMilestone, MilestoneOptions } from "./milestones/milestone";
|
||||
import { BaseUpgrade, GenericUpgrade, UpgradeOptions } from "./upgrades/upgrade";
|
||||
import { Persistent, State } from "game/persistence";
|
||||
import { computed, Ref, unref } from "vue";
|
||||
|
||||
type FeatureOptions = AchievementOptions | BarOptions | BuyableOptions | ChallengeOptions | ClickableOptions | MilestoneOptions | UpgradeOptions;
|
||||
|
||||
type BaseFeature = BaseAchievement | BaseBar | BaseBuyable | BaseChallenge | BaseClickable | BaseMilestone | BaseUpgrade;
|
||||
|
||||
type GenericFeature = GenericAchievement | GenericBar | GenericBuyable | GenericChallenge | GenericClickable | GenericMilestone | GenericUpgrade;
|
||||
|
||||
/*----====----*/
|
||||
|
||||
export type Decorator<Options extends FeatureOptions, Base extends BaseFeature, Generic extends GenericFeature, S extends State = State> = {
|
||||
getPersistents?(): Record<string, Persistent<S>>;
|
||||
preConstruct?(feature: OptionsObject<Options,Base,Generic>): void;
|
||||
postConstruct?(feature: OptionsObject<Options,Base,Generic>): void;
|
||||
getGatheredProps?(feature: OptionsObject<Options,Base,Generic>): Partial<OptionsObject<Options,Base,Generic>>
|
||||
}
|
||||
|
||||
/*----====----*/
|
||||
|
||||
// #region Effect Decorator
|
||||
export type EffectFeatureOptions = {
|
||||
effect: Computable<any>;
|
||||
}
|
||||
|
||||
export type EffectFeature<T extends EffectFeatureOptions, U extends BaseFeature> = Replace<
|
||||
T & U,
|
||||
{ effect: GetComputableType<T["effect"]>; }
|
||||
>;
|
||||
|
||||
export type GenericEffectFeature<T extends GenericFeature> = T & Replace<
|
||||
EffectFeature<EffectFeatureOptions, BaseFeature>,
|
||||
{ effect: ProcessedComputable<any>; }
|
||||
>;
|
||||
|
||||
export const effectDecorator: Decorator<FeatureOptions & EffectFeatureOptions, BaseFeature, GenericFeature & BaseFeature> = {
|
||||
postConstruct(feature) {
|
||||
processComputable(feature, "effect");
|
||||
}
|
||||
}
|
||||
// #endregion
|
||||
|
||||
/*----====----*/
|
||||
|
||||
// #region Bonus Amount Decorator
|
||||
export interface BonusFeatureOptions {
|
||||
bonusAmount: Computable<DecimalSource>;
|
||||
}
|
||||
|
||||
export type BaseBonusFeature = BaseFeature & {
|
||||
totalAmount: Ref<DecimalSource>;
|
||||
}
|
||||
|
||||
export type BonusAmountFeature<T extends BonusFeatureOptions, U extends BaseBonusFeature> = Replace<
|
||||
T & U,
|
||||
{
|
||||
bonusAmount: GetComputableType<T["bonusAmount"]>;
|
||||
}
|
||||
>;
|
||||
|
||||
export type GenericBonusFeature<T extends GenericFeature> = Replace<
|
||||
T & BonusAmountFeature<BonusFeatureOptions, BaseBonusFeature>,
|
||||
{
|
||||
bonusAmount: ProcessedComputable<DecimalSource>;
|
||||
totalAmount: ProcessedComputable<DecimalSource>;
|
||||
}
|
||||
>;
|
||||
|
||||
export const bonusAmountDecorator: Decorator<FeatureOptions & BonusFeatureOptions, BaseBonusFeature & {amount: ProcessedComputable<DecimalSource>}, GenericFeature & BaseBonusFeature & {amount: ProcessedComputable<DecimalSource>}> = {
|
||||
postConstruct(feature) {
|
||||
processComputable(feature, "bonusAmount");
|
||||
if (feature.totalAmount === undefined) {
|
||||
feature.totalAmount = computed(() => Decimal.add(
|
||||
unref(feature.amount ?? 0),
|
||||
unref(feature.bonusAmount as ProcessedComputable<DecimalSource>)
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const bonusCompletionsDecorator: Decorator<FeatureOptions & BonusFeatureOptions, BaseBonusFeature & {completions: ProcessedComputable<DecimalSource>}, GenericFeature & BaseBonusFeature & {completions: ProcessedComputable<DecimalSource>}> = {
|
||||
postConstruct(feature) {
|
||||
processComputable(feature, "bonusAmount");
|
||||
if (feature.totalAmount === undefined) {
|
||||
feature.totalAmount = computed(() => Decimal.add(
|
||||
unref(feature.completions ?? 0),
|
||||
unref(feature.bonusAmount as ProcessedComputable<DecimalSource>)
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const bonusEarnedDecorator: Decorator<FeatureOptions & BonusFeatureOptions, BaseBonusFeature & {earned: ProcessedComputable<boolean>}, GenericFeature & BaseBonusFeature & {earned: ProcessedComputable<boolean>}> = {
|
||||
postConstruct(feature) {
|
||||
processComputable(feature, "bonusAmount");
|
||||
if (feature.totalAmount === undefined) {
|
||||
feature.totalAmount = computed(() => unref(feature.earned ?? false)
|
||||
? Decimal.add(unref(feature.bonusAmount as ProcessedComputable<DecimalSource>), 1)
|
||||
: unref(feature.bonusAmount as ProcessedComputable<DecimalSource>)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
// #endregion
|
||||
|
||||
/*----====----*/
|
||||
|
|
@ -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.
|
||||
* Intended for making lazily evaluated objects.
|
||||
*/
|
||||
export type OptionsFunc<T, R = Record<string, unknown>, S = R> = () => T &
|
||||
Partial<R> &
|
||||
ThisType<T & S>;
|
||||
export type OptionsFunc<T, R = Record<string, unknown>, S = R> = () => OptionsObject<T,R,S>;
|
||||
|
||||
export type OptionsObject<T, R = Record<string, unknown>, S = R> = T & Partial<R> & ThisType<T & S>;
|
||||
|
||||
let id = 0;
|
||||
/**
|
||||
|
|
Loading…
Reference in a new issue