File format

Alex
Here's my idea for the file format for QuestKit.

It aims to have as little markup as possible, so it can be read and edited by a human.

Here's a sample based on some of the Quest 5 Tutorial Game:


title: Test Game

command:
pattern: say #text#
script:
function(text) {
msg ("You say '" + text + "', but nobody replies.");
}

location: lounge
description: This is quite a plain lounge with an old beige carpet and peeling wallpaper.
south: kitchen

object: TV
switchable: true
alt:
["television", "telly"]
look: The TV is an old model, possibly 20 years old. {if TV.switchedon:It is currently showing an old western.}{else:It is currently switched off.}
watch: You watch for a few minutes. As your will to live slowly ebbs away, you remember that you’ve always hated watching westerns.

character: Bob
type: male
look: {if Bob.alive:Bob is sitting up, appearing to feel somewhat under the weather.}{else:Bob is lying on the floor, a lot more still than usual.}
use[defibrillator]:
function() {
if (get("Bob.alive")) {
msg ("Bob is alive so you don't need to use the defibrilltor again.")
}
else {
msg ("Miraculously, the defibrillator lived up to its promise, and Bob is now alive again. He says his head feels kind of fuzzy.");
set ("Bob.alive", true);
}
}

object: table
surface: true

object: newspaper
parent: table
take: true
takemsg: You fold the newspaper and place it neatly under your arm.

object: defibrillator
look: A heart defibrillator can magically revive a dead person, if all those hospital dramas are to be believed.
take: true

location: kitchen
description: Just an ordinary kitchen.

object: eggs
weight: 250
prefix: some
look:
function() {
msg ("A box of eggs, weighing " + get("eggs.weight") + " grams.");
}

command:
pattern: weigh #object#
script:
function(object) {
msg ("It weighs " + get(object, "weight") + " grams.");
}

object: fridge
container: true
closed: true

object: milk
parent: fridge
prefix: some

object: cheese
parent: fridge
prefix: some

object: beer
parent: fridge
prefix: some


The general format is "attribute: value". If the value is tab-indented on the following line, if it begins with "function" then it's a function, otherwise it's interpreted as JSON. (This is how we define "TV.alt" as an array).

QuestKit objects are not exposed as objects in JavaScript, which is why you need to use "set" and "get" to access attributes. Scripts themselves are not attributes - you won't be able to set an object's script at run-time, but you can still get it using the same dotted string syntax (e.g. "eggs.look"). There are no script dictionaries, but you can refer to a script called "Bob.use[defibrillator]".

Some notes...

- If an attribute can be evaluated as a number, then it is a number. Otherwise it's a string. (To force a number to be a string, use JSON - so tab-indent it and surround with quotes)
- When a location is defined, the objects that follow will be in that location, unless they have a parent set separately. (Note that locations are still "just" objects, the same way they are in Quest 5)
- Setting an directional attribute name on a location (e.g. "south: some room") automatically defines a two-way exit

Some open questions:

- I defined Bob using "character:" - he's still just an object. Not sure if setting something as a character would automatically set some attributes. Or maybe just make it appear differently in the editor. Could there be a use for this?
- It would be nice to have a way to write long-ish passages of text including linebreaks without having to worry about escaping quotes. Maybe tab-indented again, but then we need a way to distinguish between plain text (Markdown?) and JSON. Maybe adding an asterisk before the attribute name or something, but that seems like a kludge.

What do you think?

Please do rip this apart - let me know if anything seems stupid, let me know what doesn't make sense, let me know what you would change...

george
Cool, some thoughts on this:

* If a function always has the function keyword, are tab indents necessary for other values?

* How do extensions/namespaces work, both for QuestKit extensions and JS libraries?

* is it necessary to have both a 'description' and a 'look' attribute?

* do the object designations ('location', 'character') imply a class hierarchy/inheritance of any kind?

* how will QK represent threaded conversations in the file format (i.e. conversation node objects nested in other node objects)? By giving a child node a parent of another node? Can a node then have multiple parents? What does that say about the object model? Does this need to be special-cased?

* could we triple quote long passages of text?

The Pixie
Looks a lot like YAML, which I like. A lot less typing that XML.

Alex
Ah, YAML - thanks The Pixie! It will make things much easier if we reuse an existing format. I'll come up with an example and post that in a bit.

To answer George's questions:

