A helper module for easily authoring rules and queries.
See https://github.com/jschomay/elm-narrative-engine/blob/master/src/Example.elm for a full example.
Example syntax:
PLAYER.current_location=(*.dark).fear<5
CAVE.dark.!explored
*.enemy.current_location=CAVEThese all will get parsed into NarrativeEngine.WorldModel.EntityMatchers.
The format is <entity ID or * or $><one or more queries as defined below>
* will become a MatchAny, otherwise it will be a Match with the supplied ID. $ is passed through directly (to be replaced with the trigger ID eventually).
By convention IDs are capitalized and query keywords are snake case, but they don't have to be.
Each query starts with a .. Query details follow.
ID.tag1.tag2becomes
Match "ID" [ HasTag "tag1", HasTag "tag2" ]ID.stat1=1.stat2>0.stat3<-2.stat_4>(stat ID1.other_stat).stat_5<(stat ID2.other_stat).stat_6=(stat ID3.other_stat)becomes
Match "ID"
[ HasStat "stat1" EQ (SpecificStat 1)
, HasStat "stat2" GT (SpecificStat 0)
, HasStat "stat3" LT (SpecificStat -2)
, HasStat "stat_4" GT (CompareStat "ID1" "other_stat")
, HasStat "stat_5" LT (CompareStat "ID2" "other_stat")
, HasStat "stat_6" EQ (CompareStat "ID3" "other_stat")
]ID.link1=ID1.link2=(ID2.tag1).link3=(*.tag2).link4=(link ID3.other_link)becomes
Match "ID"
[ HasLink "link1" (SpecificLink (Match "ID1" []))
, HasLink "link2" (SpecificLink (Match "ID2" [ HasTag "tag1" ]))
, HasLink "link3" (SpecificLink (MatchAny [ HasTag "tag2" ]))
, HasLink "link4" (CompareLink "ID3" "other_link")
]Any query segment can be prefixed with a ! for not
*.!tag1.!stat>9.!link=ID2becomes
MatchAny
[ Not (HasTag "tag1")
, Not (HasStat "stat" GT (SpecificStat 9))
, Not (HasLink "link" (SpecificLink (Match "ID2" [])))
]Parse an "entity matcher" syntax string.
Example syntax:
Change world:
PLAYER.current_location=$.fear+1
CAVE.explored
(*.enemy).blindedThese all will get parsed into NarrativeEngine.WorldModel.ChangeWorlds.
The format is <entity ID or * or $><one or more changes as defined below>
To UpdateAll, use a generic matcher in parens. Otherwise use an ID for a specific Update. $ is passed through directly (to be replaced with the trigger ID eventually).
Each change starts with a ..
ID.tag1.-tag2becomes
Update "ID" [ AddTag "tag1", RemoveTag "tag2" ]ID.stat1=1.stat2+1.stat3-2becomes
Update "ID"
[ SetStat "stat1" 1
, IncStat "stat2" 1
, DecStat "stat3" 2
]ID.link1=ID2.link2=(link ID2.link1)becomes
Update "ID"
[ SetLink "link1" (SpecificLinkTarget "ID2")
, SetLink "link2" (LookUpLinkTarget "ID2" "link1")
](*.tag1).tag2becomes
UpdateAll [ HasTag "tag1" ] [ AddTag "tag2" ]The result of parsing a "change world" syntax string.
Parse a "change world" syntax string.
ON: *.line
IF: PLAYER.chapter=1
BROADWAY_STREET.leaving_broadway_street_station_plot=1
DO: BRIEFCASE.location=THIEF
BROADWAY_STREET.leaving_broadway_street_station_plot=2You can include spaces and newlines as desired. The : after each rule part is optional. You can also leave out the "IF" and/or "DO" parts.
To create a SpecificTrigger rule instead of using an entity matcher as a trigger, use quotes arround the trigger string, like this:
ON: "next-day"Obviously, this only applies to the "ON:" line (which becomes the trigger).
A function for "merging" extra fields into a Rule {}.
The result of parsing many rules.
The result of parsing a "rule" syntax string.
Parses multiple "rule" syntax strings. The rules are tuples of the "rule" syntax for parsing, and the extra fields for that rule. You also need to provide an "extend function" to "merge" extra fields into the standard rule fields.
In general you should use parseRules at the top level of you application, and display any errors with NarrativeEngine.Syntax.Helpers.parseErrorsView.
Parses a single "rule" syntax string along with a record of additional fields. The extend function is used to "merge" the additional fields into the standard rule record. (You can use always identity if you don't have any extra fields).
The result of parsing an "entity matcher" syntax string.