Profectus-Demo/src/features/upgrade.ts
thepaperpilot e2126472b2 Fixed runtime issues with vue
Entire demo tree has been tested and is fully functional,
including all the options and save manager functionality
2022-02-27 13:49:34 -06:00

187 lines
5.9 KiB
TypeScript

import UpgradeComponent from "@/components/features/Upgrade.vue";
import {
CoercableComponent,
Component,
findFeatures,
GatherProps,
getUniqueID,
makePersistent,
Persistent,
PersistentState,
Replace,
setDefault,
StyleValue,
Visibility
} from "@/features/feature";
import { Resource } from "@/features/resource";
import { GenericLayer } from "@/game/layers";
import Decimal, { DecimalSource } from "@/util/bignum";
import { isFunction } from "@/util/common";
import {
Computable,
GetComputableType,
GetComputableTypeWithDefault,
processComputable,
ProcessedComputable
} from "@/util/computed";
import { createLazyProxy } from "@/util/proxies";
import { computed, Ref, unref } from "vue";
export const UpgradeType = Symbol("Upgrade");
export interface UpgradeOptions {
visibility?: Computable<Visibility>;
classes?: Computable<Record<string, boolean>>;
style?: Computable<StyleValue>;
display?: Computable<
| CoercableComponent
| {
title?: CoercableComponent;
description: CoercableComponent;
effectDisplay?: CoercableComponent;
}
>;
mark?: Computable<boolean | string>;
cost?: Computable<DecimalSource>;
resource?: Resource;
canAfford?: Computable<boolean>;
onPurchase?: VoidFunction;
}
interface BaseUpgrade extends Persistent<boolean> {
id: string;
bought: Ref<boolean>;
canPurchase: Ref<boolean>;
purchase: VoidFunction;
type: typeof UpgradeType;
[Component]: typeof UpgradeComponent;
[GatherProps]: () => Record<string, unknown>;
}
export type Upgrade<T extends UpgradeOptions> = Replace<
T & BaseUpgrade,
{
visibility: GetComputableTypeWithDefault<T["visibility"], Visibility.Visible>;
classes: GetComputableType<T["classes"]>;
style: GetComputableType<T["style"]>;
display: GetComputableType<T["display"]>;
mark: GetComputableType<T["mark"]>;
cost: GetComputableType<T["cost"]>;
canAfford: GetComputableTypeWithDefault<T["canAfford"], Ref<boolean>>;
}
>;
export type GenericUpgrade = Replace<
Upgrade<UpgradeOptions>,
{
visibility: ProcessedComputable<Visibility>;
canPurchase: ProcessedComputable<boolean>;
}
>;
export function createUpgrade<T extends UpgradeOptions>(
optionsFunc: () => T & ThisType<Upgrade<T>>
): Upgrade<T> {
return createLazyProxy(() => {
const upgrade: T & Partial<BaseUpgrade> = optionsFunc();
makePersistent<boolean>(upgrade, false);
upgrade.id = getUniqueID("upgrade-");
upgrade.type = UpgradeType;
upgrade[Component] = UpgradeComponent;
if (upgrade.canPurchase == null && (upgrade.resource == null || upgrade.cost == null)) {
console.warn(
"Error: can't create upgrade without a canPurchase property or a resource and cost property",
upgrade
);
}
upgrade.bought = upgrade[PersistentState];
if (upgrade.canAfford == null) {
upgrade.canAfford = computed(() => {
const genericUpgrade = upgrade as GenericUpgrade;
return (
genericUpgrade.resource != null &&
genericUpgrade.cost != null &&
Decimal.gte(genericUpgrade.resource.value, unref(genericUpgrade.cost))
);
});
} else {
processComputable(upgrade as T, "canAfford");
}
upgrade.canPurchase = computed(
() =>
unref((upgrade as GenericUpgrade).visibility) === Visibility.Visible &&
unref((upgrade as GenericUpgrade).canAfford) &&
!unref(upgrade.bought)
);
upgrade.purchase = function () {
const genericUpgrade = upgrade as GenericUpgrade;
if (!unref(genericUpgrade.canPurchase)) {
return;
}
if (genericUpgrade.resource != null && genericUpgrade.cost != null) {
genericUpgrade.resource.value = Decimal.sub(
genericUpgrade.resource.value,
unref(genericUpgrade.cost)
);
}
genericUpgrade[PersistentState].value = true;
genericUpgrade.onPurchase?.();
};
processComputable(upgrade as T, "visibility");
setDefault(upgrade, "visibility", Visibility.Visible);
processComputable(upgrade as T, "classes");
processComputable(upgrade as T, "style");
processComputable(upgrade as T, "display");
processComputable(upgrade as T, "mark");
processComputable(upgrade as T, "cost");
processComputable(upgrade as T, "resource");
upgrade[GatherProps] = function (this: GenericUpgrade) {
const {
display,
visibility,
style,
classes,
resource,
cost,
canPurchase,
bought,
mark,
id,
purchase
} = this;
return {
display,
visibility,
style,
classes,
resource,
cost,
canPurchase,
bought,
mark,
id,
purchase
};
};
return upgrade as unknown as Upgrade<T>;
});
}
export function setupAutoPurchase(
layer: GenericLayer,
autoActive: Computable<boolean>,
upgrades: GenericUpgrade[] = []
): void {
upgrades = upgrades || findFeatures(layer, UpgradeType);
const isAutoActive = isFunction(autoActive) ? computed(autoActive) : autoActive;
layer.on("update", () => {
if (unref(isAutoActive)) {
upgrades.forEach(upgrade => upgrade.purchase());
}
});
}