WDL ~ What WDL will look like - uchicago-cs/chiventure GitHub Wiki

What WDL++ will look like

Design documentation for #441.

Rationale

The current WDL is based on YAML, originally chosen for its human-readability. However, the focus is now on WDL as an easily computer-readable format, not to be edited by human authors. Therefore, WDL++ is proposed to be based on JSON for its ease of parsing and thus flexibility for adding support for new engine features in the long run.

Converting existing WDL

Given the existing YAML format's features, it should be straightforward to convert a WDL into WDL++. There are online tools for this (https://yaml-online-parser.appspot.com/) but essentially YAML dictionaries turn into JSON "objects", and YAML bulleted lists turn into arrays.

An example, converted straight from (a part of) tests/wdl/examples/connected_rooms.wdl

{
  "ROOMS": [
    {
      "connections": [
        {
          "to": "room B", 
          "direction": "NORTH"
        }
      ], 
      "long_desc": "This is room A long", 
      "short_desc": "This is room A", 
      "id": "room A"
    }, 
    {
      "connections": [
        {
          "to": "room C", 
          "direction": "NORTH"
        }
      ], 
      "long_desc": "This is room B long", 
      "short_desc": "This is room B", 
      "id": "room B"
    }
  ]
}

Of course, actions and some possibly other YAML entries should not be converted straight from old WDL because we need to implement some new features/structures in the WDL++ version, as described below.

New features

These are new WDL++ features being requested in open issues or anticipated based on other teams' work, along with some ideas of how they could be represented in WDL++.

  • Custom actions with conditions based on inventory items, visited locations, etc, and support for custom scripting. A possible way of incorporating this into the spec is to:

    • Define a set of atomic actions that take parameters. These atomic actions are then sequenced together as a list to form larger actions.

    • Define "conditional guard" actions that are followed by a list of actions to execute if the condition is true, and another list of actions to execute if the condition is false.

    • An atomic action that references a Lua script to execute in place of a regular list of actions.

    • An example of how this could look:

      The description of the object in the list of objects:

    {
      "id": "obj_CHAIR",
      "short_desc": "This is a chair",
      "long_desc": "This is a chair long",
      "in": "room_A",
     	"states": ["upright", "flipped", "broken"],
        "actions": [
            {"call": "PUSH", "acts": ["act_PUSH", "act_AFTERPUSH"]},
            {"call": "PULL", "acts": ["act_PULL"]}
        ]
    }

...and the description of associated actions In the list of custom actions:

Declaring custom actions and their action sequences

A candidate representation of custom actions is something similar to the example JSON from the Feature Wishlist wiki page (https://github.com/uchicago-cs/chiventure/wiki/Feature-Wishlist#custom-actions): space-separated "function application":

    {
      "action": "act_PUSH",
      "sequence": [
      	{"block": "if", "params": "inventory has item_SPINACH"},
    	{"block": "set", "params": "obj_CHAIR flipped"},
    	{"block": "say", "params": "'You pushed the chair. It flipped!'"},
    	{"block": "else", "params": ""},
    	{"block": "say", "params": "'Couldn't push the chair. Spinach needed.''"},
    	{"block": "endif", "params": ""},
    	{"block": "say", "params": "'This is printed regardless of push success.'"},
        {"block": "lua", "params": "'path/to/example.lua' arg0 arg1 arg2 arg3"}
      ]
    }

​This will also eventually support passing arguments into action sequences (something like "action": "act_PUSH <target>") where the variable name "target" is referenced in block parameters within the sequence.

Using these custom actions, it should also be straightforward to implement battle systems, unless there is a dedicated "scene" for battles that the game and player must transition to.

  • Global conditionals

Apart from the local conditionals that are part of an action sequence, there will also "global conditionals" that are checked constantly (practically speaking, on every new action the player or some entity makes). This will allow supporting global game end conditions such as ending the game with a win (or loss) when the player collects all required items, regardless of what order the player obtained them in. Apart from ending the game, this may also support other events and even custom actions in the future.

The spec for this is not yet decided; as of now it's dependent on what the Action Management and Custom Actions teams eventually support. However, it will probably be a separate dict entry in the JSON (alongside ROOMS, OBJECTS, etc).

  • NPC dialog trees:

Each dialog line could have a unique ID and be represented by objects of the form:

    {
      "id": 1,
      "line": "It's party time!",
      "from": "npc_PartyParrot",
      "next": [1001, 1002, 1003],
      "acts": ["act_PARROT"]
    }

The next array contains IDs of dialog lines that the current one connects to. If the array only has 1 line ID, the dialog line is understood to always lead into that line. If there are multiple IDs, the dialog line is understood to be a multiple-choice branching line, where the player selects a line out of the choices to use as their response.

The way action declarations are separated out also allows us to specify actions to run when each dialog line is invoked.

As for the NPCs themselves, they will probably be defined and put into rooms the same way static objects are. The only difference is that they will define a "starting dialog line" for when the player comes up to TALK:

    {
      "id": "npc_PartyParrot",
      "short_desc": "The resident Party Parrot",
      "long_desc": "Your feisty friend the Party Parrot",
      "in": "room_A",
      "actions": 
      [
        {"call": "PUSH", "acts": ["act_PUSH_PARROT", "act_AFTERPUSH"]},
      ],
      "talk": 1
    }

...where 1 is the ID of the first dialog line spoken by this NPC.

Evaluation

When the need for human-readability is taken away, JSON can be used as an easy-to-parse way to represent structured data. It seems that this will work fine for most of our needs with representing a game instance.

As for documentation, through trying out test games and making them in the old WDL, we've found that the documentation for the old WDL was lacking and had some conflicting directions; namely, confusion over what syntax to use for string literals in YAML, as well as undocumented features being scattered across existing .wdl files (which may or may not have only been wishlisted features that were put in the wdl early for good measure). We'll aim to improve on this as we document and design our new WDL++.

Implementation details

TBD.

Integration with other teams

Of course, this is just a tentative spec. Any feedback and suggestions from other teams based on their needs are welcome, and this will probably be flexible as we figure out implementation details later on.

⚠️ **GitHub.com Fallback** ⚠️