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