Rez v1.7.0 — open source language & engine for making procedural narrative games

It’s a good question. I think for questions about bugs etc… a GitHub issue might be best. For questions about building IF maybe here makes more sense to me. But either seems acceptable. I am also on the IntFiction Discord.

1 Like

I’m so excited! I got it to work. I tried using Twine (again) but I just couldn’t figure out how to compress the gameplay etc. into static branches. I hope this works out well.

2 Likes

I hope it works for you too, feel free to ask questions. Can you say any more about the problem you were having with Twine? Perhaps I can offer some suggestions.

Two common things that trip me up (still):

  • using , as a list separator - when I started this project I was still using Clojure and very much liked how Clojure treats commas as whitespace. These days, and given that Rez has more in common with JS, I regret that decision. The parser should ignore commas but this doesn’t always work.
  • not closing a template. when you write long templates it can be easy to forget to close one and it then consumes a bunch of other stuff until it hits the next open-template syntax. this one can be hard to spot as the error is confusing.

I should either make commas part of the language or improve the parser, and error handling needs improvements anyway.

Two traps for the unwary:

  • adding Javascript properties. Never assign a Javascript property without declaring a Rez attribute.
  • not using _id attributes.

Rez transparently adds JS properties for attributes, whose values are managed internally. So:

@object foo {
  x: 1
}

Automatically means there will be a property x for foo. If you assign JS properties they won’t be managed and won’t take part in undo (in progress) or save/load. In practice there is never a reason to declare a JS property. Use:

@object foo {
  x: _
}

If you want to assign the value later, or the setAttribute/getAttribute methods. Note that using setAttribute won’t auto-create properties when objects get initialised.

A contrived example but this is a bad idea:

@object foo {
  bar: _
}

@object bar {
}

%% somewhere else
const foo = $("foo");
const bar = $("bar");
foo.bar = bar;

What you should do is:

@object foo {
  bar_id: _
}

@object bar {
}

%% Later
const foo = $("foo");
const bar = $("bar");

%% Either works
foo.bar_id = bar.id;
foo.bar = bar;

The difference is subtle but when you specify an attribute with the suffix _id (e.g. bar_id) Rez knows to make a separate managed property for the unsuffixed name (bar). Under the hood, Rez tracks the id and looks up the object dynamically. This means that the attribute graph doesn’t have to deal with object references.

A third trap for the unwary and one I fell into on the stream:

  • if you are creating copies with copyWithAutoId() you need to track them with $game.addGameObject()

Without this step the lookup function $() can’t find them by id, although that may not be a problem because you are probably managing them in a collection. The bigger problem will be that they don’t get saved or loaded so you’ll have dangling ids.

With one simple optimisation I have cut compile times from ~2.9s to ~0.9s.

The optimisation was not in Rez but in Ergo, the parser combinator library that I built to make Rez.

At the very heart of that is a function called next_char that reads one character from the input. I was doing something incredibly inefficient there that was slower than it needed to be and created a lot of garbage. Parsing my current game file, the next_char function gets called 287,304 times! A 2-line change and poof, fast again :grinning:

3 Likes

I’ve just released v1.5.2 which includes the compiler performance improvements.

It also contains the beginning of a --output-obj-map option intended to create a “map” of the game content.

At the moment it simply lists the known objects and their types, but I hope to make it more useful as I go. I’m already starting to find it a little hard to reason about what I have.

Oh, interesting! I was going to author my own game map. I’m not sure if that will work with or against this feature.

I put “map” in quotes because it’s not a map in the sense of “room a leads to room b” but a ‘map’ of the elements defined in your game files, and some metadata about them.

Ideally things like how they relate to each other elements although this can be tricky because so much in Rez can be dynamic.

Here is an example of using --write-obj-map with my current WIP:

At some point this won’t scale very well but it’s a way of quickly assessing what I’m defining in the game. It filters out the built-in stuff although I could make that an option.

1 Like

