Rez v1.7.0 — open source tool for creating non-linear games/IF

It’s also perhaps worth mentioning that in 1.8 I have removed the table attribute type.

When you declared a table attribute it meant using an object as the value and that meant you could nest value arbitrarily deep by using lists or objects as values in the table.

Now there’s nothing especially wrong with that except… updating nested attributes was a pain. You had to read the attribute value, reach inside it and update it, then put the whole object attribute value back again. In Clojure this is idiomatic, in Javascript it’s painful.

That’s how I ended up with the composition-over-nesting strategy and why _id attributes are treated in a special way to make it easier to work with composed elements.

So instead of:

@actor player {
  attributes: {
    strength: 10
    dexterity: 14
    constitution: 11
    intelligence: 18
    wisdom: 7
    charisma: 16
  }

  inc_strength: function() {
    const attrs = this.attributes;
    attrs.strength += 1;
    this.attributes = attrs;
  }
}

A situation that got worse the more nested your structure was (for example strength might itself be a nested table with current_value, maximum_value, modifier, and so on). Using element composition you would express the same thing as:

@actor player {
  attributes_id: #player_attributes

  inc_strength: function() {
    this.attributes.strength += 1;
  }
}

@object player_attributes {
  strength: 10
  dexterity: 14
  constitution: 11
  intelligence: 18
  wisdom: 7
  charisma: 16
}

This is because Rez automatically manages a reference property for any attribute with an _id suffix. You can see that we use .attributes even though we don’t define it directly.

So if you have an attribute attributes_id and set its value to #player_attributes then it will automatically manage a paired property attributes that refers to the game element with the same id. In practice this works great and has the added bonus of making serialisation so much easier: no object references anywhere!

Removing tables has not been without consequences as I was using them in several places. However I’ve managed to convert all of those into binding lists.

I introduced binding lists to be able to specify ordered key-value bindings for binding properties in the rendering process. A bindings list is a list of key-value pairs but with restricted value types (i.e. no nesting).

For example the v1.7 syntax to specify JS events to be handled at game startup the syntax was:

@game {
  start_events: {"DOMContentLoaded": "event_name", ...}
}

In 1.8 it would be:

@game {
  start_events: [DOMContentLoaded: "event_name", ...]
}

Which is such a small difference that it feels like a reasonable compromise. In fact binding lists are a reasonably good substitute for anything except nested structures but, as I say, I’ve settled on composing game elements instead.

In practice what this means is that I’ve got the {} syntax available again and perhaps I can think of something useful to do with it.

Oh I have also removed the ability to put collections in sets. In practice I cannot see any situation in which I would want to put, e.g. a list, into a set. If such a situation arises it wouldn’t be hard to put back. But it simplifies the parser a little not to have to deal with nested collection values here either.

1 Like

I’m almost at the point where v1.8 is ready to go. I’m feeling like I trust the code again and back to working on the game. I made so many substantial changes that it dented my confidence, but I’m feeling good about it again.

As per my previous post @item is no longer a basic element but has become an alias of the @card element. In practice items now require a content: template attribute and can be used directly in a scene. That’s working well and has given me some more ideas.

I suspect if I had known how much effort it would be going in I wouldn’t have started. But the result is the codebase is in a much better place and the convoluted and unpleasant NodeValidator has been replaced by the much more elegant @schema system.

I have further extended @schema to support rules that pattern match on attribute names. For example the @inventory schema now includes the following rule:

@schema inventory {
  ?/^initial_/: {kind: :list, coll_kind: :elem_ref}
}

to ensures that @inventory attributes with an inital_ prefix must be a list of element references.

This was motivated by changes to inventory initial contents arising from the removal of table attribute value. So now, instead of writing:

@slot topics_slot {
  accepts: :topic
}

@slot backpack_slot {
  accepts: :item
}

@inventory player_inventory {
  slots: #{#topics_slot #backpack_slot}

  initial_contents: {
    #topics_slot: [#topic_1 #topic_2 ...]
    #backpack_slot: [#item_1 #item_2 ...]
  }
}

you would write:

@slot topics_slot {
  accepts: :topic
  accessor: :topics
}

@slot backpack_slot {
  accepts: :item
  accessor: backpack
}

@inventory player_inventory {
  slots: #{#topics_slot #backpack_slot}
  initial_topics: [#topic_1 #topic_2 ...]
  initial_backpack: [#item_1 #item_2 ...]
}

Is this better? I think so. I certainly don’t think it’s worse, even with the implicit link being made through the @slot accessor:. Overall, I claim a win!

Inch-by-inch Rez gets closer to the tool I imagined.

2 Likes