1. re: tab indents - the question might not be relevant if we use YAML
2. extensions/namespaces - I don't think we need to specify anything JS-wise. In general, let's not clutter up the global namespace. Extensions and JS libraries can do what they like, but it's probably advisable if they only add one global object (or perhaps even better, if they extend QuestKit's?). Hard to know yet until we flesh out how QuestKit's own core library works.
3. "Description" is for rooms, "look" is for objects when you look at them - the same as it is in Quest. I'm open to all ideas though if there might be a better way.
4. No, there are no class hierarchies or inheritance at all - I want to get away from that. "Location" or "character" are just a convenience for the editor. They also make implications - by defining a new location, all objects that occur after it have that location set as their parent.
5. For conversation nodes, I think it will work just like Squiffy. Nodes have names, and link to other nodes by name. They won't have parents, so it's easy for multiple nodes to link to the same one.
6. We certainly can do something like this with YAML...

Alex
Here we go, this version uses YAML!


title: Test Game

---
command:
pattern: "say #text#"
action:
script: |
function(text) {
msg ("You say '" + text + "', but nobody replies.");
}

---
location: lounge
description: This is quite a plain lounge with an old beige carpet and peeling wallpaper.
south: kitchen

---
object: TV
switchable: true
alt:
- television
- telly
look: The TV is an old model, possibly 20 years old. {if TV.switchedon:It is currently showing an old western.}{else:It is currently switched off.}
watch: You watch for a few minutes. As your will to live slowly ebbs away, you remember that you’ve always hated watching westerns.

---
character: Bob
type: male
look: "{if Bob.alive:Bob is sitting up, appearing to feel somewhat under the weather.}{else:Bob is lying on the floor, a lot more still than usual.}"
use[defibrillator]:
script: |
function() {
if (get("Bob.alive")) {
msg ("Bob is alive so you don't need to use the defibrilltor again.")
}
else {
msg ("Miraculously, the defibrillator lived up to its promise, and Bob is now alive again. He says his head feels kind of fuzzy.");
set ("Bob.alive", true);
}
}

---
object: table
surface: true

---
object: newspaper
parent: table
take: true
takemsg: You fold the newspaper and place it neatly under your arm.

---
object: defibrillator
look: A heart defibrillator can magically revive a dead person, if all those hospital dramas are to be believed.
take: true

---
location: kitchen
description: Just an ordinary kitchen.

---
object: eggs
weight: 250
prefix: some
look:
script: |
function() {
msg ("A box of eggs, weighing " + get("eggs.weight") + " grams.");
}

---
command:
pattern: "weigh #object#"
action:
script: |
function(object) {
msg ("It weighs " + get(object, "weight") + " grams.");
}

---
object: fridge
container: true
closed: true

---
object: milk
parent: fridge
prefix: some

---
object: cheese
parent: fridge
prefix: some

---
object: beer
parent: fridge
prefix: some


Notes:

- Each object here is a separate YAML document. I think this is neater than the alternative which would be having one document with a list of objects - then every object would need to be indented.
- I tripped up over YAML not allowing tabs for indentation - you must uses spaces instead, so watch out for that.
- Bob.look needs quotes around it, otherwise having it start with the brace character triggers parsing the value as JSON.
- command patterns need quotes around them, otherwise the # and everything that follows it is treated as a comment.

I've been thinking about what Jay said viewtopic.php?f=15&t=4835#p32135 about having a unified approach to combining text and scripts. So in this version of the format, a command has an "action". This can contain a script, but could also contain a "text" attribute. Similarly, an object's "look" can have the same structure.

If we don't do this, we won't actually be able to distinguish between scripts and strings, as they're all just strings according to the YAML parser. If we adopt a convention where scripts are always called "script", it's easy to work it out.

jaynabonne
I hate when I waffle on so much, going different directions, that the person I'm talking to ends up agreeing to something I've moved on from mentally. :)

I've been thinking a bit more, and I'd like to propose a different approach. I would appreciate your patience. First, the different text/script attributes worked well for my response code because "if"s and things were effected by having multiple responses. We don't really want that here. And having separate text and script attributes has the same separation of things I want to avoid. So let's see how this new idea might work... Keep in mind I haven't worked out all the details. :)

I like having it possible to have simple text for descriptions. It's the leap from text to conditionals and things that seems to cause grief. Someone says, "I have this room description, but now I want to have it sometimes say something else, based on something else I did elsewhere in the game." And it becomes such that even with this proposed format that you need to erase the text from the "text" field and create an "msg" command with that text in the "script" field as a first step. It's rewriting what you have, and it's all so different.

What if we adopt an approach where text is primary and scripting secondary (but still important)? I'm thinking of an approach like what you do with (now, don't laugh or groan) ASP pages, where the scripting is added as markup. But the markup would be Javsascript or macro statements or both.

The initial description could be like this:

location: a city street
description: You are on a deserted city street in the dark of night.


The game author now wants two descriptions, one for night time and one for day time, determined by an attribute (maybe "city.time"). Then we could have markup like this:

location: a city street
description: {$if (GetValue("city.time") == "night") $}
You are on a deserted city street in the dark of night.
{$ else $}
You are on a busy city street in the dark of night.
{$ endif $}


