Rez v1.8.0 — An Open-Source tool for creating choice based games/IF

Technically, yes, but I would recommend pushing the element ids instead.

Do I need to store their IDs as text attributes matching their declared element names for that purpose, or is there a built in syntax to fetch an element’s id anytime I need it?

(also, let me know if it would be appropriate to keep pestering you with questions, perhaps in DMs. I would gladly jump the REZ wagon totest it, but I can’t get the basic mechanics to work so far)

In the first case I am happy for you to ask questions. Either here, or I created a Discord. I’m also a little humbled that your experience has been so problematic, hopefully you’ll stick with so I can fix the problems.

On to your question: Every element you define has an id (the @game element is automatically assigned the id #game other IDs you assign yourself).

Every element in the .rez source files gets translated into JS object by the compiler (using classes RezObject, RezCard, RezGame and so on) with an id property that gives you back that id. So your definition:

@location gym_ring {
  $global: true
}

Will end up creating a RezObjectJS object whose id property is "gym_ring".

If you look at the end of dist/assets/js/runtime.js you will see the objects being created and how the id & attributes are assigned to it.

In the runtime you can go from id string to an object using the $ function, e.g. $(“gym_ring”). $ is a shorthand for the method getGameObject() on the singleton RezGame object.

If its an object you want to be able to refer to frequently you can set $global: true and the runtime defines a constant value $gym_ring with the reference (i.e. the id becomes the variable name).

So in the above example:

$gym_ring == $("gym_ring")
$gym_ring.id == $("gym_ring").id == "gym_ring"

Does that help?

Yesterday I released Rez v1.9.2 which contains the following changes:

  • Updated built-in Bulma CSS to v1.0.4
  • Updated built-in Alpine.JS to v3.15.2
  • @list can use includes: to pull in values from another @list
  • Detect invalidly closed user components (e.g. <.pb>….<./pb>)
  • Extensions to the @pragma API
  • The blocks: attribute is changed from a list to a binding list so you can name the binding
  • Switched from breadth-first to depth-first initialisation of runtime objects
  • Adds +#{:foo :bar :baz} to merge new elements into set values defined in defaults
  • Compiler error if you try to $(…) include a file twice
  • Template variables are automatically converted into a template function that calls the rendering engine with the object bound.
  • Several dozen bug fixes
1 Like

Thanks, it makes perfect sense.

I’m not discouraged easily :slight_smile: Switching to discord then.

1 Like

Today I am releasing Rez v1.9.4 which contains the following changes:

  • Rationalised event handling and naming
  • @plotplot now supports subscribers (which can be other @plots)
  • Added support for displaying & cancelling flash messages to the base template
  • Add support for event, card, interlude,scene, and resume for <button> tags
  • Can use <a event='event-name'>…</a>syntax for creating event firing links
  • Template expressions can use indexed accessors ${a[0]} or $foreach(x: card.values[0])
  • Fixes auto-referencing parent binding
  • Event transformations are now tied to the rez-evented CSS class which can be applied anywhere that you need dynamic links/buttons to work.
  • The addCopy() API now supports passing an id for the copy.
  • Browser & form events now fall back to event-bubbling if no handler is present in the current card
  • Improved template compiler error messages
  • Fixes ptable attribute encoding
  • Binding list attribute can include keyword values
  • Major revisions to API docs
  • Numerous bug fixes

I want to extend my thanks to @BlackTrifle for pointing out the many errors & inconsistencies that they have come across. This release is easily the best tested so far.

2 Likes

How I am handling maps in my Rez games. I have two concepts a “zone” and a “location”. Zones are a way to ‘collect’ related locations. For example the players ship might be a zone. The space station might be a zone. Or the docking area might be a zone.

@elem zone = object

@schema zone {
  label: {kind: [:string :property], required: true}
}

@defaults zone {
  $init_after: [@location]

  location_ids: ^i{
    return $game.filterObjects((o) => o?.$alias === "location" && o?.zone_id == this.id).map((o) => o.id);
  }

  locations: ^p{
    if(!this.locations_cache) {
      this.locations_cache = this.location_ids.refs();
    }
    return this.locations_cache;
  }
}

@elem location = card

@schema location {
  zone_id: {kind: :elem_ref, required: true}
  description: {kind: [:string :property], required: true}
  exits: {kind: :list, ref_elem: @location}
}

@defaults location {
  zone_id: _
  int_name: _
  ext_name: ^i{return this.int_name;}
  description: ^p{return `${this.zone.label} ${this.label}`;}
  exits: []
  on_will_start: (card) => {
    $player.location_id = card.id;
  }
}

Note that @location is actually a kind of @card. Cards are Rez’s equivalent to the Twine passage. When played their content: attribute is rendered.

When you play a @location card it automatically sets the players location_id attribute to that location. You can play other cards into the scene and the players location will stay the same. Until they move to another @location.

So an example might be:

@zone z_docking_ring {
  label: "The Docking Ring"
}

@location hangar {
  zone_id: #z_docking_ring
  int_name: "hangar"
  bindings: [player: #player ship: player.ship ship_name: ship.name]
  exits: [#airlock #lower_bazaar]
  content: ```
  <.pb>You are on the hangar deck. Through the viewport you can see the ${ship_name}, normally so huge, tiny against the station.</.pb>
  <.exits />
  ```
}

@zone z_ship {
  ship_name: ^p{return $player.ship.name}
  label: ```${this.ship_name}```
}

@location airlock {
  zone_id: #z_ship
  int_name: "airlock"
  pressurised: true
  pressurised_desc: ^p{return this.pressurised ? "pressurised" : "depressurised"}
  exits: [#hangar #corridor]
  content: ```
  <.pb>You are in the ships airlock. It is ${this.pressurised_desc}.</.pb>
  <.exits />
  ```
}

Each location specifies a list of other locations that are reachable using the exits: attribute with the id of those @location elements. Then I have a component (<.exits />) that builds the list of exits as links to those location cards:

@component exits (_bindings, _assigns, _content) => {
  const exit_links = $player.location.exits.refs().map((exit) => `<li><a data-event="card" data-target="${exit.id}">${exit.ext_name}</a></li>`).join("\n");
  return `<div class="container"><ul>${exit_links}</ul></div>`;
}

There’s more stuff layered on top of this, but this is the core. I will including this in the cookbook in future releases.

The next thing I layered over the top is the ability for locations to decide whether they are reachable or not.

@location airlock {
  reachable: function(decision) {
    if(this.pressurised) {
      decision.yes();
    } else {
      decision.no("The airlock is not pressurised");
    }
  }
}

Now the <.exits /> component can use the result of reachable to either display a link to the location, or a message about why the location cannot be reached (yet).

The sharp eyed might notice that the <.exits /> component uses the ext_name property of a @location. int_name is the ‘interior name’ or how the location is referred to when you are in it, while ext_name is the ‘exterior name’ or how its referred to when you are elsewhere. By default they are the same but you can use a property to customise it.

In a different context we may not want to show an exit at all. This is how we can make that work:

@location wizards_lair {
  reachable: function(decision) {
    if(!$player.knows_wizards_lair_exists) {
      decision.hide();
    } else if(!$player.level < 5) {
      decision.no("Powerful magic blocks you");
    } else { 
      decision.yes();
    }
  }
}

Now the <.exits /> component can use a location’s reachable attribute to customise whether it can be reached, or even seen. This depends on RezDecision which is part of the stdlib and is handy for dynamically configuring yes/no/maybe situations.

In this way we preserve the elegant passage metaphor of Twine but allow for the creation of complex, dynamic, world models.

This is how I am building my game Slipstream (albeit slowly building).

2 Likes

It’s great to see you’ve added locations to Rez. I’ve always felt that locations actually go very well with choice mechanics. Because isn’t movement just another choice?

It also significantly increases agency, as you can tackle a range of choice-based challenges in different areas simultaneously.

2 Likes

Thanks.

In fact locations were originally built in to Rez before I ripped them out as they overlapped with cards as the presentation layer.

I’ve gone back and forth but have finally come up with an implementation (locations ARE special cards) that is working for my WIP games.

They won’t be built into Rez but will be part of the cookbook lib so they can be imported into a game.

Also this validates that the core of Rez is doing a reasonable job. I was able to build the locations system on top of the engine & stdlib.

I think this is a really good point. I also have an “actions” implementation that is similar but more complex and not as well baked yet. The aim is to require very little activity to be hard-coded.

I think this really goes to show the versatility of Rez’s core elements - rez engine and stdlib can be readily employed to mimic any behaviour, from locations to actions.

1 Like

After I get 1.9.5 out the door I am going to try and steel myself to work on the documentation. It’s gotten out of date which is no help to anyone. The problem is that I can just go look at the source to see how something should work but I can’t expect an author to do that.

Yup the time for docs is ripe.

Which reminds me, it’s a bit past time I posted a report on my experience with Rez.

I’ve been using it a lot for about 2 months now, and I’m happy to report that as far as I can tell, as of v1.9.4 Rez is fully functional - and bug free. It’s also everything it says on the tin, and surely can breeze through IF of any complexity.

I was able to successfully finish a demo of a choice / parser hybrid I’ve been meaning to make for a long time, and have previously abandoned twice (on inform 7 and twine, to be specifc), without much trouble. Once I wrapped my head around the specifics of the language, I never felt stuck or limited in any way. With the full power of JS readily available, Rez could easily handle anything I threw at it.

In fact, the amount of sugaring Rez provides is just right: it always comes in handy and never gets in the way. 99% of the time you’re just writing plain JS, and everything works as it should; no weird syntax is forced on you - which is a blessing. Given how simple and intuitive JS is (well, for basic IF needs anyway), I think it’s a great design decision.

Also, I really dig the “event” philosophy that ties everything together, and I find it very flexible. A lot of careful thought must have gone into building Rez to make everything work so seamlessly under the hood.

I only used @ cards and @ objects for anything from locations to people to abstract concepts, and they proved plenty sufficient. Which means I didn’t even touch all the available functionality - or ever felt the need to. In fact, if anything, Rez might already have too much instead of too little - but those extra elements are “opt-in”, so once again, nothing is forced on you. They’re just there, in case you need them.

For screen output Rez allows full html5 markup - so customization options are endless. Your game can literally look anything you want. And the little custom scripting language that comes with rez is a great help for complex output patterns, including loops and conditionals.

With all basic mechanics already in place and working smoothly, nothing is stopping me from trying to build a full game now.

hats off @sandbags :slight_smile:

4 Likes

@BlackTrifle Please share with us when you have a game or demo built. I’d love to see Rez in action. :slight_smile:

Gladly. But rn it’s largely empty (although all core mechanics are tested and woking). I feel like I need to fill it up a bit before it can serve as a proper demonstration.

1 Like

I’m grateful to have someone else kicking the tyres and have to give @BlackTrifle credit since they’re working without up to date documentation or a really good demo game. I shall be embarrassed if they can publish their game before I do mine.

2 Likes

I’ve just released Rez v1.9.5.

  • Added: Implemented complete Javascript function declaration parser to support default parameters and destructuring
  • Added: @game now supports $window_event: [event handler ...]
  • Added: @quest element to complement @plot
  • Added: JS @asset can now specify $load_in_body: true to load before </body>
  • Added: $break{expr} template expression to trip debugger
  • Added: $init_after supports @element-type as well as #element-id
  • Added: Support for custom JS mods
  • Added: @faction now has member_ids:
  • Added: @generator passes idx to customise function
  • Added: @generator supports shuffle: true to auto-shuffle generated copies
  • Added: @generator source_id: can now be a ptable
  • Added: @slot defines has_capacity:
  • Added: Default args to stdlib round_to_nearest()
  • Added: Better error messages when an attribute value is invalid
  • Added: Validation of blocks: attribute values
  • Added: TypeHierarchy is now available at runtime
  • Changed: @schema for functions now uses min_arity/max_arity instead of params
  • Changed: @inventory specifies slots as a binding-list where the prefix replaces the @slot accessor
  • Fixed: 900+ issues raised by the biome JS lint checker
  • Fixed: Trying to bind this in attribute templates didn’t work, switched to self
  • Fixed: Incorrect schema validation of collections
  • Fixed: Runtime type hierarchy checks no longer restricted to parents
  • Fixed: Old references to @macro updated to @component
  • Fixed: rez-bind now supports <input type="number">

The mod support was kind of an “I wonder if this is possible” and to prove that I could. It works in that it can load arbitrary Javascript files in the local browser that can interact with the game via the runtime API. In the future the compiler could support generating a mod. Of course for that to be relevant we need popular games so this is more of a stake in the ground.

Running the biome linter over the generated runtime.js was eye-opening. Over 900 issues popped out. It was a bit of a slog to fix them all. Mostly they were “not quite modern” choices rather than actual problems. Still done now.

The @quest element is a complement to @plot which implements Tim Cain’s approach to quests. I did try to merge them into one but @plot is a plot-clock (a la Blades in the Dark) and encodes a rather different idea.

@generators are significantly beefed up in this release and I am using them a lot. I’m tinkering with how @inventory works as I am dealing with new cases.

The $break{} template expression replaces most uses of $do{} (mostly I am writing $do{debugger} and adds a convenience. $break{} is equivalent to $do{debugger} while $break{foo === “bar”} is equivalent to $do{if(foo === “bar”) { debugger; }} not a big change but a little more convenient. I’ve deprecated $do{} although not removed it yet.

If anyone has played The Lords of Midnight. I think it’s a good conceptual pre-cursor to the kind of IF game I want to make. It’s IF but not in the “take rock. put rock in bucket” puzzle sense but, rather, a hybrid of strategy game (Luxor raising armies to attack the north) and narrative-driven quest (Morkin’s attempt to destroy the ice-crown). In the ‘epic’ variant of the game you do both.

I am probably more character/RPG driven than LoM and I don’t draw stylistically from that game, but some of the root DNA comes from there.

2 Likes

u have my attention

2 Likes

What can I tell you?

So Blades in the Dark has this notion of “progress clock” which is a circle divided into a number of wedge segments. The idea is for the person running the game to use it to track effort towards tackling an obstacle or trouble incoming.

For example you might have a clock for ‘perimeter security’ of a building. As the players overcome challenges you tick off segments and when the clock is full they have achieved their goal, e.g. infiltrate the security base. Or perhaps it represents the alertness of the guards and each time something goes wrong you tick off a segment. When the clock is full an alarm sounds.

In the latter context for example you might have to sneak through a number of corridors, past sentry points, through vents etc… if security is lax you might have 6 segments. If security is very tight you might have 3.

In the game Citizen Sleeper they actual show plot clocks to the player in the UI as a circle with wedges getting filled in. It works in that game but might not work everywhere. I’m still thinking this through.

For example in Slipstream I use @plot to track system security when the player is trying to infiltrate a system. I have similar plans for systems hacking. How to represent the current state of the clock is a question. For example I might represent a system with a 3-segment clock as “High security” and one with an 8-segment clock as “Lax security” rather than actually showing the clock in the UI itself. I could represent the current state of the clock as a number of radar pulses representing the increasing alertness of the security.

At the time I introduced it, I called the element @plot because, to me, it was representing moving the game plot along and @clock was too overloaded a term.

I recently introduced @quest to represent in-game tasks/objectives/quests the player is given (typically by an NPC) which the player has to complete and ‘turn in’ (typically to the same NPC).

This is because I was watching Tim Cain talk about the states he applies to quests in his games:

  • unknown
  • mentioned (known to the player)
  • accepted
  • achieved (technically met the requirements)
  • completed (turned in for their reward if any)
  • botched

And tried to think how i could apply that to @plot and found that they were really doing two quite different things. Hence @quest.

An interesting detail is that a quest can be botched (which sets botched as its status) but can also be unbotched again at which point it resumes its previous status (except for quests which are completed which cannot, by definition, botch). An example would be that you are escorting an NPC and they get killed (botch). Later you obtain the scroll of Frotz which can revive dead people and use it on their corpse (unbotch) and finish escorting them.

There’s a whole train of thought here about systems vs scripts which I won’t go into but is one of the reasons I love listening to Tim Cain.

So @quest represents the kind of things players might have in a journal or be given as an objective to complete while @plot represents obstacles or challenges along the way. I see a @quest as maybe using multiple @plot's in its implementation.

1 Like