Would it be possible to implement RenPy-like/standard visual novel GUI controls in Rez? These controls include: back, history, skip, auto, save, q. save, q. load, prefs. I guess all of those controls would be a <button> inside some reused/copy-pasted HTML element (or a block: in Rez?)

I can figure out the HTML/CSS myself, but I’m more wondering what the actual functionality for each of those controls would look like. I can mark up the HTML <button> for q. save and style it with CSS, but I’m not sure what the JavaScript would look like for giving the button real functionality.

(I don’t want to use RenPy, or Twine, I already tried those engines. RenPy is actually the most painful of the engines I have tried - I prefer Rez, I just think the standard VN GUI controls are extremely useful.)

Possible? Yes, up to a point.

At the moment Rez has the ability to save/load to/from JSON files.

I’m assuming that Quick Save/Load would probably be to local storage? I haven’t played with that but I don’t see why you could store the same objects that get serialised.

Back/History/Auto/Skip I’m really not sure what those are doing. I’ve never used RenPy so perhaps they have particular meanings there?

If back is “go back a card” then that’s probably possible. Undo is still kind of a work in progress.

If you can explain a little bit more about what you intend these functions to do I might be able to give a more informed answer.

Here’s what I was planning to do (I asked Copilot for coding/general implementation help. it’s funny because I keep asking Copilot to code in Rez but it doesn’t know how)


1. Back

  • Goal: Allow the player to go back to a previous state or passage.
    • You need to keep track of the player’s current and previous locations in the game.
    • When the player uses the “back” function, you should retrieve the previous location from memory and navigate back to it.

2. History

  • Goal: Track and show the passages or sections the player has visited.
    • Maintain a list (or history) of all the passages the player has visited.
    • When the player requests to view history, display this list in a readable format.

3. Skip

  • Goal: Allow the player to skip to the next major section or passage.
    • Define key points in your game as major sections.
    • Implement a command that will jump to the next major section when invoked.

4. Auto

  • Goal: Automatically advance to the next section after a set time interval.
    • Create a timer that counts down.
    • When the timer reaches zero, the game should automatically move to the next section.

5. Save

  • Goal: Save the current state of the game so the player can resume later.
    • Implement a save function that records all necessary game state data.
    • Store this data in a file or memory slot.
    • This command saves the current game state, including variables, character positions, and other game data. It’s typically used when the player explicitly chooses to save their progress.

6. Quick Save

  • Goal: Provide a quick and easy way to save the game state.
    • This command is a shortcut for saving the game quickly without needing to navigate through menus or prompts. It’s designed for convenience, allowing players to save their progress with minimal interruption to gameplay.
      • meant to be faster, especially during critical moments
    • Create a shortcut to save the current game state quickly.
    • Store this quick save in a predetermined slot or file.

7. Quick Load

  • Goal: Allow the player to quickly load the game state from the quick save.
    • allows players to quickly resume their game from the last quick save point
  • Implementation: Create a shortcut to load the game state from the quick save slot or file.

8. Preferences

  • Goal: Allow players to access and modify game preferences such as volume, text speed, etc.
    • Create a preferences menu where players can change settings.
    • Implement the logic to apply these settings to the game.

I have a much simpler question! How do I give each @scene a unique background? I feel like I need to change the CSS background-image for the HTML <body> element, maybe? But how would I actually implement this? My mind is blanking.

I want to give each scene one dedicated background. Different @card elements at the same map location (same @scene) will all have the same background.

You don’t have access to the <body> tag, mainly because it’s never been relevant.

Do you need to set a background-image on the <body>? You can’t set it on a <div> within the @scene layout?

If you do, I will have to think about how that could work. It’s probably not that hard, it just never came up before.

For example I could add a @scene attribute for background image that would get applied to the <body> automatically. That might be simple and do the trick. But it depends how much you need to be able to configure it, add multiple styles, etc…

1 Like

