Profectus-Demo/src/features/upgrade.ts

150 lines
4.6 KiB
TypeScript
Raw Normal View History

2022-01-13 22:25:47 -06:00
import UpgradeComponent from "@/components/features/Upgrade.vue";
import {
CoercableComponent,
Component,
findFeatures,
getUniqueID,
makePersistent,
Persistent,
2022-01-24 22:23:30 -06:00
PersistentState,
2022-01-13 22:25:47 -06:00
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 { createProxy } 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?: Computable<Resource>;
canPurchase?: Computable<boolean>;
onPurchase?: VoidFunction;
}
interface BaseUpgrade extends Persistent<boolean> {
id: string;
bought: Ref<boolean>;
canAfford: Ref<boolean>;
purchase: VoidFunction;
type: typeof UpgradeType;
[Component]: typeof UpgradeComponent;
}
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"]>;
resource: GetComputableType<T["resource"]>;
canPurchase: GetComputableTypeWithDefault<T["canPurchase"], Ref<boolean>>;
}
>;
export type GenericUpgrade = Replace<
Upgrade<UpgradeOptions>,
{
visibility: ProcessedComputable<Visibility>;
canPurchase: ProcessedComputable<boolean>;
}
>;
export function createUpgrade<T extends UpgradeOptions>(
options: T & ThisType<Upgrade<T>>
): Upgrade<T> {
const upgrade: T & Partial<BaseUpgrade> = options;
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
);
}
2022-01-24 22:23:30 -06:00
upgrade.bought = upgrade[PersistentState];
2022-01-13 22:25:47 -06:00
if (upgrade.canAfford == null) {
upgrade.canAfford = computed(
() =>
proxy.resource != null &&
proxy.cost != null &&
Decimal.gte(unref<Resource>(proxy.resource).value, unref(proxy.cost))
);
}
if (upgrade.canPurchase == null) {
upgrade.canPurchase = computed(() => unref(proxy.canAfford) && !unref(proxy.bought));
}
2022-01-24 22:23:30 -06:00
upgrade.purchase = function () {
2022-01-13 22:25:47 -06:00
if (!unref(proxy.canPurchase)) {
return;
}
if (proxy.resource != null && proxy.cost != null) {
proxy.resource.value = Decimal.sub(
unref<Resource>(proxy.resource).value,
unref(proxy.cost)
);
}
2022-01-24 22:23:30 -06:00
proxy[PersistentState].value = true;
2022-01-13 22:25:47 -06:00
proxy.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");
processComputable(upgrade as T, "canPurchase");
2022-01-24 22:23:30 -06:00
const proxy = createProxy(upgrade as unknown as Upgrade<T>);
2022-01-13 22:25:47 -06:00
return proxy;
}
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());
}
});
}