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());
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|