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=CAVE
These all will get parsed into NarrativeEngine.WorldModel.EntityMatcher
s.
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.tag2
becomes
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=ID2
becomes
MatchAny
[ Not (HasTag "tag1")
, Not (HasStat "stat" GT (SpecificStat 9))
, Not (HasLink "link" (SpecificLink (Match "ID2" [])))
]
Result String NarrativeEngine.Core.WorldModel.EntityMatcher
The result of parsing an "entity matcher" syntax string.
parseMatcher : String -> ParsedMatcher
Parse an "entity matcher" syntax string.
Example syntax:
Change world:
PLAYER.current_location=$.fear+1
CAVE.explored
(*.enemy).blinded
These all will get parsed into NarrativeEngine.WorldModel.ChangeWorld
s.
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.-tag2
becomes
Update "ID" [ AddTag "tag1", RemoveTag "tag2" ]
ID.stat1=1.stat2+1.stat3-2
becomes
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).tag2
becomes
UpdateAll [ HasTag "tag1" ] [ AddTag "tag2" ]
Result String NarrativeEngine.Core.WorldModel.ChangeWorld
The result of parsing a "change world" syntax string.
parseChanges : String -> ParsedChanges
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=2
You 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 -> NarrativeEngine.Core.Rules.Rule {} -> NarrativeEngine.Core.Rules.Rule a
A function for "merging" extra fields into a Rule {}
.
Result NarrativeEngine.Syntax.Helpers.ParseErrors (NarrativeEngine.Core.Rules.Rules a)
The result of parsing many rules.
Result String (NarrativeEngine.Core.Rules.Rule a)
The result of parsing a "rule" syntax string.
parseRules : ExtendFn a -> Dict NarrativeEngine.Core.Rules.RuleID ( String, a ) -> ParsedRules a
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
.
parseRule : ExtendFn a -> ( String, a ) -> ParsedRule a
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).