parent
8f13574ee7
commit
e721129d16
File diff suppressed because it is too large
Load Diff
@ -1,849 +0,0 @@ |
||||
import grammar from "./grammar.ohm-bundle"; |
||||
import { |
||||
Affinity, |
||||
ElementalType, |
||||
FailReason, |
||||
MarkdownContext, |
||||
MarkdownOutput, |
||||
MeteredResource, |
||||
NumberSign, |
||||
Operands, |
||||
OperandsFrom, |
||||
ParseContext, |
||||
Resource, |
||||
Source, |
||||
Target, |
||||
UnmeteredResource, |
||||
} from "../model/Messages"; |
||||
import {IterationNode, Node, NonterminalNode, TerminalNode} from "ohm-js"; |
||||
import {CharacterPrivacy, CharacterSide} from "../model/Character"; |
||||
import {ClockMode, TimerDirection} from "../model/GameState"; |
||||
|
||||
export const parser = grammar.createSemantics() |
||||
|
||||
export interface ParserNode extends Node { |
||||
readonly element: ElementalType|null |
||||
readonly affinity: Affinity |
||||
readonly sign: NumberSign |
||||
readonly numberValue: number |
||||
readonly identifier: string |
||||
readonly resource: Resource|null |
||||
readonly operands: Operands |
||||
readonly blockTargets: Operands |
||||
readonly blockSources: Operands |
||||
readonly silenced: boolean |
||||
readonly currentValue: number |
||||
readonly maxValue: number |
||||
readonly failReason: FailReason |
||||
readonly side: CharacterSide |
||||
|
||||
// TODO: Implement all of the things listed below.
|
||||
readonly textValue: string |
||||
// TODO: create rules for these to describe clocks/timers/items/statuses/characters and implement them
|
||||
readonly nameText: string |
||||
readonly descriptionText: string |
||||
// TODO: use for portraits, status icons, backdrops, BGM, and SFX
|
||||
readonly url: string |
||||
readonly privacy: CharacterPrivacy |
||||
readonly clockMode: ClockMode |
||||
readonly timerDirection: TimerDirection |
||||
readonly timerDurationMs: number |
||||
|
||||
// TODO: add backdrop and music change and sfx commands
|
||||
// TODO: make sure that FP and UP spent gets saved in the appropriate counters
|
||||
evaluate(ctx: ParseContext): ParseContext |
||||
renderMarkdown(ctx: MarkdownContext): MarkdownOutput |
||||
} |
||||
|
||||
parser.addAttribute<ElementalType|null>("element", { |
||||
fireElement(): ElementalType.Fire { |
||||
return ElementalType.Fire |
||||
}, |
||||
waterElement(): ElementalType.Water { |
||||
return ElementalType.Water |
||||
}, |
||||
lightningElement(): ElementalType.Lightning { |
||||
return ElementalType.Lightning |
||||
}, |
||||
iceElement(): ElementalType.Ice { |
||||
return ElementalType.Ice |
||||
}, |
||||
earthElement(): ElementalType.Earth { |
||||
return ElementalType.Earth |
||||
}, |
||||
windElement(): ElementalType.Wind { |
||||
return ElementalType.Wind |
||||
}, |
||||
lightElement(): ElementalType.Light { |
||||
return ElementalType.Light |
||||
}, |
||||
darkElement(): ElementalType.Dark { |
||||
return ElementalType.Dark |
||||
}, |
||||
physicalElement(): ElementalType.Physical { |
||||
return ElementalType.Physical |
||||
}, |
||||
nonElement(): ElementalType.Nonelemental { |
||||
return ElementalType.Nonelemental |
||||
}, |
||||
healingElement(): ElementalType.Healing { |
||||
return ElementalType.Healing |
||||
}, |
||||
elementalType(elementNode: NonterminalNode): ElementalType { |
||||
const element = (elementNode as ParserNode).element |
||||
if (element === null) { |
||||
throw Error("Unexpectedly null element when an element was specified") |
||||
} |
||||
return element |
||||
}, |
||||
DeltaOperation(operands: NonterminalNode, space: NonterminalNode, resource: NonterminalNode, colon: NonterminalNode, delta: NonterminalNode, affinity: IterationNode, elementalType: IterationNode): ElementalType | null { |
||||
return (elementalType as ParserNode).element |
||||
}, |
||||
DeltaOperationAlternate(operands: NonterminalNode, colon: NonterminalNode, delta: NonterminalNode, affinity: IterationNode, resource: IterationNode, elementalType: IterationNode): ElementalType | null { |
||||
return (elementalType as ParserNode).element |
||||
}, |
||||
DeltaOperationAlternate2(operands: NonterminalNode, colon: NonterminalNode, resource: NonterminalNode, delta: NonterminalNode, affinity: IterationNode, elementalType: IterationNode): ElementalType | null { |
||||
return (elementalType as ParserNode).element |
||||
}, |
||||
_iter(...children: readonly Node[]): ElementalType|null { |
||||
if (this.isOptional() && children.length === 0) { |
||||
return null |
||||
} else if (this.isOptional() && children.length === 1) { |
||||
return (children[0] as ParserNode).element |
||||
} else { |
||||
throw Error(`No idea what to say ${this.ctorName} iteration node's element is when there are multiple children`) |
||||
} |
||||
}, |
||||
_nonterminal(): never { |
||||
throw Error(`No idea what to say ${this.ctorName} nonterminal node's element is`) |
||||
}, |
||||
_terminal(): never { |
||||
throw Error(`No idea what to say ${this.ctorName} terminal node's element is`) |
||||
} |
||||
}) |
||||
|
||||
parser.addAttribute<Affinity>("affinity", { |
||||
absorbAffinity(): Affinity.Absorbs { |
||||
return Affinity.Absorbs |
||||
}, |
||||
immuneAffinity(): Affinity.Immune { |
||||
return Affinity.Immune |
||||
}, |
||||
resistAffinity(): Affinity.Resistant { |
||||
return Affinity.Resistant |
||||
}, |
||||
vulnerableAffinity(): Affinity.Vulnerable { |
||||
return Affinity.Vulnerable |
||||
}, |
||||
normalAffinity(): Affinity.Normal { |
||||
return Affinity.Normal |
||||
}, |
||||
affinity(affinityNode: NonterminalNode): Affinity { |
||||
return (affinityNode as ParserNode).affinity |
||||
}, |
||||
DeltaOperation(operands: NonterminalNode, space: NonterminalNode, resource: NonterminalNode, colon: NonterminalNode, delta: NonterminalNode, affinity: IterationNode, elementalType: IterationNode): Affinity { |
||||
return (affinity as ParserNode).affinity |
||||
}, |
||||
DeltaOperationAlternate(operands: NonterminalNode, colon: NonterminalNode, delta: NonterminalNode, affinity: IterationNode, resource: IterationNode, elementalType: IterationNode): Affinity { |
||||
return (affinity as ParserNode).affinity |
||||
}, |
||||
DeltaOperationAlternate2(operands: NonterminalNode, colon: NonterminalNode, resource: NonterminalNode, delta: NonterminalNode, affinity: IterationNode, elementalType: IterationNode): Affinity { |
||||
return (affinity as ParserNode).affinity |
||||
}, |
||||
_iter(...children: readonly Node[]): Affinity { |
||||
if (this.isOptional() && children.length === 0) { |
||||
return Affinity.Normal |
||||
} else if (this.isOptional() && children.length === 1) { |
||||
return (children[0] as ParserNode).affinity |
||||
} else { |
||||
throw Error(`No idea what to say ${this.ctorName} iteration node's affinity is when there are multiple children`) |
||||
} |
||||
}, |
||||
_nonterminal(): never { |
||||
throw Error(`No idea what to say ${this.ctorName} nonterminal node's affinity is`) |
||||
}, |
||||
_terminal(): never { |
||||
throw Error(`No idea what to say ${this.ctorName} terminal node's affinity is`) |
||||
} |
||||
}) |
||||
|
||||
parser.addAttribute<NumberSign>("sign", { |
||||
deltaOperator(oper: NonterminalNode): NumberSign { |
||||
return (oper as ParserNode).sign |
||||
}, |
||||
plus(): NumberSign.Positive { |
||||
return NumberSign.Positive |
||||
}, |
||||
minus(): NumberSign.Negative { |
||||
return NumberSign.Negative |
||||
}, |
||||
StatusOrItemDeltaOperation(operands: NonterminalNode, colon: NonterminalNode, deltaOperator: NonterminalNode, identifier: NonterminalNode, counter: NonterminalNode): NumberSign { |
||||
return (deltaOperator as ParserNode).sign |
||||
}, |
||||
StatusOrItemCounterDeltaOperation(operands: NonterminalNode, colon: NonterminalNode, identifier: NonterminalNode, delta: NonterminalNode): NumberSign { |
||||
return (delta as ParserNode).sign |
||||
}, |
||||
_iter(): never { |
||||
throw Error(`No idea what to say ${this.ctorName} iteration node's number sign is`) |
||||
}, |
||||
_nonterminal(): never { |
||||
throw Error(`No idea what to say ${this.ctorName} nonterminal node's number sign is`) |
||||
}, |
||||
_terminal(): never { |
||||
throw Error(`No idea what to say ${this.ctorName} terminal node's number sign is`) |
||||
} |
||||
}) |
||||
|
||||
parser.addAttribute<number>("numberValue", { |
||||
integer(digits: IterationNode): number { |
||||
return parseInt(digits.sourceString) |
||||
}, |
||||
Delta(sign: NonterminalNode, integer: NonterminalNode): number { |
||||
const number = (integer as ParserNode).numberValue |
||||
switch ((sign as ParserNode).sign) { |
||||
case NumberSign.Negative: |
||||
return -number |
||||
case NumberSign.Positive: |
||||
return number |
||||
} |
||||
}, |
||||
DeltaOperation(operands: NonterminalNode, space: NonterminalNode, resource: NonterminalNode, colon: NonterminalNode, delta: NonterminalNode, affinity: IterationNode, elementalType: IterationNode): number { |
||||
return (delta as ParserNode).numberValue |
||||
}, |
||||
DeltaOperationAlternate(operands: NonterminalNode, colon: NonterminalNode, delta: NonterminalNode, affinity: IterationNode, resource: IterationNode, elementalType: IterationNode): number { |
||||
return (delta as ParserNode).numberValue |
||||
}, |
||||
DeltaOperationAlternate2(operands: NonterminalNode, colon: NonterminalNode, resource: NonterminalNode, delta: NonterminalNode, affinity: IterationNode, elementalType: IterationNode): number { |
||||
return (delta as ParserNode).numberValue |
||||
}, |
||||
MaxValue(separator: NonterminalNode, integer: NonterminalNode): number { |
||||
return (integer as ParserNode).numberValue; |
||||
}, |
||||
SetValueOperation(operands: NonterminalNode, space: NonterminalNode, resource: NonterminalNode, colon: NonterminalNode, integer: NonterminalNode): number { |
||||
return (integer as ParserNode).numberValue; |
||||
}, |
||||
SetValueOperationAlternate(operands: NonterminalNode, colon: NonterminalNode, integer: NonterminalNode, resource: NonterminalNode): number { |
||||
return (integer as ParserNode).numberValue; |
||||
}, |
||||
SetValueOperationAlternate2(operands: NonterminalNode, colon: NonterminalNode, resource: NonterminalNode, integer: NonterminalNode): number { |
||||
return (integer as ParserNode).numberValue; |
||||
}, |
||||
SetMaxOperation(operands: NonterminalNode, space: NonterminalNode, resource: NonterminalNode, colon: NonterminalNode, maxvalue: NonterminalNode): number { |
||||
return (maxvalue as ParserNode).numberValue; |
||||
}, |
||||
SetMaxOperationAlternate(operands: NonterminalNode, colon: NonterminalNode, maxvalue: NonterminalNode, resource: NonterminalNode): number { |
||||
return (maxvalue as ParserNode).numberValue; |
||||
}, |
||||
SetMaxOperationAlternate2(operands: NonterminalNode, colon: NonterminalNode, resource: NonterminalNode, maxvalue: NonterminalNode): number { |
||||
return (maxvalue as ParserNode).numberValue; |
||||
}, |
||||
StatusOrItemCounterUnwrapped(x: IterationNode, integer: NonterminalNode): number { |
||||
return (integer as ParserNode).numberValue |
||||
}, |
||||
StatusOrItemCounterWrapped(lParen: TerminalNode, counter: NonterminalNode, rParen: TerminalNode): number { |
||||
return (counter as ParserNode).numberValue |
||||
}, |
||||
StatusOrItemDeltaOperation(operands: NonterminalNode, colon: NonterminalNode, deltaOperator: NonterminalNode, identifier: NonterminalNode, counter: NonterminalNode): number { |
||||
return (counter as ParserNode).numberValue |
||||
}, |
||||
StatusOrItemCounterDeltaOperation(operands: NonterminalNode, colon: NonterminalNode, identifier: NonterminalNode, delta: NonterminalNode): number { |
||||
return (delta as ParserNode).numberValue |
||||
}, |
||||
StatusOrItemCounterSetOperation(operands: NonterminalNode, colon: NonterminalNode, identifier: NonterminalNode, counter: NonterminalNode): number { |
||||
return (counter as ParserNode).numberValue |
||||
}, |
||||
_iter(): never { |
||||
throw Error(`No idea what to say ${this.ctorName} iteration node's number value is`) |
||||
}, |
||||
_nonterminal(): never { |
||||
throw Error(`No idea what to say ${this.ctorName} nonterminal node's number value is`) |
||||
}, |
||||
_terminal(): never { |
||||
throw Error(`No idea what to say ${this.ctorName} terminal node's number value is`) |
||||
} |
||||
}) |
||||
|
||||
parser.addAttribute<string>("identifier", { |
||||
identifier(): string { |
||||
return this.sourceString |
||||
}, |
||||
StatusOrItemDeltaOperation(operands: NonterminalNode, colon: NonterminalNode, deltaOperator: NonterminalNode, identifier: NonterminalNode, counter: NonterminalNode): string { |
||||
return (identifier as ParserNode).identifier |
||||
}, |
||||
StatusOrItemCounterDeltaOperation(operands: NonterminalNode, colon: NonterminalNode, identifier: NonterminalNode, delta: NonterminalNode): string { |
||||
return (identifier as ParserNode).identifier |
||||
}, |
||||
StatusOrItemCounterSetOperation(operands: NonterminalNode, colon: NonterminalNode, identifier: NonterminalNode, counter: NonterminalNode): string { |
||||
return (identifier as ParserNode).identifier |
||||
}, |
||||
_iter(): never { |
||||
throw Error(`No idea what to say ${this.ctorName} iteration node's identifier value is`) |
||||
}, |
||||
_nonterminal(): never { |
||||
throw Error(`No idea what to say ${this.ctorName} nonterminal node's identifier value is`) |
||||
}, |
||||
_terminal(): never { |
||||
throw Error(`No idea what to say ${this.ctorName} terminal node's identifier value is`) |
||||
} |
||||
}) |
||||
|
||||
parser.addAttribute<Resource|null>("resource", { |
||||
BpResource(): MeteredResource.Blood { |
||||
return MeteredResource.Blood |
||||
}, |
||||
FabulaResource(): UnmeteredResource.Fabula { |
||||
return UnmeteredResource.Fabula; |
||||
}, |
||||
HpResource(): MeteredResource.Health { |
||||
return MeteredResource.Health; |
||||
}, |
||||
IpResource(): MeteredResource.Items { |
||||
return MeteredResource.Items; |
||||
}, |
||||
LevelResource(): UnmeteredResource.Level { |
||||
return UnmeteredResource.Level; |
||||
}, |
||||
MaterialsResource(): UnmeteredResource.Materials { |
||||
return UnmeteredResource.Materials; |
||||
}, |
||||
MeteredResource(res: NonterminalNode): MeteredResource { |
||||
const r = (res as ParserNode).resource; |
||||
switch (r) { |
||||
case MeteredResource.Health: |
||||
case MeteredResource.Magic: |
||||
case MeteredResource.Items: |
||||
case MeteredResource.Experience: |
||||
case MeteredResource.Zero: |
||||
case MeteredResource.Turns: |
||||
case MeteredResource.Segments: |
||||
case MeteredResource.Blood: |
||||
return r |
||||
default: |
||||
throw Error("unexpected unmetered resource in metered resource node") |
||||
} |
||||
}, |
||||
MoneyResource(): UnmeteredResource.Zenit { |
||||
return UnmeteredResource.Zenit; |
||||
}, |
||||
MpResource(): MeteredResource.Magic { |
||||
return MeteredResource.Magic; |
||||
}, |
||||
Resource(res: NonterminalNode): Resource { |
||||
const r = (res as ParserNode).resource; |
||||
if (r === null) { |
||||
throw Error("unexpected null resource in resource node") |
||||
} |
||||
return r |
||||
}, |
||||
SpResource(type: NonterminalNode): UnmeteredResource.Special|UnmeteredResource.Fabula|UnmeteredResource.Ultima { |
||||
const r = (type as ParserNode).resource; |
||||
switch (r) { |
||||
case UnmeteredResource.Special: |
||||
case UnmeteredResource.Fabula: |
||||
case UnmeteredResource.Ultima: |
||||
return r |
||||
default: |
||||
throw Error("unexpected non-SP resources in an SP node") |
||||
} |
||||
}, |
||||
SpecialResource(): UnmeteredResource.Special { |
||||
return UnmeteredResource.Special; |
||||
}, |
||||
TpResource(): MeteredResource.Turns { |
||||
return MeteredResource.Turns; |
||||
}, |
||||
UltimaResource(): Resource { |
||||
return UnmeteredResource.Ultima; |
||||
}, |
||||
UnmeteredResource(res: NonterminalNode): UnmeteredResource { |
||||
const r = (res as ParserNode).resource; |
||||
switch (r) { |
||||
case UnmeteredResource.Fabula: |
||||
case UnmeteredResource.Ultima: |
||||
case UnmeteredResource.Zenit: |
||||
case UnmeteredResource.Materials: |
||||
case UnmeteredResource.Special: |
||||
case UnmeteredResource.Level: |
||||
return r |
||||
default: |
||||
throw Error("unexpected metered resource in unmetered resource node") |
||||
} |
||||
}, |
||||
XpResource(): MeteredResource.Experience { |
||||
return MeteredResource.Experience; |
||||
}, |
||||
ZpResource(): MeteredResource.Zero { |
||||
return MeteredResource.Zero; |
||||
}, |
||||
DeltaOperation(operands: NonterminalNode, space: NonterminalNode, resource: NonterminalNode, colon: NonterminalNode, delta: NonterminalNode, affinity: IterationNode, elementalType: IterationNode): Resource { |
||||
const r = (resource as ParserNode).resource |
||||
if (r === null) { |
||||
throw Error("unexpected null resource in required resource node") |
||||
} |
||||
return r |
||||
}, |
||||
DeltaOperationAlternate(operands: NonterminalNode, colon: NonterminalNode, delta: NonterminalNode, affinity: IterationNode, resource: IterationNode, elementalType: IterationNode): Resource|null { |
||||
return (resource as ParserNode).resource |
||||
}, |
||||
DeltaOperationAlternate2(operands: NonterminalNode, colon: NonterminalNode, resource: NonterminalNode, delta: NonterminalNode, affinity: IterationNode, elementalType: IterationNode): Resource { |
||||
const r = (resource as ParserNode).resource |
||||
if (r === null) { |
||||
throw Error("unexpected null resource in required resource node") |
||||
} |
||||
return r |
||||
}, |
||||
SetValueOperation(operands: NonterminalNode, space: NonterminalNode, resource: NonterminalNode, colon: NonterminalNode, integer: NonterminalNode): Resource { |
||||
const r = (resource as ParserNode).resource |
||||
if (r === null) { |
||||
throw Error("unexpected null resource in required resource node") |
||||
} |
||||
return r |
||||
}, |
||||
SetValueOperationAlternate(operands: NonterminalNode, colon: NonterminalNode, integer: NonterminalNode, resource: NonterminalNode): Resource { |
||||
const r = (resource as ParserNode).resource |
||||
if (r === null) { |
||||
throw Error("unexpected null resource in required resource node") |
||||
} |
||||
return r |
||||
}, |
||||
SetValueOperationAlternate2(operands: NonterminalNode, colon: NonterminalNode, resource: NonterminalNode, integer: NonterminalNode): Resource { |
||||
const r = (resource as ParserNode).resource |
||||
if (r === null) { |
||||
throw Error("unexpected null resource in required resource node") |
||||
} |
||||
return r |
||||
}, |
||||
SetMaxOperation(operands: NonterminalNode, space: NonterminalNode, resource: NonterminalNode, colon: NonterminalNode, maxvalue: NonterminalNode): MeteredResource { |
||||
const r = (resource as ParserNode).resource; |
||||
switch (r) { |
||||
case MeteredResource.Health: |
||||
case MeteredResource.Magic: |
||||
case MeteredResource.Items: |
||||
case MeteredResource.Experience: |
||||
case MeteredResource.Zero: |
||||
case MeteredResource.Turns: |
||||
case MeteredResource.Segments: |
||||
case MeteredResource.Blood: |
||||
return r |
||||
default: |
||||
throw Error("unexpected unmetered resource in metered resource node") |
||||
} |
||||
}, |
||||
SetMaxOperationAlternate(operands: NonterminalNode, colon: NonterminalNode, maxvalue: NonterminalNode, resource: NonterminalNode): MeteredResource { |
||||
const r = (resource as ParserNode).resource; |
||||
switch (r) { |
||||
case MeteredResource.Health: |
||||
case MeteredResource.Magic: |
||||
case MeteredResource.Items: |
||||
case MeteredResource.Experience: |
||||
case MeteredResource.Zero: |
||||
case MeteredResource.Turns: |
||||
case MeteredResource.Segments: |
||||
case MeteredResource.Blood: |
||||
return r |
||||
default: |
||||
throw Error("unexpected unmetered resource in metered resource node") |
||||
} |
||||
}, |
||||
SetMaxOperationAlternate2(operands: NonterminalNode, colon: NonterminalNode, resource: NonterminalNode, maxvalue: NonterminalNode): MeteredResource { |
||||
const r = (resource as ParserNode).resource; |
||||
switch (r) { |
||||
case MeteredResource.Health: |
||||
case MeteredResource.Magic: |
||||
case MeteredResource.Items: |
||||
case MeteredResource.Experience: |
||||
case MeteredResource.Zero: |
||||
case MeteredResource.Turns: |
||||
case MeteredResource.Segments: |
||||
case MeteredResource.Blood: |
||||
return r |
||||
default: |
||||
throw Error("unexpected unmetered resource in metered resource node") |
||||
} |
||||
}, |
||||
|
||||
_iter(...children: readonly Node[]): Resource|null { |
||||
if (this.isOptional() && children.length === 0) { |
||||
return null |
||||
} else if (this.isOptional() && children.length === 1) { |
||||
return (children[0] as ParserNode).resource |
||||
} else { |
||||
throw Error(`No idea what to say ${this.ctorName} iteration node's resource is when there are multiple children`) |
||||
} |
||||
}, |
||||
_nonterminal(): never { |
||||
throw Error(`No idea what to say ${this.ctorName} nonterminal node's resource value is`) |
||||
}, |
||||
_terminal(): never { |
||||
throw Error(`No idea what to say ${this.ctorName} terminal node's resource value is`) |
||||
}, |
||||
SetMeteredOperation(operands: NonterminalNode, space: NonterminalNode, resource: NonterminalNode, colon: NonterminalNode, value: NonterminalNode): MeteredResource { |
||||
const r = (resource as ParserNode).resource; |
||||
switch (r) { |
||||
case MeteredResource.Health: |
||||
case MeteredResource.Magic: |
||||
case MeteredResource.Items: |
||||
case MeteredResource.Experience: |
||||
case MeteredResource.Zero: |
||||
case MeteredResource.Turns: |
||||
case MeteredResource.Segments: |
||||
case MeteredResource.Blood: |
||||
return r |
||||
default: |
||||
throw Error("unexpected unmetered resource in metered resource node") |
||||
} |
||||
}, |
||||
SetMeteredOperationAlternate(operands: NonterminalNode, colon: NonterminalNode, value: NonterminalNode, resource: NonterminalNode): MeteredResource { |
||||
const r = (resource as ParserNode).resource; |
||||
switch (r) { |
||||
case MeteredResource.Health: |
||||
case MeteredResource.Magic: |
||||
case MeteredResource.Items: |
||||
case MeteredResource.Experience: |
||||
case MeteredResource.Zero: |
||||
case MeteredResource.Turns: |
||||
case MeteredResource.Segments: |
||||
case MeteredResource.Blood: |
||||
return r |
||||
default: |
||||
throw Error("unexpected unmetered resource in metered resource node") |
||||
} |
||||
}, |
||||
SetMeteredOperationAlternate2(operands: NonterminalNode, colon: NonterminalNode, resource: NonterminalNode, value: NonterminalNode): MeteredResource { |
||||
const r = (resource as ParserNode).resource; |
||||
switch (r) { |
||||
case MeteredResource.Health: |
||||
case MeteredResource.Magic: |
||||
case MeteredResource.Items: |
||||
case MeteredResource.Experience: |
||||
case MeteredResource.Zero: |
||||
case MeteredResource.Turns: |
||||
case MeteredResource.Segments: |
||||
case MeteredResource.Blood: |
||||
return r |
||||
default: |
||||
throw Error("unexpected unmetered resource in metered resource node") |
||||
} |
||||
}, |
||||
ClearOperation(operands: NonterminalNode, space: NonterminalNode, resource: NonterminalNode, colon: NonterminalNode, nul: NonterminalNode): Resource { |
||||
const r = (resource as ParserNode).resource |
||||
if (r === null) { |
||||
throw Error("unexpected null resource in required resource node") |
||||
} |
||||
return r |
||||
}, |
||||
ClearOperationAlternate(operands: NonterminalNode, colon: NonterminalNode, nul: NonterminalNode, resource: NonterminalNode): Resource { |
||||
const r = (resource as ParserNode).resource |
||||
if (r === null) { |
||||
throw Error("unexpected null resource in required resource node") |
||||
} |
||||
return r |
||||
}, |
||||
ClearOperationAlternate2(operands: NonterminalNode, colon: NonterminalNode, resource: NonterminalNode, nul: NonterminalNode): Resource { |
||||
const r = (resource as ParserNode).resource |
||||
if (r === null) { |
||||
throw Error("unexpected null resource in required resource node") |
||||
} |
||||
return r |
||||
}, |
||||
}) |
||||
|
||||
parser.addAttribute<Operands>("operands", { |
||||
CompleteOperation(silence: IterationNode, operation: NonterminalNode, terminator: NonterminalNode): Operands { |
||||
return (operation as ParserNode).operands |
||||
}, |
||||
Operation(operation: NonterminalNode): Operands { |
||||
return (operation as ParserNode).operands |
||||
}, |
||||
ClearOperation(operands: NonterminalNode, space: NonterminalNode, resource: NonterminalNode, colon: NonterminalNode, nul: NonterminalNode): Operands { |
||||
return (operands as ParserNode).operands |
||||
}, |
||||
ClearOperationAlternate(operands: NonterminalNode, colon: NonterminalNode, nul: NonterminalNode, resource: NonterminalNode): Operands { |
||||
return (operands as ParserNode).operands |
||||
}, |
||||
ClearOperationAlternate2(operands: NonterminalNode, colon: NonterminalNode, resource: NonterminalNode, nul: NonterminalNode): Operands { |
||||
return (operands as ParserNode).operands; |
||||
}, |
||||
DeltaOperation(operands: NonterminalNode, space: NonterminalNode, resource: NonterminalNode, colon: NonterminalNode, delta: NonterminalNode, affinity: IterationNode, element: IterationNode): Operands { |
||||
return (operands as ParserNode).operands; |
||||
}, |
||||
DeltaOperationAlternate(operands: NonterminalNode, colon: NonterminalNode, delta: NonterminalNode, affinity: IterationNode, resource: IterationNode, element: IterationNode): Operands { |
||||
return (operands as ParserNode).operands; |
||||
}, |
||||
DeltaOperationAlternate2(operands: NonterminalNode, colon: NonterminalNode, resource: NonterminalNode, delta: NonterminalNode, affinity: IterationNode, element: IterationNode): Operands { |
||||
return (operands as ParserNode).operands; |
||||
}, |
||||
FailOperation(operands: NonterminalNode, colon: NonterminalNode, fail: NonterminalNode): Operands { |
||||
return (operands as ParserNode).operands; |
||||
}, |
||||
PrintOperation(): Set<never> { |
||||
return Operands<never>() |
||||
}, |
||||
SetMaxOperation(operands: NonterminalNode, space: NonterminalNode, resource: NonterminalNode, colon: NonterminalNode, max: NonterminalNode): Operands { |
||||
return (operands as ParserNode).operands |
||||
}, |
||||
SetMaxOperationAlternate(operands: NonterminalNode, colon: NonterminalNode, max: NonterminalNode, resource: NonterminalNode): Operands { |
||||
return (operands as ParserNode).operands |
||||
}, |
||||
SetMaxOperationAlternate2(operands: NonterminalNode, colon: NonterminalNode, resource: NonterminalNode, max: NonterminalNode): Operands { |
||||
return (operands as ParserNode).operands |
||||
}, |
||||
SetMeteredOperation(operands: NonterminalNode, space: NonterminalNode, resource: NonterminalNode, colon: NonterminalNode, meter: NonterminalNode): Operands { |
||||
return (operands as ParserNode).operands |
||||
}, |
||||
SetMeteredOperationAlternate(operands: NonterminalNode, colon: NonterminalNode, meter: NonterminalNode, resource: NonterminalNode): Operands { |
||||
return (operands as ParserNode).operands |
||||
}, |
||||
SetMeteredOperationAlternate2(operands: NonterminalNode, colon: NonterminalNode, resource: NonterminalNode, meter: NonterminalNode): Operands { |
||||
return (operands as ParserNode).operands |
||||
}, |
||||
SetSourceOperation(source: NonterminalNode, operands: NonterminalNode): Operands { |
||||
return (operands as ParserNode).operands |
||||
}, |
||||
SetTargetOperation(target: NonterminalNode, operands: NonterminalNode): Operands { |
||||
return (operands as ParserNode).operands |
||||
}, |
||||
SetValueOperation(operands: NonterminalNode, space: NonterminalNode, resource: NonterminalNode, colon: NonterminalNode, value: NonterminalNode): Operands { |
||||
return (operands as ParserNode).operands |
||||
}, |
||||
SetValueOperationAlternate(operands: NonterminalNode, colon: NonterminalNode, value: NonterminalNode, resource: NonterminalNode): Operands { |
||||
return (operands as ParserNode).operands |
||||
}, |
||||
SetValueOperationAlternate2(operands: NonterminalNode, colon: NonterminalNode, resource: NonterminalNode, value: NonterminalNode): Operands { |
||||
return (operands as ParserNode).operands |
||||
}, |
||||
StatusOrItemCounterDeltaOperation(operands: NonterminalNode, colon: NonterminalNode, statusOrItem: NonterminalNode, delta: NonterminalNode): Operands { |
||||
return (operands as ParserNode).operands |
||||
}, |
||||
StatusOrItemCounterSetOperation(operands: NonterminalNode, colon: NonterminalNode, statusOrItem: NonterminalNode, value: NonterminalNode): Operands { |
||||
return (operands as ParserNode).operands |
||||
}, |
||||
StatusOrItemDeltaOperation(operands: NonterminalNode, colon: NonterminalNode, delta: NonterminalNode, statusOrItem: NonterminalNode, counter: IterationNode): Operands { |
||||
return (operands as ParserNode).operands |
||||
}, |
||||
Operands(arg0: NonterminalNode): Operands { |
||||
return (arg0.asIteration() as ParserNode).operands |
||||
}, |
||||
_iter(...children: readonly Node[]): Operands { |
||||
return OperandsFrom(children.map((child) => (child as ParserNode).operands)) |
||||
}, |
||||
operand(oper: NonterminalNode): Operands { |
||||
return (oper as ParserNode).operands |
||||
}, |
||||
identifier(): Set<string> { |
||||
return Operands((this as ParserNode).identifier) |
||||
}, |
||||
target(): Set<typeof Target> { |
||||
return Operands(Target) |
||||
}, |
||||
source(): Set<typeof Source> { |
||||
return Operands(Source); |
||||
}, |
||||
["null"](): Set<never> { |
||||
return Operands() |
||||
}, |
||||
_nonterminal(): never { |
||||
throw Error(`No idea what to say ${this.ctorName} nonterminal node's operands value is`) |
||||
}, |
||||
_terminal(): never { |
||||
throw Error(`No idea what to say ${this.ctorName} terminal node's operands value is`) |
||||
}, |
||||
}) |
||||
|
||||
parser.addAttribute<Operands>("blockTargets", { |
||||
Block(start: NonterminalNode, terminator: NonterminalNode, code: NonterminalNode, end: NonterminalNode, terminator2: NonterminalNode): Operands { |
||||
return (start as ParserNode).blockTargets |
||||
}, |
||||
BlockStart(silenced: IterationNode, begin: NonterminalNode, source: IterationNode, target: IterationNode, colon: NonterminalNode, text: NonterminalNode): Operands { |
||||
return (target as ParserNode).operands |
||||
}, |
||||
_iter(): Operands { |
||||
throw Error(`No idea what to say ${this.ctorName} iteration node's block targets value is`) |
||||
}, |
||||
_nonterminal(): never { |
||||
throw Error(`No idea what to say ${this.ctorName} nonterminal node's block targets value is`) |
||||
}, |
||||
_terminal(): never { |
||||
throw Error(`No idea what to say ${this.ctorName} terminal node's block targets value is`) |
||||
}, |
||||
}) |
||||
|
||||
parser.addAttribute<Operands>("blockSources", { |
||||
Block(start: NonterminalNode, terminator: NonterminalNode, code: NonterminalNode, end: NonterminalNode, terminator2: NonterminalNode): Operands { |
||||
return (start as ParserNode).blockSources |
||||
}, |
||||
BlockStart(silenced: IterationNode, begin: NonterminalNode, source: IterationNode, target: IterationNode, colon: NonterminalNode, text: NonterminalNode): Operands { |
||||
return (source as ParserNode).operands |
||||
}, |
||||
_iter(): Operands { |
||||
throw Error(`No idea what to say ${this.ctorName} iteration node's block sources value is`) |
||||
}, |
||||
_nonterminal(): never { |
||||
throw Error(`No idea what to say ${this.ctorName} nonterminal node's block sources value is`) |
||||
}, |
||||
_terminal(): never { |
||||
throw Error(`No idea what to say ${this.ctorName} terminal node's block sources value is`) |
||||
}, |
||||
}) |
||||
|
||||
parser.addAttribute<boolean>("silenced", { |
||||
Block(start: NonterminalNode, terminator: NonterminalNode, code: NonterminalNode, end: NonterminalNode, terminator2: NonterminalNode): boolean { |
||||
return (start as ParserNode).silenced |
||||
}, |
||||
BlockStart(silenced: IterationNode, begin: NonterminalNode, source: IterationNode, target: IterationNode, colon: NonterminalNode, text: NonterminalNode): boolean { |
||||
return (target as ParserNode).silenced |
||||
}, |
||||
CompleteOperation(silenced: IterationNode, operation: NonterminalNode, terminator: NonterminalNode): boolean { |
||||
return (silenced as ParserNode).silenced |
||||
}, |
||||
silentOperator(): boolean { |
||||
return true |
||||
}, |
||||
_iter(): boolean { |
||||
if (this.isOptional() && this.children.length === 0) { |
||||
return false |
||||
} else if (this.isOptional() && this.children.length === 1) { |
||||
return (this.children[0] as ParserNode).silenced |
||||
} else { |
||||
throw Error(`No idea what to say ${this.ctorName} iteration node's element is when there are multiple children`) |
||||
} |
||||
}, |
||||
_nonterminal(): never { |
||||
throw Error(`No idea what to say ${this.ctorName} nonterminal node's silenced status is`) |
||||
}, |
||||
_terminal(): never { |
||||
throw Error(`No idea what to say ${this.ctorName} terminal node's silenced status is`) |
||||
} |
||||
}) |
||||
|
||||
parser.addAttribute<number>("currentValue", { |
||||
SetValueOperation(operands: NonterminalNode, space: NonterminalNode, resource: NonterminalNode, colon: NonterminalNode, integer: NonterminalNode): number { |
||||
return (integer as ParserNode).numberValue; |
||||
}, |
||||
SetValueOperationAlternate(operands: NonterminalNode, colon: NonterminalNode, integer: NonterminalNode, resource: NonterminalNode): number { |
||||
return (integer as ParserNode).numberValue; |
||||
}, |
||||
SetValueOperationAlternate2(operands: NonterminalNode, colon: NonterminalNode, resource: NonterminalNode, integer: NonterminalNode): number { |
||||
return (integer as ParserNode).numberValue; |
||||
}, |
||||
MeteredValue(current: NonterminalNode, max: NonterminalNode): number { |
||||
return (current as ParserNode).numberValue |
||||
}, |
||||
SetMeteredOperation(operands: NonterminalNode, space: NonterminalNode, resource: NonterminalNode, colon: NonterminalNode, value: NonterminalNode): number { |
||||
return (value as ParserNode).currentValue; |
||||
}, |
||||
SetMeteredOperationAlternate(operands: NonterminalNode, colon: NonterminalNode, value: NonterminalNode, resource: NonterminalNode): number { |
||||
return (value as ParserNode).currentValue; |
||||
}, |
||||
SetMeteredOperationAlternate2(operands: NonterminalNode, colon: NonterminalNode, resource: NonterminalNode, value: NonterminalNode): number { |
||||
return (value as ParserNode).currentValue; |
||||
}, |
||||
StatusOrItemDeltaOperation(operands: NonterminalNode, colon: NonterminalNode, deltaOperator: NonterminalNode, identifier: NonterminalNode, counter: NonterminalNode): number { |
||||
return (counter as ParserNode).numberValue |
||||
}, |
||||
StatusOrItemCounterSetOperation(operands: NonterminalNode, colon: NonterminalNode, identifier: NonterminalNode, counter: NonterminalNode): number { |
||||
return (counter as ParserNode).numberValue |
||||
}, |
||||
_iter(): never { |
||||
throw Error(`No idea what to say ${this.ctorName} iteration node's current value is`) |
||||
}, |
||||
_nonterminal(): never { |
||||
throw Error(`No idea what to say ${this.ctorName} nonterminal node's current value is`) |
||||
}, |
||||
_terminal(): never { |
||||
throw Error(`No idea what to say ${this.ctorName} terminal node's current value is`) |
||||
} |
||||
}) |
||||
|
||||
parser.addAttribute<number>("maxValue", { |
||||
SetMaxOperation(operands: NonterminalNode, space: NonterminalNode, resource: NonterminalNode, colon: NonterminalNode, maxvalue: NonterminalNode): number { |
||||
return (maxvalue as ParserNode).numberValue; |
||||
}, |
||||
SetMaxOperationAlternate(operands: NonterminalNode, colon: NonterminalNode, maxvalue: NonterminalNode, resource: NonterminalNode): number { |
||||
return (maxvalue as ParserNode).numberValue; |
||||
}, |
||||
SetMaxOperationAlternate2(operands: NonterminalNode, colon: NonterminalNode, resource: NonterminalNode, maxvalue: NonterminalNode): number { |
||||
return (maxvalue as ParserNode).numberValue; |
||||
}, |
||||
MeteredValue(current: NonterminalNode, max: NonterminalNode): number { |
||||
return (max as ParserNode).numberValue |
||||
}, |
||||
SetMeteredOperation(operands: NonterminalNode, space: NonterminalNode, resource: NonterminalNode, colon: NonterminalNode, value: NonterminalNode): number { |
||||
return (value as ParserNode).maxValue; |
||||
}, |
||||
SetMeteredOperationAlternate(operands: NonterminalNode, colon: NonterminalNode, value: NonterminalNode, resource: NonterminalNode): number { |
||||
return (value as ParserNode).maxValue; |
||||
}, |
||||
SetMeteredOperationAlternate2(operands: NonterminalNode, colon: NonterminalNode, resource: NonterminalNode, value: NonterminalNode): number { |
||||
return (value as ParserNode).maxValue; |
||||
}, |
||||
_iter(): never { |
||||
throw Error(`No idea what to say ${this.ctorName} iteration node's max value is`) |
||||
}, |
||||
_nonterminal(): never { |
||||
throw Error(`No idea what to say ${this.ctorName} nonterminal node's max value is`) |
||||
}, |
||||
_terminal(): never { |
||||
throw Error(`No idea what to say ${this.ctorName} terminal node's max value is`) |
||||
} |
||||
}) |
||||
|
||||
parser.addAttribute<FailReason>("failReason", { |
||||
avoid(): FailReason.Avoid { |
||||
return FailReason.Avoid |
||||
}, |
||||
dodge(): FailReason.Dodge { |
||||
return FailReason.Dodge |
||||
}, |
||||
miss(): FailReason.Miss { |
||||
return FailReason.Miss |
||||
}, |
||||
resist(): FailReason.Resist { |
||||
return FailReason.Resist |
||||
}, |
||||
fail(): FailReason.Fail { |
||||
return FailReason.Fail |
||||
}, |
||||
block(): FailReason.Block { |
||||
return FailReason.Block |
||||
}, |
||||
parry(): FailReason.Parry { |
||||
return FailReason.Parry |
||||
}, |
||||
FailReason(reason: NonterminalNode): FailReason { |
||||
return (reason as ParserNode).failReason |
||||
}, |
||||
FailOperation(operands: NonterminalNode, colon: NonterminalNode, reason: NonterminalNode): FailReason { |
||||
return (reason as ParserNode).failReason |
||||
}, |
||||
_iter(): never { |
||||
throw Error(`No idea what to say ${this.ctorName} iteration node's fail reason is`) |
||||
}, |
||||
_nonterminal(): never { |
||||
throw Error(`No idea what to say ${this.ctorName} nonterminal node's fail reason is`) |
||||
}, |
||||
_terminal(): never { |
||||
throw Error(`No idea what to say ${this.ctorName} terminal node's fail reason is`) |
||||
} |
||||
}) |
||||
|
||||
parser.addAttribute<CharacterSide>("side", { |
||||
characterSide(side: NonterminalNode): CharacterSide { |
||||
return (side as ParserNode).side |
||||
}, |
||||
enemySide(): CharacterSide.Enemy { |
||||
return CharacterSide.Enemy |
||||
}, |
||||
allySide(): CharacterSide.Ally { |
||||
return CharacterSide.Ally |
||||
}, |
||||
_iter(): never { |
||||
throw Error(`No idea what to say ${this.ctorName} iteration node's loyalties are`) |
||||
}, |
||||
_nonterminal(): never { |
||||
throw Error(`No idea what to say ${this.ctorName} nonterminal node's loyalties are`) |
||||
}, |
||||
_terminal(): never { |
||||
throw Error(`No idea what to say ${this.ctorName} terminal node's loyalties are`) |
||||
} |
||||
}) |
@ -0,0 +1,62 @@ |
||||
import { |
||||
createResourceManipulator, |
||||
MeteredResource, |
||||
Resource, |
||||
ResourceManipulator, |
||||
UnmeteredResource |
||||
} from "./Resources"; |
||||
|
||||
export enum ClockMode { |
||||
HEROES_FILL = "fill", |
||||
HEROES_EMPTY = "empty", |
||||
} |
||||
|
||||
export interface Clock { |
||||
readonly id: string |
||||
readonly text: string |
||||
readonly segments: number |
||||
readonly filled: number |
||||
readonly mode: ClockMode |
||||
readonly order: number |
||||
} |
||||
|
||||
const ClockResourceValue = { |
||||
[MeteredResource.Blood]: undefined, |
||||
[MeteredResource.Experience]: undefined, |
||||
[MeteredResource.Health]: undefined, |
||||
[MeteredResource.Items]: undefined, |
||||
[MeteredResource.Magic]: undefined, |
||||
[MeteredResource.Segments]: "filled", |
||||
[MeteredResource.Turns]: undefined, |
||||
[MeteredResource.Zero]: undefined, |
||||
[UnmeteredResource.Materials]: undefined, |
||||
[UnmeteredResource.Fabula]: undefined, |
||||
[UnmeteredResource.Ultima]: undefined, |
||||
[UnmeteredResource.Special]: undefined, |
||||
[UnmeteredResource.Zenit]: undefined, |
||||
[UnmeteredResource.Order]: "order", |
||||
[UnmeteredResource.Level]: undefined, |
||||
} as const satisfies {[key in Resource]: keyof Clock|undefined} |
||||
|
||||
const ClockResourceMax = { |
||||
[MeteredResource.Blood]: undefined, |
||||
[MeteredResource.Experience]: undefined, |
||||
[MeteredResource.Health]: undefined, |
||||
[MeteredResource.Items]: undefined, |
||||
[MeteredResource.Magic]: undefined, |
||||
[MeteredResource.Segments]: "segments", |
||||
[MeteredResource.Turns]: undefined, |
||||
[MeteredResource.Zero]: undefined, |
||||
[UnmeteredResource.Materials]: undefined, |
||||
[UnmeteredResource.Fabula]: undefined, |
||||
[UnmeteredResource.Ultima]: undefined, |
||||
[UnmeteredResource.Special]: undefined, |
||||
[UnmeteredResource.Zenit]: undefined, |
||||
[UnmeteredResource.Order]: undefined, |
||||
[UnmeteredResource.Level]: undefined, |
||||
} as const satisfies {[key in MeteredResource]: keyof Clock|undefined} & {[key in UnmeteredResource]: undefined} |
||||
|
||||
export const ClockResources: ResourceManipulator<Clock, Resource> = |
||||
createResourceManipulator< |
||||
Exclude<typeof ClockResourceMax[Resource]|typeof ClockResourceValue[Resource], undefined>, |
||||
Clock, Resource>(ClockResourceValue, ClockResourceMax) |
@ -0,0 +1,230 @@ |
||||
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<Resource>(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<Resource>(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<ObjectT, ResourceT> { |
||||
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<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}): ResourceManipulator<ObjectT, ResourceT> { |
||||
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) |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue