import {Affinity, AffinityDisplay} from "./Messages"; export enum MeteredResource { Experience = "EXP", Health = "HP", Magic = "MP", Items = "IP", Zero = "Zero Charge", Blood = "BP", Turns = "Turns", Segments = "Segments", } export function isMeteredResource(r: Resource): r is MeteredResource { return Object.values(MeteredResource).includes(r) } export enum UnmeteredResource { Fabula = "FP", Ultima = "UP", Special = "SP", Level = "Levels", Materials = "Zenit of Materials", Zenit = "Zenit", Order = "Order", } export function formatResourceDelta(r: Resource, a: Affinity, n: number): string { const delta = `${n > 0 ? "+" : n === 0 ? "±" : ""}${n.toFixed(0)}${AffinityDisplay[a]}` if (Math.abs(n) === 1) { switch (r) { case MeteredResource.Turns: return `${delta} Turn` case MeteredResource.Segments: return `${delta} Segment` case UnmeteredResource.Level: return `${delta} Level` } } return `${delta} ${r}` } export function formatMeteredResource(r: MeteredResource, n: number, mx: number): string { const value = `${n}/${mx}` if (n === 1 && mx === 1) { switch (r) { case MeteredResource.Turns: return `${value} Turn` case MeteredResource.Segments: return `${value} Segment` } } return `${value} ${r}` } export function formatResourceValue(r: Resource, n: number): string { const value = `${n}` if (r === UnmeteredResource.Level) { return `Lv. ${value}` } if (n === 1) { switch (r) { case MeteredResource.Turns: return `${value} Turn` case MeteredResource.Segments: return `${value} Segment` } } return `${value} ${r}` } export function formatResourceMax(r: MeteredResource, n: number): string { const value = `${n}` if (n === 1) { switch (r) { case MeteredResource.Turns: return `Max ${value} Turn` case MeteredResource.Segments: return `Max ${value} Segment` } } return `Max ${value} ${r}` } export function isUnmeteredResource(r: Resource): r is UnmeteredResource { return Object.values(UnmeteredResource).includes(r) } export type Resource = MeteredResource|UnmeteredResource export function getResourceValue< KeySetT extends string|number|symbol, ObjectT extends {[key in KeySetT]?: number}, ResourceT extends string|number|symbol> (valueMap: {[key in ResourceT]: KeySetT|undefined}, object: ObjectT, resource: ResourceT): number|undefined { const field = valueMap[resource] if (typeof field === "undefined") { return undefined } return object[field] } export function setResourceValue< KeySetT extends string|number|symbol, ObjectT extends {[key in KeySetT]?: number}, ResourceT extends string|number|symbol> (valueMap: {[key in ResourceT]: KeySetT|undefined}, maxMap: {[key in ResourceT]: KeySetT|undefined}, object: ObjectT, resource: ResourceT, value: number): ObjectT { const valueField = valueMap[resource] const maxField = maxMap[resource] if (typeof valueField === "undefined") { return object } if (typeof maxField !== "undefined") { const max = getResourceValue(maxMap, object, resource) if (typeof max === "undefined") { return object } return setMeteredResource(valueMap, maxMap, object, resource, value, max) } const effectiveValue = Math.max(0, value) return { ...object, [valueField as KeySetT]: effectiveValue, } } export function applyResourceDelta< KeySetT extends string|number|symbol, ObjectT extends {[key in KeySetT]?: number}, ResourceT extends string|number|symbol> (valueMap: {[key in ResourceT]: KeySetT|undefined}, maxMap: {[key in ResourceT]: KeySetT|undefined}, object: ObjectT, resource: ResourceT, delta: number): ObjectT { const value = getResourceValue(valueMap, object, resource) if (typeof value === "undefined") { return object } return setResourceValue(valueMap, maxMap, object, resource, value + delta) } export function setResourceMax< KeySetT extends string|number|symbol, ObjectT extends {[key in KeySetT]?: number}, ResourceT extends string|number|symbol> (valueMap: {[key in ResourceT]: KeySetT|undefined}, maxMap: {[key in ResourceT]: KeySetT|undefined}, object: ObjectT, resource: ResourceT, max: number): ObjectT { const valueField = valueMap[resource] const maxField = maxMap[resource] if (typeof valueField === "undefined" || typeof maxField === "undefined") { return object } const value = getResourceValue(valueMap, object, resource) if (typeof value === "undefined") { return object } return setMeteredResource(valueMap, maxMap, object, resource, value, max) } export function setMeteredResource< KeySetT extends string|number|symbol, ObjectT extends {[key in KeySetT]?: number}, ResourceT extends string|number|symbol> (valueMap: {[key in ResourceT]: KeySetT|undefined}, maxMap: {[key in ResourceT]: KeySetT|undefined}, object: ObjectT, resource: ResourceT, value: number, max: number): ObjectT { const valueField = valueMap[resource] const maxField = maxMap[resource] if (typeof valueField === "undefined" || typeof maxField === "undefined") { return object } const effectiveMax = Math.max(0, max) const effectiveValue = Math.max(0, Math.min(value, effectiveMax)) return { ...object, [valueField as KeySetT]: effectiveValue, [maxField as KeySetT]: effectiveMax, } } export function clearResource< KeySetT extends string|number|symbol, ObjectT extends {[key in KeySetT]?: number}, ResourceT extends string|number|symbol> (valueMap: {[key in ResourceT]: KeySetT|undefined}, maxMap: {[key in ResourceT]: KeySetT|undefined}, object: ObjectT, resource: ResourceT): ObjectT { const valueField = valueMap[resource] const maxField = maxMap[resource] const isValidValue = typeof valueField !== "undefined" && object.hasOwnProperty(valueField) const isValidMax = typeof maxField !== "undefined" && object.hasOwnProperty(maxField) const result: { -readonly [key in keyof ObjectT]: ObjectT[key] } = {...object} if (isValidValue) { delete result[valueField as KeySetT] } if (isValidMax) { delete result[maxField as KeySetT] } return result as ObjectT } export interface ResourceManipulator { getValue(object: ObjectT, resource: ResourceT): number|undefined getMax(object: ObjectT, resource: ResourceT): number|undefined setValue(object: ObjectT, resource: ResourceT, value: number): ObjectT setMax(object: ObjectT, resource: ResourceT, max: number): ObjectT setMetered(object: ObjectT, resource: ResourceT, value: number, max: number): ObjectT applyDelta(object: ObjectT, resource: ResourceT, delta: number): ObjectT clear(object: ObjectT, resource: ResourceT): ObjectT } export function createResourceManipulator(valueMap: {[key in ResourceT]: KeySetT|undefined}, maxMap: {[key in ResourceT]: KeySetT|undefined}): ResourceManipulator { return { getValue(object: ObjectT, resource: ResourceT): number | undefined { return getResourceValue(valueMap, object, resource) }, applyDelta(object: ObjectT, resource: ResourceT, delta: number): ObjectT { return applyResourceDelta(valueMap, maxMap, object, resource, delta) }, getMax(object: ObjectT, resource: ResourceT): number | undefined { return getResourceValue(maxMap, object, resource) }, setMax(object: ObjectT, resource: ResourceT, max: number): ObjectT { return setResourceMax(valueMap, maxMap, object, resource, max) }, setMetered(object: ObjectT, resource: ResourceT, value: number, max: number): ObjectT { return setMeteredResource(valueMap, maxMap, object, resource, value, max); }, setValue(object: ObjectT, resource: ResourceT, value: number): ObjectT { return setResourceValue(valueMap, maxMap, object, resource, value); }, clear(object: ObjectT, resource: ResourceT): ObjectT { return clearResource(valueMap, maxMap, object, resource) } } }