There’s no specific support for this. Bear in mind there’s no specific concept for ‘location’ either. Where it’s been relevant I create @object elements for locations that work in tandem with @cards that are relevant in a location.

There is no specific support for history, it’s not something I have considered before.

I think it would be feasible to create a history system that recorded card & scene events and then interlude to a scene that showed you what had happened.

I’m not sure I understand this one. Rez is organised around scenes that play one or more cards. I mean you decide which scene follows which. Scratching my head a little to work out how this is meant to play out.

You could implement this with @timer and a script attribute to change to another event.

Save, Quick Save, and Quick Load should all be relatively easy. Save and load exist and I can take a look at quick save/load to/from localStorage.

There’s nothing stopping you implementing preferences. I’d implement it as a @scene and use an interlude to present it in game. General preferences I’d just make an attribute of the @game.

What kind of game are you trying to create?

M

1 Like

Hrmm… trying to figure this out I’ve made a change to the index template so you can write:

@game {
  body_styles: {
    background: "#ee00aa"
  }
}

and these are then applied to the HTML <body>, e.g.:

<body style="background: #ee00aa;">

And that’s all good. But there are two wrinkles with setting background-image.

The first is that you want to set it to the image path which means getting the @asset’s $dist_path attribute.

The second is that - is not a legal attribute character, because it’s not a legal character in Javascript identifiers. So you can’t write:

@game {
  body_styles: {
    background-image: "..."
  }
}

In theory I could let you write:

@game {
  body_styles: {
    background_image: "..."
  }
}

and automatically transform snake-case (background_image) to kebab-case (background-image). Are there situations where that’s not what you’d want though?

If that worked you’d end up with something like:

@game {
  body_styles: {
    background_image: &background_asset_id.$dist_path
  }
}

And then I would have to check that attribute refs are still working properly. They used to be much more relevant in earlier versions of Rez that didn’t have property support, they may have gotten broken.

An alternative and much simpler approach to this problem would be to simply support backgrounds a la:

@game {
  background_image_id: #background_asset_id
}

But I suspect that just leads to “can I set background-position” or “background-repeat” and I end up special-casing a series of CSS properties.

I’m not entirely sure about the approach I have taken, but it does work:

@asset background_image {
  file_name: "lizard.png"
}

@game {
  body_styles: {
    background: "#66ee00"
    background_image: &background_image.$dist_path
  }
}

The reason I am not sure about it, is that it makes a number of quite specific assumptions. For example I am treating the body_styles attribute differently. It uses a table value which I don’t like (I’ve thought several times about getting rid of table attributes), it makes use of &#id.attr attribute refs which were always a bit of a hack and special cases that @asset turns into url('<value>') instead of simply value.

[Edit] Actually there’s a better reason to not like this approach. I forgot that the <body> doesn’t get rewritten for different events, it’s the contents of the <div id='game-container'> that are getting replaced. So changing the body_styles attribute won’t change anything.

Back to the drawing board.

I am such an idiot. Never code tired, there’s a much easier way.

I’ve added an event handler for scene_start to RezGame so now you can write:

@game {
  on_scene_start: (game) => {
    const backgroundPath = $("background_image").$dist_path;
    document.body.style.backgroundImage = `url('${backgroundPath}')`;
  }
}

You can do whatever you like with the body tag, no special attributes or processing required. This is now released in v1.5.3.

Note that this makes use of @asset which auto-packs assets files along with the game and takes care of the paths for you.

So you would put background1.png and background2.png into the assets folder, then:

@asset scene1_background {
  file: "background_1.png"
}

@asset scene2_background {
  file: "background_2.png"
}

@scene scene_1 {
  background_image_id: #scene1_background
}

@scene scene_2 {
  background_image_id: #scene2_background
}

@game {
  on_scene_start: (game) => {
    const backgroundPath = game.current_scene.background_image.$dist_path;
    document.body.style.backgroundImage = `url('${backgroundPath')`;
  }
}
2 Likes