advance through statuses some more

main
Mari 1 year ago
parent bd4e49fd50
commit 84f70f86d8
  1. 26
      src/grammar/grammar.ohm
  2. 93
      src/grammar/interpreter.ts
  3. 12
      src/model/Character.ts
  4. 6
      src/ui/App.tsx

@ -185,18 +185,29 @@ FabulaDSL {
ClearOperationAlternate = Operands colon null Resource ClearOperationAlternate = Operands colon null Resource
ClearOperationAlternate2 = Operands colon Resource null ClearOperationAlternate2 = Operands colon Resource null
// TODO: continue implementation list from here // StatusOrItemCounter/Unwrapped/Wrapped/DeltaWrapped/Delta: numberValue
StatusOrItemCounterUnwrapped = "x"? integer StatusOrItemCounterUnwrapped = "x"? integer
StatusOrItemCounterWrapped = "(" StatusOrItemCounterUnwrapped ")" StatusOrItemCounterWrapped = "(" StatusOrItemCounterUnwrapped ")"
StatusOrItemCounter = StatusOrItemCounterWrapped | StatusOrItemCounterUnwrapped StatusOrItemCounter = StatusOrItemCounterWrapped | StatusOrItemCounterUnwrapped
StatusOrItemDeltaWrapped = "(" Delta ")" StatusOrItemDeltaWrapped = "(" Delta ")"
StatusOrItemDelta = StatusOrItemDeltaWrapped | Delta StatusOrItemDelta = StatusOrItemDeltaWrapped | Delta
// StatusOrItemAddOperation: operands, identifier, numberValue, evaluate, renderMarkdown
StatusOrItemAddOperation = Operands colon plus identifier StatusOrItemCounter? StatusOrItemAddOperation = Operands colon plus identifier StatusOrItemCounter?
// TODO: StatusOrItemRemoveOperation: operands, identifier, evaluate, renderMarkdown
StatusOrItemRemoveOperation = Operands colon minus identifier StatusOrItemRemoveOperation = Operands colon minus identifier
StatusOrItemCounterDeltaOperation = Operands colon identifier Delta
StatusOrItemCounterDeltaOperationAlternate = Operands colon Delta identifier // TODO: StatusOrItemDeltaOperation/Alternate: operands, identifier, numberValue, evaluate, renderMarkdown
StatusOrItemCounterDeltaOperation = Operands colon identifier StatusOrItemDelta
StatusOrItemCounterDeltaOperationAlternate = Operands colon StatusOrItemDelta identifier
// TODO: Fix conflict with resource mutators (use brackets?)
// TODO: StatusOrItemSetOperation/Alternate: operands, identifier, numberValue, evaluate, renderMarkdown
StatusOrItemCounterSetOperation = Operands colon identifier StatusOrItemCounter? StatusOrItemCounterSetOperation = Operands colon identifier StatusOrItemCounter?
StatusOrItemCounterSetOperationAlternate = Operands colon StatusOrItemCounter identifier
// TODO: continue implementation list from here
notNewlineOrComment = ~newline ~lineCommentStart ~blockCommentStart any notNewlineOrComment = ~newline ~lineCommentStart ~blockCommentStart any
textToEndOfLine = notNewlineOrComment+ textToEndOfLine = notNewlineOrComment+
@ -300,13 +311,14 @@ FabulaDSL {
PrintOperation = printOperator textToEndOfLine PrintOperation = printOperator textToEndOfLine
PrintOperationWithOperands = Operands colon printOperator textToEndOfLine PrintOperationWithOperands = Operands colon printOperator textToEndOfLine
Operation = DeltaOperation | DeltaOperationAlternate | DeltaOperationAlternate2 Operation = StatusOrItemAddOperation | StatusOrItemRemoveOperation
| StatusOrItemCounterDeltaOperation | StatusOrItemCounterDeltaOperationAlternate
| StatusOrItemCounterSetOperation | StatusOrItemCounterSetOperationAlternate
| 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
| StatusOrItemAddOperation | StatusOrItemRemoveOperation | StatusOrItemCounterSetOperation
| StatusOrItemCounterDeltaOperation | StatusOrItemCounterDeltaOperationAlternate
| SetTargetOperation | SetSourceOperation | FailOperation | PrintOperationWithOperands | PrintOperation | SetTargetOperation | SetSourceOperation | FailOperation | PrintOperationWithOperands | PrintOperation
silentOperator = "~" silentOperator = "~"

@ -24,7 +24,7 @@ import {
CharacterStatuses, CharacterStatuses,
isZpReady isZpReady
} from "../model/Character"; } from "../model/Character";
import {GameState, TimerDirection} from "../model/GameState"; import {GameState, lookupIdentifier, TimerDirection} from "../model/GameState";
import { import {
formatMeteredResource, formatMeteredResource,
formatResourceDelta, formatResourceMax, formatResourceValue, formatResourceDelta, formatResourceMax, formatResourceValue,
@ -266,6 +266,16 @@ interpreter.addAttribute<number>("numberValue", {
StatusOrItemCounterWrapped(lParen: TerminalNode, counter: NonterminalNode, _rParen: TerminalNode): number { StatusOrItemCounterWrapped(lParen: TerminalNode, counter: NonterminalNode, _rParen: TerminalNode): number {
return (counter as InterpreterNode).numberValue return (counter as InterpreterNode).numberValue
}, },
StatusOrItemCounter(counter: NonterminalNode): number {
return (counter as InterpreterNode).numberValue
},
StatusOrItemDelta(delta: NonterminalNode): number {
return (delta as InterpreterNode).numberValue;
},
StatusOrItemDeltaWrapped(_lParen: TerminalNode, delta: NonterminalNode, _rParen: TerminalNode): number {
return (delta as InterpreterNode).numberValue;
},
StatusOrItemAddOperation(operands: NonterminalNode, colon: NonterminalNode, deltaOperator: NonterminalNode, identifier: NonterminalNode, counter: IterationNode): number { StatusOrItemAddOperation(operands: NonterminalNode, colon: NonterminalNode, deltaOperator: NonterminalNode, identifier: NonterminalNode, counter: IterationNode): number {
if (counter.numChildren === 0) { if (counter.numChildren === 0) {
return 0 return 0
@ -311,6 +321,9 @@ interpreter.addAttribute<string>("identifier", {
StatusOrItemAddOperation(operands: NonterminalNode, colon: NonterminalNode, deltaOperator: NonterminalNode, identifier: NonterminalNode, _counter: NonterminalNode): string { StatusOrItemAddOperation(operands: NonterminalNode, colon: NonterminalNode, deltaOperator: NonterminalNode, identifier: NonterminalNode, _counter: NonterminalNode): string {
return (identifier as InterpreterNode).identifier return (identifier as InterpreterNode).identifier
}, },
StatusOrItemRemoveOperation(_arg0: NonterminalNode, _arg1: NonterminalNode, _arg2: NonterminalNode, identifier: NonterminalNode): string {
return (identifier as InterpreterNode).identifier;
},
StatusOrItemCounterDeltaOperation(operands: NonterminalNode, colon: NonterminalNode, identifier: NonterminalNode, _delta: NonterminalNode): string { StatusOrItemCounterDeltaOperation(operands: NonterminalNode, colon: NonterminalNode, identifier: NonterminalNode, _delta: NonterminalNode): string {
return (identifier as InterpreterNode).identifier return (identifier as InterpreterNode).identifier
}, },
@ -598,6 +611,9 @@ interpreter.addAttribute<Operands>("operands", {
StatusOrItemAddOperation(operands: NonterminalNode, _colon: NonterminalNode, _delta: NonterminalNode, _statusOrItem: NonterminalNode, _counter: IterationNode): Operands { StatusOrItemAddOperation(operands: NonterminalNode, _colon: NonterminalNode, _delta: NonterminalNode, _statusOrItem: NonterminalNode, _counter: IterationNode): Operands {
return (operands as InterpreterNode).operands return (operands as InterpreterNode).operands
}, },
StatusOrItemRemoveOperation(operands: NonterminalNode, _arg1: NonterminalNode, _arg2: NonterminalNode, _arg3: NonterminalNode): Operands {
return (operands as InterpreterNode).operands
},
Operands(arg0: NonterminalNode): Operands { Operands(arg0: NonterminalNode): Operands {
return (arg0.asIteration() as InterpreterNode).operands return (arg0.asIteration() as InterpreterNode).operands
}, },
@ -624,7 +640,7 @@ interpreter.addAttribute<Operands>("operands", {
}, },
_terminal(): never { _terminal(): never {
throw Error(`No idea what to say ${this.ctorName} terminal node's operands value is`) throw Error(`No idea what to say ${this.ctorName} terminal node's operands value is`)
}, }
}) })
interpreter.addAttribute<Operands>("targets", { interpreter.addAttribute<Operands>("targets", {
@ -1064,6 +1080,20 @@ interpreter.addOperation<EvaluationContext>("evaluate(ctx)", {
} }
} }
}, },
StatusOrItemRemoveOperation(_arg0: NonterminalNode, _arg1: NonterminalNode, _arg2: NonterminalNode, _arg3: NonterminalNode): EvaluationContext {
const ctx = (this as EvaluationNode).args.ctx
const operands = evaluateOperands((this as InterpreterNode).operands, ctx)
const statusOrItemId = (this as InterpreterNode).identifier
return {
...ctx,
game: {
...ctx.game,
characters: ctx.game.characters.map((c) =>
operands.has(c.id) ? CharacterStatuses.removeStatus(c, statusOrItemId) : c),
}
}
},
ClearOperation(_arg0: NonterminalNode, _arg1: NonterminalNode, _arg2: NonterminalNode, _arg3: NonterminalNode, _arg4: NonterminalNode): EvaluationContext { ClearOperation(_arg0: NonterminalNode, _arg1: NonterminalNode, _arg2: NonterminalNode, _arg3: NonterminalNode, _arg4: NonterminalNode): EvaluationContext {
return EvaluateClear(this as EvaluationNode); return EvaluateClear(this as EvaluationNode);
}, },
@ -1110,7 +1140,7 @@ function RenderDelta(node: MarkdownNode): MarkdownOutput {
const affinity = node.affinity const affinity = node.affinity
const element = node.element const element = node.element
const operandsBefore = lookupOperands(node.operands, ctx) const operandsBefore = lookupOperands(node.operands, ctx)
const ctxAfter = EvaluateDelta(node) const ctxAfter = node.evaluate(ctx)
const operandsAfter = lookupOperands(node.operands, ctxAfter) const operandsAfter = lookupOperands(node.operands, ctxAfter)
for (const [id, after] of operandsAfter) { for (const [id, after] of operandsAfter) {
@ -1184,7 +1214,7 @@ function RenderMeteredSet(node: MarkdownNode): MarkdownOutput {
const newValue = node.currentValue const newValue = node.currentValue
const newMax = node.maxValue const newMax = node.maxValue
const operandsBefore = lookupOperands(node.operands, ctx) const operandsBefore = lookupOperands(node.operands, ctx)
const ctxAfter = EvaluateDelta(node) const ctxAfter = node.evaluate(ctx)
const operandsAfter = lookupOperands(node.operands, ctxAfter) const operandsAfter = lookupOperands(node.operands, ctxAfter)
for (const [id, after] of operandsAfter) { for (const [id, after] of operandsAfter) {
@ -1261,7 +1291,7 @@ function RenderValueSet(node: MarkdownNode): MarkdownOutput {
const resource = node.resource const resource = node.resource
const newValue = node.currentValue const newValue = node.currentValue
const operandsBefore = lookupOperands(node.operands, ctx) const operandsBefore = lookupOperands(node.operands, ctx)
const ctxAfter = EvaluateDelta(node) const ctxAfter = node.evaluate(ctx)
const operandsAfter = lookupOperands(node.operands, ctxAfter) const operandsAfter = lookupOperands(node.operands, ctxAfter)
for (const [id, after] of operandsAfter) { for (const [id, after] of operandsAfter) {
@ -1317,7 +1347,7 @@ function RenderValueSet(node: MarkdownNode): MarkdownOutput {
result.push(`${clockText}: [${formatResourceValue(resource ?? MeteredResource.Segments, newValue)}`) result.push(`${clockText}: [${formatResourceValue(resource ?? MeteredResource.Segments, newValue)}`)
break break
default: default:
throw Error(`Don't know how to render metered set on ${before.type} ${id}`) throw Error(`Don't know how to render value set on ${before.type} ${id}`)
} }
} }
return { return {
@ -1332,7 +1362,7 @@ function RenderMaxSet(node: MarkdownNode): MarkdownOutput {
const resource = node.resource const resource = node.resource
const newMax = node.maxValue const newMax = node.maxValue
const operandsBefore = lookupOperands(node.operands, ctx) const operandsBefore = lookupOperands(node.operands, ctx)
const ctxAfter = EvaluateDelta(node) const ctxAfter = node.evaluate(ctx)
const operandsAfter = lookupOperands(node.operands, ctxAfter) const operandsAfter = lookupOperands(node.operands, ctxAfter)
for (const [id, after] of operandsAfter) { for (const [id, after] of operandsAfter) {
@ -1346,7 +1376,7 @@ function RenderMaxSet(node: MarkdownNode): MarkdownOutput {
throw Error(`character ${id} somehow changed type between calls?`) throw Error(`character ${id} somehow changed type between calls?`)
} }
if (resource && !isMeteredResource(resource)) { if (resource && !isMeteredResource(resource)) {
throw Error(`somehow got non-metered resource in metered set`) throw Error(`somehow got non-metered resource in max set`)
} }
const beforeChar = before.character const beforeChar = before.character
const afterChar = after.character const afterChar = after.character
@ -1389,12 +1419,12 @@ function RenderMaxSet(node: MarkdownNode): MarkdownOutput {
const afterClock = after.clock const afterClock = after.clock
const clockText = afterClock.text const clockText = afterClock.text
if (resource && !isMeteredResource(resource)) { if (resource && !isMeteredResource(resource)) {
throw Error(`somehow got non-metered resource in metered set`) throw Error(`somehow got non-metered resource in max set`)
} }
result.push(`${clockText}: [${formatResourceMax(resource ?? MeteredResource.Segments, newMax)}`) result.push(`${clockText}: [${formatResourceMax(resource ?? MeteredResource.Segments, newMax)}`)
break break
default: default:
throw Error(`Don't know how to render metered set on ${before.type} ${id}`) throw Error(`Don't know how to render max set on ${before.type} ${id}`)
} }
} }
return { return {
@ -1409,7 +1439,7 @@ function RenderClear(node: MarkdownNode): MarkdownOutput {
const resource = node.resource const resource = node.resource
const newMax = node.maxValue const newMax = node.maxValue
const operandsBefore = lookupOperands(node.operands, ctx) const operandsBefore = lookupOperands(node.operands, ctx)
const ctxAfter = EvaluateDelta(node) const ctxAfter = node.evaluate(ctx)
const operandsAfter = lookupOperands(node.operands, ctxAfter) const operandsAfter = lookupOperands(node.operands, ctxAfter)
for (const [id, after] of operandsAfter) { for (const [id, after] of operandsAfter) {
@ -1437,6 +1467,44 @@ function RenderClear(node: MarkdownNode): MarkdownOutput {
} }
result.push(`${clockText}: [${formatResourceMax(resource ?? MeteredResource.Segments, newMax)}`) result.push(`${clockText}: [${formatResourceMax(resource ?? MeteredResource.Segments, newMax)}`)
break break
default:
throw Error(`Don't know how to render resource clear on ${before.type} ${id}`)
}
}
return {
...ctxAfter,
output: result.length > 0 ? result.join("\n") : null
}
}
function RenderStatusAdd(node: MarkdownNode): MarkdownOutput {
const ctx = node.args.ctx
const result: string[] = []
const identifier = node.identifier
const counter = node.numberValue
const operandsBefore = lookupOperands(node.operands, ctx)
const ctxAfter = node.evaluate(ctx)
const operandsAfter = lookupOperands(node.operands, ctxAfter)
const status = lookupIdentifier(ctxAfter.game, identifier)
const statusName = status.type === "status" ? status.status.name : identifier
for (const [id, after] of operandsAfter) {
const before = operandsBefore.get(id)
if (!before) {
throw Error("Somehow an object appeared?")
}
switch (before.type) {
case "character":
if (after.type !== "character") {
throw Error(`character ${id} somehow changed type between calls?`)
}
const beforeChar = before.character
const afterChar = after.character
const charName = (afterChar.privacy ? applyCharacterPrivacy(afterChar) : afterChar)?.name
if (CharacterStatuses.hasStatus(beforeChar, identifier) !== CharacterStatuses.hasStatus(afterChar, identifier)) {
result.push(`${charName}: [+${statusName}${counter === 0 ? "" : `(${counter})`}]`)
}
break
default: default:
throw Error(`Don't know how to render metered set on ${before.type} ${id}`) throw Error(`Don't know how to render metered set on ${before.type} ${id}`)
} }
@ -1501,6 +1569,9 @@ interpreter.addOperation<MarkdownOutput>("renderMarkdown(ctx)", {
ClearOperationAlternate2(_arg0: NonterminalNode, _arg1: NonterminalNode, _arg2: NonterminalNode, _arg3: NonterminalNode): MarkdownOutput { ClearOperationAlternate2(_arg0: NonterminalNode, _arg1: NonterminalNode, _arg2: NonterminalNode, _arg3: NonterminalNode): MarkdownOutput {
return RenderClear(this as MarkdownNode); return RenderClear(this as MarkdownNode);
}, },
StatusOrItemAddOperation(_arg0: NonterminalNode, _arg1: NonterminalNode, _arg2: NonterminalNode, _arg3: NonterminalNode, _arg4: IterationNode): MarkdownOutput {
return RenderStatusAdd(this as MarkdownNode);
},
_iter(...children: readonly Node[]): MarkdownOutput { _iter(...children: readonly Node[]): MarkdownOutput {
let ctx = (this as MarkdownNode).args.ctx let ctx = (this as MarkdownNode).args.ctx

@ -537,15 +537,19 @@ export function applyCharacterPrivacy(character: Character): Character|null {
} }
export const CharacterStatuses = { export const CharacterStatuses = {
hasStatus(c: Character, id: string): number|null {
const status = c.statuses?.find((s) => s.id === id)
return status ? status.count : null
},
addStatus(c: Character, id: string, stacks: number): Character { addStatus(c: Character, id: string, stacks: number): Character {
if (!c.statuses || c.statuses.some((s) => s.id === id)) { if (!c.statuses || this.hasStatus(c, id) !== null) {
return c return c
} else { } else {
return this.setStatus(c, id, stacks) return this.setStatus(c, id, stacks)
} }
}, },
setStatus(c: Character, id: string, stacks: number): Character { setStatus(c: Character, id: string, stacks: number): Character {
if (!c.statuses) { if (!c.statuses || this.hasStatus(c, id) === stacks) {
return c return c
} }
if (c.statuses.some((s) => s.id === id)) { if (c.statuses.some((s) => s.id === id)) {
@ -579,12 +583,12 @@ export const CharacterStatuses = {
} }
}, },
removeStatus(c: Character, id: string): Character { removeStatus(c: Character, id: string): Character {
if (!c.statuses || !c.statuses.some((s) => s.id === id)) { if (this.hasStatus(c, id) === null) {
return c return c
} else { } else {
return { return {
...c, ...c,
statuses: c.statuses.filter((s) => s.id !== id) statuses: c.statuses ? c.statuses.filter((s) => s.id !== id) : []
} }
} }
} }

@ -50,6 +50,12 @@ function App() {
athetyz: - MP athetyz: - MP
linnet: HP - linnet: HP -
&: Lv - &: Lv -
gale, echo, aelica: +Digesting
gale: -Digesting
flow: +Digesting 2
echo, flow: +Digesting 3
calor, flow, echo: Digesting +1
gale, echo: +1 Digesting
End`) End`)
} }
} catch (ex) { } catch (ex) {

Loading…
Cancel
Save