There are two things I like about this:
1) You're doing the same thing you did before but extending it. You have the same description field and the same text, but now you've simply added in conditionals and more text.
2) Any string not inside the funny braces is output as is. This allows you not have to put text inside quotes for an msg, worry about how to quote quote characters, etc.

I know this is basically the text processor, or a souped up version of the same. But I think people can deal with markup much more readily than script, and it makes handing some code to someone else trivial. They just copy and paste it where it needs to go, as they would text.

A call to Javascript could simply be:

{$ someFunction(); $}

Its text value would be nothing, so it would contribute nothing to the output. Or maybe it does have a value that is output. :)

And to get to the other thing I had mentioned in my email, we could enable user defined macros as well - with the same text markup usage.

macro: authorname
text: Jay Nabonne

macro: gametitle
text: My Special Game

location: about
description: This special game named "{$gametitle$}" is brought to you by {$ authorname $}


Now that could be done with attributes. Where it gains power is when you use the same conditionals you use for other text.

macro: sunset
description: {$ switch (GetValue("sunset.state")) $}
{$ case "approaching" $}
The sky outside is bathed in lovely colors as sunset approaches.
{$ end $}
{$ case "set" $}
The sun has set.
{$ end $}
{$ end $}

object: window
description: The window is grimy and in need of repair. {$ sunset $}


One disadvantage this has is that you need to bracket everything. If you wanted the various strings to only be printed the first time, you could put a "{$ firsrtime $} marker around the text - but you'd need to close it. Perhaps there's a way around that. But it leaves the text itself more or less alone and allows people to have curly braces, semicolons, and whatever else might in a current text processor markup, as long as it's not {$ or $}. :)

I'm not sure if this seems extreme or makes sense. I like the consistent treatment of text and the incremental addition of scripting without having to switch paradigms. Also, it doesn't have to be things like "end" and the like. I originally had it with actual Javascript (which might be cool to support) and it looked like:

{$ switch (GetValue("sunset.state")) {
case "approaching": { $}
The sky outside is bathed in lovely colors as sunset approaches.
{$ }
case "set": { $}
The sun has set.
{$ }
} $}

which seemed better in some ways and absolutely awful in others (unless we change the markers to not use curly braces as well).

Some more thoughts. Not sure what you think, but I think the text processor and use of markup in descriptions is one of the best recent additions to Quest, since it eliminates a need for scripting of many small things. Extending it to cover more general purpose scripting needs could make it fantastic and get rid of the need for a separate scripting mode (no offense to Blockly - or perhaps it can all just generate JS code and be used by Blockly anyway. We're talking about simple blocks here anyway)/

Alex
OK, so this is really just a (much) more sophisticated text processor - which is certainly an idea I can get behind.

We still need scripting though, to handle things such as freezing the player when they look at Medusa or whatever...

Good point about being able to call JS functions directly from the text processor though - that should be easy to implement.

So I don't think this changes the approach in the short term - and I think for the initial release of QuestKit we'll have a text processor that is similar to what Quest has now. (Well, it all depends on what kind of pull requests we get I suppose!)

jaynabonne
What I had in mind is not so much Javascript functions as much as Javascript *code*. That the text plus markup turns into script when compiled, always. The text turns into msg's. The rest passes through as JS (or generates JS). Random example follows. (I'm making this up as I go along, so don't think I mean this to be final form. I'm just trying to get across the idea.)

location: TrickyRoom
onenter: You enter a dimly lot room with strange markings on the wall.
{firsttime}
{$
var markings = getRandomMarkings();
SetValue("TrickyRoom.marking1", markings[0]);
SetValue("TrickyRoom.marking2", markings[1]);
SetValue("TrickyRoom.marking3", markings[2]);
SetValue("TrickyRoom.marking4", markings[3]);
$}
{end}
The first is a {TrickyRoom.marking1}. Right beside it is one that looks like {TrickyRoom.marking2}. There are two more, {TrickyRoom.marking3} and {TrickyRoom.marking4}. There is an elevator button on the wall.


command:
pattern: push elevator button
description:
{$ if (!GetBoolean("button.pressed")) $}
{$ SetFlag("button.pressed") $}
You press the button, and it lights up.
{$ else $}
The button has already been pressed.
{$ end $}

The idea is that with something like this, we wouldn't need dedicated scripting, unless people want to write function. It's all just markup. I know it's a world apart from how it's done now with text-plus-text-processor and separate script blocks that do things, but I think it's interesting to think about anyway.

jaynabonne
The generated code for the latter would be:

if (!GetBoolean("button.pressed")) {
SetFlag("button.pressed");
msg("You press the button, and it lights up.");
} else {
msg("The button has already been pressed.");
}

I'm talking about merging text and scripting into a single thing.

