Rez v1.2.8 — open source engine for choice-based HTML games

But after a while you start wanting to incorporate some more advanced features like actors and inventory. Twine is… just passages of text. Increasingly you gravitate from ‘Harlowe’ and ‘SugarCube’ (the two major ‘formats’ for writing in Twine) to ‘Snowman’ which is a plain wrapper. And then you start making your game mainly by writing Javascript.

The day comes when you realise that Twine isn’t actually offering you very much.

I would like to disagree on this on a few things if you don’t mind.
You can do a lot more than just passages of text in Twine, even while mainly staying with the base macros of the format. Inventories and NPCs (I think that’s what you mean by Actors?) can be done with either of the more basic formats (there are many new users asking for helps for their Combat Systems or how to manage inventories in the Twine Discord).
And there are a lot of resources for Twine to help customise the basic format. Some even create custom macro so you don’t have to deal with coding JavaScript.
See:

I’m gonna plug: DOL-OS and The Thick Table Tavern that rely heavily on visuals and manipulations of elements on the screen. There may be some jQuery coded in the first, but over 95% of the code in both these projects were done in SugarCube.

Twine is a good tool for a lot of people, and it does offer a good starting base for many getting in the community. Some formats might not offer enough for some (which is why there is Snowman to help you do stuff from scratch, and why other people create their own format), but I fail to see how Twine doesn’t offer much with the kinds of games created in Twine…

Or were you just describing your experience with Twine?

2 Likes

Oh, disagree away :smiley:

I by no means meant to suggest that such things are impossible. I think Twine is an amazing tool but, by it’s own admission, it’s sweet spot is as “an open-source tool for telling interactive, nonlinear stories”. At this it excels and has clearly enabled all kinds of people who would never go near the likes of Inform or TADS to create some amazing works.

What I was, in my own clumsy way, trying to get at is that it doesn’t form a coherent system for supporting more complex games. Yes you can use Snowman and build stuff in pure JS. That’s the route I went down.

But at this point you’re often having to build or stitch together a framework in JS and Twine, at this point, largely offers constraints that, I found more frustrating than inspiring.

Again I am not trying to suggest you can’t make games in Twine, even complex games. But I don’t think it’s well suited to the latter purpose.

2 Likes

I am not sure by what you mean by complex game, then.

Without an international standard for complexity this is a difficult question to answer. What I think is complex you may consider trivial or vice verca.

I’m building a game with many NPC’s with behaviour trees, different inventory & item types (memories, dialogs, spells, equipment), procedural generation of elements, factions, relationships, plot clocks, with a UI structured into different scenes, and so on.

Rez has support for all of these concepts “out of the box” as a coherent system.

I agree, you can implement this stuff in Twine. Indeed I built the beginnings of the Rez std library as ad hoc JS libraries for Twine+Snowman before I started work on Rez.

If you want to do calligraphy, you can do it with a biro. But there are other tools more suited. That doesn’t devalue the biro.

3 Likes

Sounds like an impressive enterprise to undertake. Good luck!
:sweat_smile: I thing I was just a bit ticked about the language of the post, it did make it seem to devalue Twine, imo. Even if unintentionally.

2 Likes

Thanks and I think you’re right. I was a little strident and dismissive in my tone. It wasn’t my intention but I can see how it comes across.

2 Likes

I think it’s the same design instinct that behind the Adventures Story Format. Twine lets you do all the things you describe for Rez, but it doesn’t supply them. You have to make your own inventory system, or NPC system, or action-response system — or find someone else’s implementation of them and massage them into your own game.

i.e. beyond the basic branching choice-based narrative, most Story Formats provide you with the tools you’d use to build the systems that the game needs to run on, rather than providing the systems themselves. Of course that’s a strength, if the systems you need aren’t ones that anyone else has ever needed (or needed together). But if you have a particular sort of game structure in mind (and as @manonamora says, there are a lot of people who want inventory/combat/player dolls/quests/character builders) then having a language that offers those systems “out of the box” makes a lot of sense.

1 Like

Yes, it’s perfectly possible to do all these things in Twine and, indeed, that’s what I was doing. But it’s not just implementing them, but also combining things together into a coherent whole. I found that lead to a weak foundation.

What I’ve attempted is a foundation of “game system” over a foundation of “interactive fiction”. The systems in Rez are meant to encompass the core concepts that you’d expect to build a game on top of.

