more grammar and model improvements

main
Mari 1 year ago
parent b01bea67f4
commit 64cda38606
  1. 172
      src/grammar/grammar.ohm
  2. 536
      src/grammar/parser.ts
  3. 18
      src/model/Character.ts
  4. 29
      src/model/GameState.ts
  5. 66
      src/model/Messages.ts

@ -20,43 +20,135 @@ FabulaDSL {
comma = "," comma = ","
Operands (operands) = nonemptyListOf<operand, comma> Operands (operands) = nonemptyListOf<operand, comma>
experienceResource = caseInsensitive<"XP">|caseInsensitive<"EXP"> segments = caseInsensitive<"segments">|caseInsensitive<"sections">
meteredResource (the name of a metered resource) = caseInsensitive<"HP">|caseInsensitive<"MP">|caseInsensitive<"IP">|caseInsensitive<"ZP">|caseInsensitive<"BP">|caseInsensitive<"Turns">|experienceResource |caseInsensitive<"segment">|caseInsensitive<"section">
spResource = caseInsensitive<"FP">|caseInsensitive<"UP">|caseInsensitive<"SP"> |caseInsensitive<"ticks">|caseInsensitive<"tick">
moneyResource = caseInsensitive<"Zenit">|caseInsensitive<"z"> |caseInsensitive<"clock">
materialsResource = caseInsensitive<"materials">|caseInsensitive<"mats">|caseInsensitive<"m">
levelResource = caseInsensitive<"Level">|caseInsensitive<"lv"> points = caseInsensitive<"points">|caseInsensitive<"pts">|caseInsensitive<"power">
unmeteredResource (the name of an unmetered resource) = spResource|moneyResource|materialsResource|levelResource|caseInsensitive<"order">
resource (the name of a resource) = meteredResource|unmeteredResource experience = caseInsensitive<"experience">
xp = caseInsensitive<"XP">|caseInsensitive<"EXP">
deltaOperator = "+"|"-" ExperienceFull = experience points
XpResource = ExperienceFull|experience|xp
health = caseInsensitive<"health">|caseInsensitive<"life">|caseInsensitive<"hit">
HealthFull = health points
hp = caseInsensitive<"HP">
HpResource = HealthFull|health|hp
mind = caseInsensitive<"mind">|caseInsensitive<"magic">|caseInsensitive<"mana">
MindFull = mind points
mp = caseInsensitive<"MP">
MpResource = MindFull|mind|mp
item = caseInsensitive<"items">|caseInsensitive<"item">|caseInsensitive<"inventory">
ItemFull = item points
ip = caseInsensitive<"IP">
IpResource = ItemFull|item|ip
zero = caseInsensitive<"zero">
charge = points|caseInsensitive<"charge">|segments
ZeroFull = zero charge
zp = caseInsensitive<"ZP">|caseInsensitive<"ZC">
ZpResource = ZeroFull|zero|zp
blood = caseInsensitive<"blood">|caseInsensitive<"grave">
BloodFull = blood points
bp = caseInsensitive<"BP">
BpResource = BloodFull|blood|bp
turns = caseInsensitive<"turns">|caseInsensitive<"turn">|caseInsensitive<"actions">|caseInsensitive<"action">
TurnsFull = turns points
tp = caseInsensitive<"TP">|caseInsensitive<"AP">
TpResource = TurnsFull|turns|tp
MeteredResource (the name of a metered resource) = segments|XpResource|HpResource|MpResource
|IpResource|ZpResource|BpResource|TpResource
fabula = caseInsensitive<"Fabula">
ultima = caseInsensitive<"Ultima">
special = caseInsensitive<"Special">
fp = caseInsensitive<"FP">
up = caseInsensitive<"UP">
sp = caseInsensitive<"SP">
FabulaFull = fabula points
UltimaFull = ultima points
SpecialFull = special points
FabulaResource = FabulaFull|fabula|fp
UltimaResource = UltimaFull|ultima|up
SpecialResource = SpecialFull|special|sp
SpResource = FabulaResource|UltimaResource|SpecialResource
LevelResource = caseInsensitive<"Level">|caseInsensitive<"lvl">|caseInsensitive<"lv">
money = caseInsensitive<"Zenit">|caseInsensitive<"z">|caseInsensitive<"gold">|caseInsensitive<"gil">|caseInsensitive<"gp">|caseInsensitive<"g">
MoneyResource = money
materials = caseInsensitive<"materials">|caseInsensitive<"mats">|caseInsensitive<"m">
MaterialsFull = money space materials
MaterialsResource = MaterialsFull|materials
UnmeteredResource (the name of an unmetered resource) = SpResource|LevelResource|MaterialsResource|MoneyResource
Resource (the name of a resource) = MeteredResource|UnmeteredResource
plus = "+"
minus = "-"
deltaOperator = plus|minus
integer (an integer) = digit+ integer (an integer) = digit+
Delta (a delta) = deltaOperator integer Delta (a delta) = deltaOperator integer
element = caseInsensitive<"fire">|caseInsensitive<"water">|caseInsensitive<"poison">|caseInsensitive<"lightning">|caseInsensitive<"earth">|caseInsensitive<"ice">|caseInsensitive<"wind">|caseInsensitive<"light">|caseInsensitive<"dark">|caseInsensitive<"physical"> fireElement = caseInsensitive<"fire">|caseInsensitive<"flame">|caseInsensitive<"burn">
affinity = "??"|"..."|"."|"!!" waterElement = caseInsensitive<"water">|caseInsensitive<"poison">|caseInsensitive<"acid">
lightningElement = caseInsensitive<"lightning">|caseInsensitive<"zap">|caseInsensitive<"electricity">
|caseInsensitive<"electrical">|caseInsensitive<"electric">|caseInsensitive<"elec">
|caseInsensitive<"shock">
earthElement = caseInsensitive<"earth">|caseInsensitive<"rock">|caseInsensitive<"dirt">
iceElement = caseInsensitive<"ice">|caseInsensitive<"cold">
windElement = caseInsensitive<"wind">|caseInsensitive<"air">|caseInsensitive<"gust">
lightElement = caseInsensitive<"light">|caseInsensitive<"holy">
darkElement = caseInsensitive<"dark">|caseInsensitive<"evil">
physicalElement = caseInsensitive<"physical">|caseInsensitive<"phys">
nonElement = caseInsensitive<"nonelemental">|caseInsensitive<"non-elemental">|caseInsensitive<"none">|caseInsensitive<"non">
healingElement = caseInsensitive<"healing">|caseInsensitive<"heal">|caseInsensitive<"cure">
elementalType (an element) = fireElement|waterElement
|lightningElement|earthElement
|iceElement|windElement
|lightElement|darkElement
|physicalElement|nonElement|healingElement
absorbAffinity = "??"|caseInsensitive<"drained">|caseInsensitive<"drain">|caseInsensitive<"absorbed">|caseInsensitive<"absorb">
immuneAffinity = "."|caseInsensitive<"immunity">|caseInsensitive<"blocked">|caseInsensitive<"immune">|caseInsensitive<"block">
resistAffinity = "..."|caseInsensitive<"resistance">|caseInsensitive<"resisted">|caseInsensitive<"resist">
vulnerableAffinity = "!!"|caseInsensitive<"vulnerable">|caseInsensitive<"weakness">|caseInsensitive<"weak">
normalAffinity = caseInsensitive<"normal">
affinity (an affinity indicator) = vulnerableAffinity|resistAffinity|immuneAffinity|absorbAffinity|normalAffinity
meteredSeparator = ~lineCommentStart ~blockCommentStart "/" meteredSeparator = ~lineCommentStart ~blockCommentStart "/"
MeteredValue = integer MaxValue MeteredValue = integer MaxValue
MaxValue = meteredSeparator integer MaxValue = meteredSeparator integer
DeltaOperation = Operands space resource colon Delta affinity? element? DeltaOperation = Operands space Resource colon Delta affinity? elementalType?
DeltaOperationAlternate = Operands colon Delta affinity? resource? element? DeltaOperationAlternate = Operands colon Delta affinity? Resource? elementalType?
DeltaOperationAlternate2 = Operands colon resource Delta affinity? element? DeltaOperationAlternate2 = Operands colon Resource Delta affinity? elementalType?
SetMeteredOperation = Operands space meteredResource colon MeteredValue SetMeteredOperation = Operands space MeteredResource colon MeteredValue
SetMeteredOperationAlternate = Operands colon MeteredValue meteredResource SetMeteredOperationAlternate = Operands colon MeteredValue MeteredResource
SetMeteredOperationAlternate2 = Operands colon meteredResource MeteredValue SetMeteredOperationAlternate2 = Operands colon MeteredResource MeteredValue
SetValueOperation = Operands space resource colon integer SetValueOperation = Operands space Resource colon integer
SetValueOperationAlternate = Operands colon integer resource SetValueOperationAlternate = Operands colon integer Resource
SetValueOperationAlternate2 = Operands colon resource integer SetValueOperationAlternate2 = Operands colon Resource integer
SetMaxOperation = Operands space meteredResource colon MaxValue SetMaxOperation = Operands space MeteredResource colon MaxValue
SetMaxOperationAlternate = Operands colon MaxValue meteredResource SetMaxOperationAlternate = Operands colon MaxValue MeteredResource
SetMaxOperationAlternate2 = Operands colon meteredResource MaxValue SetMaxOperationAlternate2 = Operands colon MeteredResource MaxValue
ClearOperation = Operands space resource colon null ClearOperation = Operands space Resource colon null
ClearOperationAlternate = Operands colon null resource ClearOperationAlternate = Operands colon null Resource
ClearOperationAlternate2 = Operands colon resource null ClearOperationAlternate2 = Operands colon Resource null
StatusDeltaOperation = Operands colon deltaOperator identifier StatusOrItemCounterUnwrapped = "x"? integer
StatusOrItemCounterWrapped = "(" StatusOrItemCounterUnwrapped ")"
StatusOrItemCounter = StatusOrItemCounterWrapped | StatusOrItemCounterUnwrapped
StatusOrItemDeltaOperation = Operands colon deltaOperator identifier StatusOrItemCounter?
StatusOrItemCounterDeltaOperation = Operands colon identifier Delta
StatusOrItemCounterSetOperation = Operands colon identifier StatusOrItemCounter
notNewlineOrComment = ~newline ~lineCommentStart ~blockCommentStart any notNewlineOrComment = ~newline ~lineCommentStart ~blockCommentStart any
textToEndOfLine = notNewlineOrComment+ textToEndOfLine = notNewlineOrComment+
@ -64,15 +156,23 @@ FabulaDSL {
SetTargetOperation = target (Operands | null) SetTargetOperation = target (Operands | null)
SetSourceOperation = source (Operands | null) SetSourceOperation = source (Operands | null)
dodge = caseInsensitive<"dodged">|caseInsensitive<"dodge">
miss = caseInsensitive<"missed">|caseInsensitive<"miss">
resist = caseInsensitive<"resisted">|caseInsensitive<"resist">
fail = caseInsensitive<"failed">|caseInsensitive<"fail">
block = caseInsensitive<"blocked">|caseInsensitive<"block">
parry = caseInsensitive<"parried">|caseInsensitive<"parry">
FailReason = dodge|miss|resist|fail|block|parry
FailOperation = Operands colon FailReason
PrintOperation = ">" textToEndOfLine PrintOperation = ">" textToEndOfLine
Operation = DeltaOperation | DeltaOperationAlternate | DeltaOperationAlternate2 Operation = DeltaOperation | DeltaOperationAlternate | DeltaOperationAlternate2
| SetMeteredOperation | SetMeteredOperationAlternate | SetMeteredOperationAlternate2 | SetMeteredOperation | SetMeteredOperationAlternate | SetMeteredOperationAlternate2
| SetValueOperation | SetValueOperationAlternate | SetValueOperationAlternate2 | SetValueOperation | SetValueOperationAlternate | SetValueOperationAlternate2
| SetMaxOperation | SetMaxOperationAlternate | SetMaxOperationAlternate2 | SetMaxOperation | SetMaxOperationAlternate | SetMaxOperationAlternate2
| ClearOperation | ClearOperationAlternate | ClearOperationAlternate2 | ClearOperation | ClearOperationAlternate | ClearOperationAlternate2
| StatusDeltaOperation | StatusOrItemDeltaOperation | StatusOrItemCounterDeltaOperation | StatusOrItemCounterSetOperation
| SetTargetOperation | SetSourceOperation | PrintOperation | SetTargetOperation | SetSourceOperation | FailOperation | PrintOperation
silentOperator = "~" silentOperator = "~"
@ -92,7 +192,7 @@ FabulaDSL {
beginKeyword = caseInsensitive<"begin"> beginKeyword = caseInsensitive<"begin">
endKeyword = caseInsensitive<"end"> endKeyword = caseInsensitive<"end">
BlockStart = silentOperator? beginKeyword SetSourceOperation? SetTargetOperation? textToEndOfLine BlockStart = silentOperator? beginKeyword SetSourceOperation? SetTargetOperation? colon textToEndOfLine
BlockEnd = endKeyword textToEndOfLine? BlockEnd = endKeyword textToEndOfLine?
Block = BlockStart operationTerminator CodeSegment BlockEnd operationTerminatorOrEnd Block = BlockStart operationTerminator CodeSegment BlockEnd operationTerminatorOrEnd

@ -0,0 +1,536 @@
import grammar from "./grammar.ohm-bundle";
import {
Affinity,
ElementalType,
MarkdownContext,
MarkdownOutput,
MeteredResource,
NumberSign,
Operands,
ParseContext,
Resource,
UnmeteredResource,
} from "../model/Messages";
import {IterationNode, Node, NonterminalNode, TerminalNode} from "ohm-js";
import {CharacterPrivacy, CharacterSide} from "../model/Character";
import {ClockMode} from "../model/GameState";
export const parser = grammar.createSemantics()
export interface ParserNode extends Node {
element: ElementalType|null
affinity: Affinity
sign: NumberSign
numberValue: number
identifier: string
resource: Resource|null
// TODO: Implement all of the things listed below.
operands: Operands
blockTargets: Operands
blockSources: Operands
currentValue: number|null
maxValue: number|null
textValue: string
silenced: boolean
failReason: unknown // TODO: create an enum for this
// TODO: create rules for these to describe clocks/timers/items/statuses/characters and implement them
nameText: string
descriptionText: string
side: CharacterSide
privacy: CharacterPrivacy
clockMode: ClockMode
timerDirection: unknown // TODO: create an enum for this
timerDurationMs: number
// 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: 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: 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: 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`)
}
},
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
},
})