Alex
Ah right, I understand now. Interesting! I'll have a think...

Alex
One thing that occurs to me, if all "look" descriptions were scripts, you wouldn't be able to change it at run-time. I wonder if that would be limiting?

Alex
Thinking about it more... I think it's fine not to be able to change the description definition at run-time. It means all the potential descriptions an object might show are defined by its original object definition, which is nice and straightforward.

Richard
Hi Alex and Jay, I am new to this forum but I have been watching Quest for quite some time. I am excited that you are starting on a new version and would like to add my idea. Instead of using the funny braces I would suggest using the ASP.NET Razor Markup. See the following link: http://www.w3schools.com/aspnet/razor_intro.asp.

Here is an example:

command:
pattern: push elevator button
description:
@if (!GetBoolean("button.pressed"))
@SetFlag("button.pressed")
You press the button, and it lights up.
@else
The button has already been pressed.
@end


I think this will make the code much easier to read and modify.
Let me know what you think.

jaynabonne
Alex wrote:Thinking about it more... I think it's fine not to be able to change the description definition at run-time. It means all the potential descriptions an object might show are defined by its original object definition, which is nice and straightforward.


You could always have the marked up text reference attributes as well. And then change the attributes dynamically. :)

jaynabonne
Richard, it does look cleaner, especially in those examples. If the goal was to have Javascript blocks as well, I wonder how that could be extended where you're not putting an "@" on the front of each line.

Pertex
Alex wrote:One thing that occurs to me, if all "look" descriptions were scripts, you wouldn't be able to change it at run-time. I wonder if that would be limiting?


Ähhmm,yes. Perhaps this feature is not used but the average Quest-User but I often change descriptions at runtime. I don't want to have a script testing lots of boolean values, I just change the description.

Thinking about Jays suggestions: I am a bit confused. So you will have two scripting languages in Quest: Javascript and "Jayscript"? ( :D ) I don't think this would be a good idea. The normal Quest user has problems with asl, now he should know two languages?

Pertex
One other thing I want to mention: Please choose clear seperarators and identifier. I feel bad if i have to think about "Here you need a space, here you have to use a tab, this must be quoted, move this into the next line." If you have to write games with text editors, you will get lots of errors without editorsupport.

For example:

location: forest
description: There are several exits leading away from this
location: west ans east


Alex
Jay makes an excellent point - if you want to be able to change the entire look description, just make the look description print some other attribute. Then you can change that. Simple!

I like Razor syntax. That got me to thinking, why not just use an off-the-shelf JS template engine? That way there's even less code to write!

I think we should try using Underscore's template engine: http://documentcloud.github.io/underscore/#template

jaynabonne
Pertex wrote:

"Alex"

One thing that occurs to me, if all "look" descriptions were scripts, you wouldn't be able to change it at run-time. I wonder if that would be limiting?



Ähhmm,yes. Perhaps this feature is not used but the average Quest-User but I often change descriptions at runtime. I don't want to have a script testing lots of boolean values, I just change the description.



Since Javascript is flexible about types, it could be that if the description attribute is an script, it runs it. Otherwise, if it's a string, it prints it, etc. I'm more concerned with keeping the top-level source consistent. The code that it compiles to (and the underlying code to execute) could do things behind the scenes to make things "just work".

Pertex wrote:

Thinking about Jays suggestions: I am a bit confused. So you will have two scripting languages in Quest: Javascript and "Jayscript"? ( :D ) I don't think this would be a good idea. The normal Quest user has problems with asl, now he should know two languages?


I think ideally, it would all just be Javascript. But I could see there being some "syntactic sugar" for common cases. The vast majority of the code would be Javascript. It's just things like delimiting blocks that might benefit from being made easier.

I also have to admit that I've been less than rigorous so far about what things look like in the interest of getting the ideas out. I think making it consistent should be a goal.

george
I like how Underscore is flexible about its template delimiters. When you're mixing code and prose I think you want syntax to be as out-of-the-way as can be.

One thing I don't think should get lost in the weeds here is to what extent this file format is meant to be user-facing, or just the base format that the later QK IDE will use. For the former it should be as clean as possible -- if you look at examples like Twine with its twisted macro system you see a lot of complaints from end users when they're trying to do anything complicated. But for the latter, as long as the format is clean yaml or whatever, then it doesn't have to be as user friendly.

Silver
I assume that it will be easier to implement audio/media files into games if it's JS rather than HTML dealing with it? Also would the files be client side as that would obviously make more sense too.

jaynabonne
I was looking at this page about underscore templates: http://www.bennadel.com/blog/2411-using ... rtials.htm

and it's almost exactly one variant I had in mind (short of syntactical differences). :) Javascript code intertwined in the text.

This topic is now closed. Topics are closed after 60 days of inactivity.

Support

Forums