DragnLang Documentation - seastan/dragncards GitHub Wiki
DragnLang
The language used to automate game functions in DragnCards is a custom language called DragnLang. It is designed so that it can be written directly in your .json
game files. It comes built-in with most basic functions needed to manipulate the state of a card game, and provides a method for defining your own funtions for more complex tasks.
Functions
The syntax to call a function in DragnLang is a list, where the first element of the list is the name of a function and the remaining elements are the arguments to that function. If you are familiar with LISP, DragnLang's syntax may be somewhat familiar to you.
Examples
To perform 1 + 2
:
["ADD", 1, 2]
Returns 3
.
To perform 1 == 2
:
["EQUAL", 1, 2]
Returns false
.
To see if abcde
contains bcd
:
["IN_STRING", "abcde", "bcd"]
Returns true
.
Functions can be nested:
["NOT", ["IN_STRING", "abcde", "bcd"]]
Returns false
.
Actions
To manipulate the game state, you must perform an action. Actions are functions that return a game state. The return values for each built-in function are listed in the function documentation.
Here is an example of an action. It will return a game state where a message Hello World!
has been added to the log.
["LOG", "Hello World!"]
This action will return a game state where the card with id abc123
has been moved to the top card of the group with id player1
:
["MOVE_CARD", "abc123", "player1Deck", 0]
Action List
The core of DragnLang is the "action list": a list of actions (so a list of lists) that you want to perform on the game state.
Each action within the action list takes in the game state returned by the previous action, manipulates it, and outputs a new game state that the next action will operate on.
[
["LOG", "Shuffling player 1's deck."],
["SHUFFLE_GROUP", "player1Deck"]
]
In the above action list, the LOG
action is performed first, resulting in a game state with the new log statement added. This game state is fed into the SHUFFLE_GROUP
action, which shuffles player 1's deck.
Best practice: Most of the time, you should place a
LOG
action before calling one or more non-LOG
actions, rather than after. This is because if you have automation that is triggered by your non-LOG
statement, and that automation produces its own log statements, you will still maintain proper chronilogical ordering of your log.
Variables
Variables in DragnLang must begin with a $
and can be set using the VAR
action.
[
["VAR", "$MY_VAR", "Test"],
["LOG", "$MY_VAR"]
]
The above action list will add the message "Test" to the log. This is because all arguments to functions are evaluated first (with some required exceptions, like the first argument of VAR
), before the function itself is evaluated.
Variable scope
A variable is only accessible at its current level and lower/chile levels of the action list.
[
["VAR", "$X", 5],
[
["VAR", "$Y", 10],
["LOG", "$X"], // 5
["LOG", "$Y"] // 10
],
["LOG", "$X"], // 5
["LOG", "$Y"] // Error: Y is not defined
]
You can define a global variable that is accessible at any scope using DEFINE
.
[
["VAR", "$X", 5],
[
["DEFINE", "$Y", 10],
["LOG", "$X"], // 5
["LOG", "$Y"] // 10
],
["LOG", "$X"], // 5
["LOG", "$Y"] // 10
]
Backend Process
When a player triggers an action list by pressing a hotkey or a button, the action list is sent to the server backend to be evaluated. The final result of this action list is then broadcast to all players and their clients are updated accordingly. This is known as the "backend process". At the beginning of a backend process, any variable that may have been set during other backend processes are cleared. In addition, several built-in variables are set, such as:
Some examples:
$GAME
: Evaluates to the game state$PLAYER_N
: Evaluates to the player that triggered the hotkey/button, such asplayer1
.$ALIAS_N
: Evaluates to the alias of the player corresponding to$PLAYER_N
.$ACTIVE_CARD_ID
: Evaluates to the id of the card that was being hovered over by$PLAYER_N
when they triggered the backend process.$ACTIVE_CARD
: Evaluates to the card object corresponding to$ACTIVE_CARD_ID
.
The full list of these are found in the Built-in Variables documentation. Built-in variables are defined globally and are therefore available from within any action list called during the backend process.
For example, to decrease the `hitPoints`` of the player triggering the backend process by 1:
["DECREASE_VAL", "/playerData/$PLAYER_N/hitPoints", 1]
Or to set the rotation of the card being highlighted to 90:
["SET", "/cardById/$ACTIVE_CARD_ID/rotation", 90]
{{}}
String Pre-processing Using When a string argument in DragnLang is evaluated, it first undergoes a preprocessing step, where variables contained inside {{}}
are evaluated and the results are inserted in the place of the {{}}
.
Consider:
["LOG", "{{$ALIAS_N}} flipped {{$ACTIVE_CARD.sides.A.name}} facedown."]
Before the LOG
function is called the string argument will be pre-processed, and the variables inside {{}}
will be resolved and inserted into the string. So the log statement might read something like MyUser123 flipped Queen of Spades facedown.
This can be helpful in working with groups corresponding to the player that triggered the backend process ($PLAYER_N
). To discard the top card of that player's deck into that player's discard, you could do:
["MOVE_CARD", "$GAME.groupById.{{$PLAYER_N}}Deck.parentCardIds.[0]", "{{$PLAYER_N}}Discard", 0]
If $PLAYER_N
in this case is player2
, the pre-processing would simplify this to:
["MOVE_CARD", "$GAME.groupById.player2Deck.parentCardIds.[0]", "player2Discard", 0]
Example Action Lists:
Flip the active card
["COND",
["EQUAL", "$ACTIVE_CARD.currentSide", "A"],
[
["LOG", "{{$ALIAS_N}} flipped {{$ACTIVE_CARD.sides.A.name}} facedown."],
["SET", "/cardById/$ACTIVE_CARD_ID/currentSide", "B"]
],
["TRUE"],
[
["LOG", "{{$ALIAS_N}} flipped {{$ACTIVE_CARD.sides.A.name}} faceup."]
["SET", "/cardById/$ACTIVE_CARD_ID/currentSide", "A"]
]
]
Detach an attachment
["COND",
["GREATER_THAN", "$ACTIVE_CARD.cardIndex", 0],
[
["LOG", "{{$ALIAS_N}} detached {{$ACTIVE_FACE.name}}."],
["MOVE_CARD", "$ACTIVE_CARD_ID", "$ACTIVE_CARD.groupId", ["ADD", "$ACTIVE_CARD.stackIndex", 1]]
]
]
Toggle the active card's rotation between 0 and 90, if it's in play
["COND",
["AND", ["EQUAL", "$ACTIVE_CARD.rotation", 90], "$ACTIVE_CARD.inPlay"],
[
["LOG", "{{$ALIAS_N}} readied {{$ACTIVE_FACE.name}}."],
["SET", "/cardById/$ACTIVE_CARD_ID/rotation", 0],
["SET", "/cardById/$ACTIVE_CARD_ID/exhausted", false]
],
["AND", ["EQUAL", "$ACTIVE_CARD.rotation", 0], "$ACTIVE_CARD.inPlay"],
[
["LOG", "{{$ALIAS_N}} exhausted {{$ACTIVE_FACE.name}}."],
["SET", "/cardById/$ACTIVE_CARD_ID/rotation", 90],
["SET", "/cardById/$ACTIVE_CARD_ID/exhausted", true]
]
]
Discard the active card into its associated discard pile
["COND",
["EQUAL", "$ACTIVE_CARD.discardGroupId", null],
["LOG", "{{$ALIAS_N}} failed to discard {{$ACTIVE_FACE.name}} because it is not associated with a discard pile. Please drag the card instead."],
["TRUE"],
[
["LOG", "{{$ALIAS_N}} discarded {{$ACTIVE_CARD.sides.A.name}}."],
["MOVE_CARD", "$ACTIVE_CARD_ID", "$ACTIVE_CARD.discardGroupId", 0]
]
]
Shuffle the active card into its associated deck
[
["COND",
["EQUAL", "$ACTIVE_CARD.deckGroupId", null],
["ABORT", "{{$ALIAS_N}} failed to shuffle {{$ACTIVE_FACE.name}} into its deck because it is not associated with a deck."]
]
["MOVE_CARD", "$ACTIVE_CARD_ID", "$ACTIVE_CARD.deckGroupId", 0],
["VAR", "$GROUP_ID", "$ACTIVE_CARD.deckGroupId"],
["SHUFFLE_GROUP", "$GROUP_ID"],
["LOG", "{{$ALIAS_N}} shuffled {{$ACTIVE_FACE.name}} into {{$GAME.groupById.$GROUP_ID.label}}."]
]