While, of course, those systems are biased towards my needs I’ve given thought to leaving them flexible enough to handle use cases I haven’t anticipated. That can’t be perfect of course, but it’s the intention.

For example the Rez inventory system employs 4 interlocking elements (‘element’ being the Rez term for a game component):

  • inventory
  • slot
  • item
  • effect

An inventory is a container, composed of a set of slots, and optionally attached to another game element (e.g. an actor).

Slots accept particular kinds of items and can have rules defined about how they combine (Example: in a fantasy RPG game, we might have a slot for main-hand & off-hand, and a two-handed slot. We can setup rules so that the 2-handed slot is unavailable if either the main or off-hand slots have contents, and vice verca).

Items are a generic ‘thing’ so can be used as easily for memories and potions. Every item has a type and there are rules for matching the types of items to the types that any given slot accepts. These rules include a conceptual kind of inheritance of item types that is relatively simple but can express most combinations.

Finally, effects can be applied to items and triggered by slots. Having a ring in your pocket might do nothing, but put it in a ‘hand’ slot and you might turn invisible.

So far, I haven’t come across a “container” situation I can’t express with these tools. Of course sooner or later it will happen and I will have to adapt :slight_smile:

If anyone else gives a serious attempt to building a game in Rez I will, of course, be interested to hear their feedback and where possible to accommodate their needs as authors.

1 Like

So… it sounds like a weapon is just an item that goes in the hand slot (making it equipped) for attacking. If I wanted to make the weapon have gem slots, the weapon item could also be a container and have it’s own slots associated with it?

I hadn’t thought of that but it would be a simple enough adjustment. In fact it might actually work out of the box, I don’t think I check that the container is actually an actor.

This would look like this:

@derive :1h_item :item

@slot hand_slot {
  accepts :1h_item
}

@slot belt_slot {
  accepts: :item
}

@inventory player_inventory {
  slots: #{#hand_slot #belt_slot}
}

@item sword {
  type: 1h_item
}

@item rock {
  type: item
}

The @derive directive establishes a hierarchy of types. In the example above it says that :1h_item is a type of :item.

The belt_slot accepts anything with type :item. The hand_slot only accepts things that are of type :1h_item. So the sword can go in either slot but the rock will only go in the belt_slot.

From a practical perspective it means you can answer the question “Should the player be allowed to put this item here?”

1 Like

Building Rez I knew I wanted to be make use of dynamic UI, so I needed a way of creating templates. After looking at several options I picked Handlebars. Pre-1.0 versions of Rez allowed you write @card content as a Handlebars template. The Rez compiler shelled out to the Handlebars compiler to precompile these templates into functions that could be executed at run-time.

From the get go I was unhappy with the approach I had taken. I could never like the Handlebars syntax, how it was difficult to integrate with Rez, and the dependency on the external compiler made the installation story more annoying.

For 1.0 I decided to bite the bullet and replace Handlebars with Rez’s own template framework that would be deeply embedded. It was a bit of effort but the result is, I think, worth it.

There are four types of expression, the most simple is an interpolation. When you want to put a variable into a template you do this:

${variable}

So, for example, to display the players name

${player.name}

But you cannot execute arbitrary Javascript here so:

${player.name + "'s"}

Is not a legal template expression. But this is where the nod to Liquid comes because we have filters that can alter a value. It’s like sending the data through a pipeline where, at each stage, it is transformed. So, in this case, we would write:

${player.name | possessive}

Which makes use of the possessive filter. Here is how that’s implemented in the stdlib:

@filter STRING_POSSESSIVE_FILTER {
  %% String -> String

  name: "possessive"
  impl: (s) => {return s.possessive();}
}

The standard library defines over 40 such filters (for things like plurals, array manipulation, generating links) and you can implement your own filters in exactly the same way.

The second expression is the “conditional”:

$if(cond) {% true output %}, {% false output %}

If the cond expression evaluates to true output the first sub-expression, otherwise the second sub-expression (or nothing if there isn’t a 2nd expression). These expressions may, themselves, be template expressions and nest as deeply as you need them to.

$if(player.wounded) {%
  <p style="color: red;">You are wounded, and cannot move very quickly.</p>
%}, {%
  <p style="color: green;">You are fit and healthy and can move normally.</p>
%}

The third type of expression is the “iterator”:

