import { globalBus } from "game/events"; import { NonPersistent, Persistent, State } from "game/persistence"; import { persistent } from "game/persistence"; import player from "game/player"; import settings from "game/settings"; import type { DecimalSource } from "util/bignum"; import Decimal, { format, formatWhole } from "util/bignum"; import type { ProcessedComputable } from "util/computed"; import { loadingSave } from "util/save"; import type { ComputedRef, Ref } from "vue"; import { computed, isRef, ref, unref, watch } from "vue"; export interface Resource extends Ref { displayName: string; precision: number; small?: boolean; } export function createResource( defaultValue: T, displayName?: string, precision?: number, small?: boolean | undefined ): Resource & Persistent & { [NonPersistent]: Resource }; export function createResource( defaultValue: Ref, displayName?: string, precision?: number, small?: boolean | undefined ): Resource; export function createResource( defaultValue: T | Ref, displayName = "points", precision = 0, small: boolean | undefined = undefined ) { const resource: Partial> = isRef(defaultValue) ? defaultValue : persistent(defaultValue); resource.displayName = displayName; resource.precision = precision; resource.small = small; if (!isRef(defaultValue)) { const nonPersistentResource = (resource as Persistent)[ NonPersistent ] as unknown as Resource; nonPersistentResource.displayName = displayName; nonPersistentResource.precision = precision; nonPersistentResource.small = small; } return resource as Resource; } export function trackBest(resource: Resource): Ref { const best = persistent(resource.value); watch(resource, amount => { if (loadingSave.value) { return; } if (Decimal.gt(amount, best.value)) { best.value = amount; } }); return best; } export function trackTotal(resource: Resource): Ref { const total = persistent(resource.value); watch(resource, (amount, prevAmount) => { if (loadingSave.value) { return; } if (Decimal.gt(amount, prevAmount)) { total.value = Decimal.add(total.value, Decimal.sub(amount, prevAmount)); } }); return total; } const tetra8 = new Decimal("10^^8"); const e100 = new Decimal("1e100"); export function trackOOMPS( resource: Resource, pointGain?: ComputedRef ): Ref { const oomps = ref(0); const oompsMag = ref(0); const lastPoints = ref(0); globalBus.on("update", diff => { oompsMag.value = 0; if (Decimal.lte(resource.value, e100)) { lastPoints.value = resource.value; return; } let curr = resource.value; let prev = lastPoints.value; lastPoints.value = curr; if (Decimal.gt(curr, prev)) { if (Decimal.gte(curr, tetra8)) { curr = Decimal.slog(curr, 1e10); prev = Decimal.slog(prev, 1e10); oomps.value = curr.sub(prev).div(diff); oompsMag.value = -1; } else { while ( Decimal.div(curr, prev).log(10).div(diff).gte("100") && oompsMag.value <= 5 && Decimal.gt(prev, 0) ) { curr = Decimal.log10(curr); prev = Decimal.log10(prev); oomps.value = curr.sub(prev).div(diff); oompsMag.value++; } } } }); const oompsString = computed(() => { if (oompsMag.value === 0) { return pointGain ? format(pointGain.value, resource.precision, resource.small) + " " + resource.displayName + "/s" : ""; } return ( format(oomps.value) + " OOM" + (oompsMag.value < 0 ? "^OOM" : "^" + oompsMag.value) + "s/sec" ); }); return oompsString; } export function displayResource(resource: Resource, overrideAmount?: DecimalSource): string { const amount = overrideAmount ?? resource.value; if (Decimal.eq(resource.precision, 0)) { return formatWhole(resource.small ? amount : Decimal.floor(amount)); } return format(amount, resource.precision, resource.small); } export function unwrapResource(resource: ProcessedComputable): Resource { if ("displayName" in resource) { return resource; } return unref(resource); }