@ -173,26 +173,19 @@ export function spTypeToDescription(sp: SPType): string {
return SPTypes[sp].description return SPTypes[sp].description
} }
export type StatusId = string export interface StatusEffectInstance {
readonly id: string
export interface StatusEffect {
readonly id: StatusId
readonly name: string
readonly count?: number readonly count?: number
readonly iconUrl?: string
readonly description?: string
} }
export type CharacterId = string
export enum CharacterSide { export enum CharacterSide {
Ally = "ally", Ally = "ally",
Enemy = "enemy" Enemy = "enemy"
} }
export interface Character { export interface Character {
readonly id: CharacterId readonly id: string
readonly side: CharacterSide readonly side?: CharacterSide
readonly portraitUrl?: string readonly portraitUrl?: string
readonly name?: string readonly name?: string
readonly altName?: string readonly altName?: string
@ -217,7 +210,8 @@ export interface Character {
readonly turnsLeft?: number readonly turnsLeft?: number
readonly turnsTotal?: number readonly turnsTotal?: number
readonly canAct?: boolean readonly canAct?: boolean
readonly statuses?: readonly StatusEffect[] readonly statuses?: readonly StatusEffectInstance[]
readonly order?: number
readonly privacy?: CharacterPrivacy readonly privacy?: CharacterPrivacy
} }

@ -1,4 +1,4 @@
import {Character, CharacterId, CharacterSide, SPType} from "./Character"; import {Character, CharacterSide, SPType} from "./Character";
export enum ClockMode { export enum ClockMode {
HEROES_FILL = "fill", HEROES_FILL = "fill",
@ -21,7 +21,6 @@ export interface ConflictState {
readonly round: number readonly round: number
readonly activeSide: CharacterSide readonly activeSide: CharacterSide
readonly activeCharacterId: string|null readonly activeCharacterId: string|null
readonly timers: readonly TimerState[]
} }
export interface BaseTimerState { export interface BaseTimerState {
@ -43,20 +42,34 @@ export interface CountdownTimerState extends BaseTimerState {
export type TimerState = CountupTimerState|CountdownTimerState; export type TimerState = CountupTimerState|CountdownTimerState;
export interface StatusEffect {
readonly id: string
readonly name: string
readonly description: string
readonly iconUrl: string
}
export interface GameState { export interface GameState {
readonly session: SessionState readonly session: SessionState
readonly conflict?: ConflictState readonly conflict?: ConflictState
readonly clocks: readonly Clock[] readonly clocks: readonly Clock[]
readonly characters: readonly Character[] readonly characters: readonly Character[]
readonly statuses: readonly StatusEffect[]
readonly timers: readonly TimerState[]
} }
export interface PastState { export function getClockById(state: GameState, id: string): Clock|undefined {
// The unix timestamp in ms when changing _away_ from this state. return state.clocks.find((clock) => clock.id === id)
readonly timestamp: number
readonly logMarkdown: string
readonly state: GameState
} }
export function getCharacterById(state: GameState, id: CharacterId): Character|undefined { export function getCharacterById(state: GameState, id: string): Character|undefined {
return state.characters.find((character) => character.id === id) return state.characters.find((character) => character.id === id)
} }
export function getStatusById(state: GameState, id: string): StatusEffect|undefined {
return state.statuses.find((status) => status.id === id)
}
export function getTimerById(state: GameState, id: string): TimerState|undefined {
return state.timers.find((timer) => timer.id === id)
}

@ -0,0 +1,66 @@
import {GameState} from "./GameState";
export enum ElementalType {
Fire = "fire",
Water = "water",
Lightning = "lightning",
Ice = "ice",
Earth = "earth",
Wind = "wind",
Light = "light",
Dark = "dark",
Physical = "physical",
Nonelemental = "non-elemental",
Healing = "healing",
}
export enum Affinity {
Absorbs = "absorbs",
Immune = "immune",
Resistant = "resistant",
Normal = "normal",
Vulnerable = "vulnerable",
}
export enum NumberSign {
Positive = "+",
Negative = "-",
}
export enum MeteredResource {
Experience = "EXP",
Health = "HP",
Magic = "MP",
Items = "IP",
Zero = "ZP",
Blood = "BP",
Turns = "TP",
Segments = "Segments",
}
export enum UnmeteredResource {
Fabula = "FP",
Ultima = "UP",
Special = "SP",
Level = "Level",
Materials = "Materials",
Zenit = "Zenit",
}
export type Resource = MeteredResource|UnmeteredResource
export const Target = Symbol("target");
export const Source = Symbol("source");
export type Operands = Set<string|typeof Target|typeof Source>
export interface ParseContext {
readonly source: readonly string[]
readonly target: readonly string[]
readonly game: GameState
}
export interface MarkdownContext extends ParseContext {}
export interface MarkdownOutput extends MarkdownContext {
readonly output: string
}
Loading…
Cancel
Save