$for(x:xs) {% each-x output %}, {% separator output %}

When using the iterator xs must Javascript expression that you can run map over (e.g. an array). It binds x to each value in turn, running the sub-expression, and collecting its output. Then it joins all the output together (if an optional second expression is giving it uses that to join them).

From here you can visit:<ul>
$for(loc: location.exits) {%
  <li><a data-event="move" data-target="${loc.id}">${loc.name}</a></li>
%}
</ul>

With a separator:

$for(p:people) {% ${p.name} %}, {%, %}

The last type of expression is the “do” expression:

$do{...code...}

This allows for executing arbitrary code as the template is being rendered. Care should be taken here and it’s likely more appropriate to use one of the built in callbacks such as on_start and on_render. But the facility is there if it’s needed.

The result is a decently expressive template language that is built right in and tailored to Rez’s needs. All template expressions are compiled to Javascript functions at compile time so they are pretty fast to execute.

1 Like

Do you intend to support an equivalent of $elseif ?

2 Likes

I wonder how that might be formatted, if it exists or is in the works…

$if(player.pristine) {%
     OKAY
%}, (player.wounded) {%
     NOT OKAY
%}, {%
     NURSE!
%}

The need hasn’t arisen for me personally, although I’m not opposed to adding it, depending on finding a good syntax.

1 Like

This looks plausible although I will have to give it a little thought. Thanks for the suggestion!

1 Like

I’m liking what I’m seeing, it gives me ideas. I’ll have a tinker with it!

2 Likes

Just playing, maybe taking a leaf from Clojure/Elixir cond:

$if (...) -> {% ... %}
    (...) -> {% ... %}
    (...) -> {% ... %}
    {% ... %}

It’s more verbose but, perhaps, clearer about what is going on? The ‘,’ was a kind of useful “connective tissue” but not necessarily required in this version. If we aped cond more closely we’d get:

$if (...) -> {% ... %}
    (...) -> {% ... %}
    (...) -> {% ... %}
    (true) -> {% ... %}

and not need a ‘special’ else syntax.

However, I do appreciate regularity in my syntax and losing the commas would make if different to foreach for not necessarily any good reason.

Of course, right now, you could write:

$if(x > 0) {%
  ...
%}, {%
  $if(x < -25) {%
    ...
  %}, {%
    ...
  %}
%}

So elseif is possible with the existing syntax, all this is doing is “neatening” it up a little. On balance I am inclined to just see if I can implement your solution, I don’t think it makes the parser particularly more complicated.

1 Like

Very cool! I wasn’t even thinking about nesting $if conditions. I agree with you in that, I really don’t know how useful a new elseif syntax would be, other than condensing code. Do you make a case/switch statement structure with breaks too? Like, how far down the rabbit hole do you need to go? KISS method whenever possible, I say. I was just suggesting a possible syntax/structure… but to be honest, nested $if conditions look good to me too (it’s not that different than seeing a JS if() { ... } else if() { ... } else { ... }.

I haven’t had any time to play with Rez yet, but I do enjoy reading these posts and learning more about it’s philosophy and capabilities. I really like what you’ve proposed with Rez and it resonates with me. It definitely fills a niche that traditional Twine story formats don’t. I went down a Twine path of Harlowe, Chapbook, Snowman and well… thank you for making Rez! :slight_smile:

1 Like

This is a great question:

In syntax, as in many things, I’m a fan of minimal, regular, and ergonomic. I think having fewer, more powerful, concepts that can be understood & combined makes things easier to learn while, at the same time, expanding their possibilities. But I’m not a purist.

In this context we’re talking about embedded expressions, within HTML, within the Rez syntax. I think there is a basic, conceptual, overhead to each level of nesting that needs to be managed. I also think it’s harder to reason about the overall logic of what something is trying to achieve when using nested conditions like this. So I think allowing the logic to be layed out at the same level has value. But, again, there are limits.

I’m playing with this at the moment. Sadly it’s reminding me how much work there needs to be done on the parser in terms of error handing. Although I think I have gone further than most parser combinator libraries I’ve looked at, it’s still not quite a strength.

There will be something for this in 1.1.7.

1 Like

Thank you. While Rez started as a replacement for Twine, over the last two years, I do feel like it’s grown into its own thing. I hope you will find an opportunity to use it but, in any case, I appreciate the thoughts, questions, and support.

1 Like