sandbags | 2025-01-23 12:32:59 UTC | #1
[Rez](https://rez-lang.com/) is an open-source game engine for making choice-based HTML games.
My target audience is me: someone frustrated building with Twine and comfortable in Javascript. I had been using Twine+Snowman but... Twine wasn't really doing very much for me.
Rez uses a mostly declarative syntax. Here is a player `@actor`:
```
@actor player {
name: "The adventurer"
description: "A bold adventurer"
class: :m
container_id: #player_inventory
fought_boss: false
initiative: 2
strength: 8
toughness: 6
fighting: 8
dodge: 0
wounds: 0
mana: 0
gold: 0
kills: 0
xp: 0
spells: []
class_name: ^v{this.class == "m" ? "Mage" : this.class == "r" ? "Rogue" : "Fighter"}
weapon: ^p{
const weapon_id = $player.container.getItemForSlot("weapon_slot");
return $(weapon_id);
}
on_start: (player) => {
player.container.setItemForSlot("weapon_slot", "dagger");
}
}
```
And here is a `@card` (roughly equivalent to a passage in Twine):
```
@card monster_dies {
bindings: {monster: `scene.monster}
content: ```
```
on_start: (card) => {
$player.xp += card.scene.monster.xp;
$("location").monster_id = "";
}
}
```
Inline Javascript functions are used for dynamic behaviours (e.g. the `on_start` event) and the built-in template expression language for dynamic content (e.g. `${monster.name}`).
Rez has a scene/card layout system that is simple but exceptionally powerful, with a template expression system including dynamic content. It uses [Bulma CSS](https://bulma.io/) for styling.
Rez has support for inventories & items, factions & relationships, NPC actors, plots, customised event handling, and more.
I'm not much of a one for self-promotion but I'm proud of Rez and what it can do at this point. It's come along way in the 2 years that I've been working on it on-and-off. It's gotten to the point I can make progress on my own games.
So I'm calling out v1.1 because, while there are still rough edges, it's ready enough and good enough that I think other authors could make use of it. I'd love to see what people can create with it.
Here are the major changes since the last public release (v0.9) in Nov 2022:
* Switched from `begin`/`end` to `{}` for block scopes
* Throwing out Handlebars.js in favour of the built-in Template Expression system.
* Throwing out Markdown in favour of writing plain HTML
* Bi-directional data binding from HTML form elements to object attributes
* Automatic creation of JS object properties from game element attributes
* Expressive dynamic attribute system
* Extensive improvements to documentation
* Events all driven from `` so simple but powerful!
* `@system`s now take part in processing events
* Fixed stack layout for running dialogue/events
* Embeds Tracery for grammars
* Added probability tables for procgen
* Working inventory system
Here's the [Hello World](http://rez-lang.com/games/hello/) of Rez ([source](http://rez-lang.com/games/hello/hello.rez)).
My test game is a [dungeon crawler](http://rez-lang.com/games/dungeon/) ([source](http://rez-lang.com/games/dungeon/dungeon.rez)). It's just a sliver of a game at the moment, a few rooms strung together, but I've been using it to incrementally test Rez's basic functionality.

I would be really pleased to find even a couple of authors who would try Rez. Even reading the [documentation](http://rez-lang.com/docs/rez.html) and giving me some feedback would be helpful.
-------------------------
BadParser | 2024-01-02 16:21:06 UTC | #2
I haven't looked though the documentation yet, so I can't comment on the language. I did just play through the demo a couple of times and think I've encountered a bug (in the demo, not the language). It looks like the mage character is given a limited number of spells, which I'm assuming the number in parentheses denotes. When my character uses a spell, however, that number does not change and it seems as though I have an unlimited amount. EDIT: Oops, not a bug. See below.
-------------------------
sandbags | 2024-01-02 16:17:19 UTC | #3
It's not clear but that's the number of mana points to cast the spell. It's not much of a game really, more of a test-bed as I figure out how to make things work. But it has a lot of aspects of a real game.
-------------------------
BadParser | 2024-01-02 16:18:38 UTC | #4
I just realized that when I looked through the source and can confirm that the behavior is as expected. Sorry about that. :sweat_smile:
-------------------------
sandbags | 2024-01-02 16:20:39 UTC | #5
No worries, thanks for taking the time to look and post.
-------------------------
jkj_yuio | 2024-01-02 18:45:57 UTC | #6
Congrats on your _Rez_ update.
-------------------------
sandbags | 2024-01-03 19:18:38 UTC | #7
I have updated the [demo game](http://rez-lang.com/games/dungeon/) to include a simple topic-based conversation system.

Topics are defined as a kind of `item` and held in a dedicated slot in an actors inventory. I use a card with a `$foreach()` template expression to render the topics and style them according to whether they have been read or not. Ease to add new available topics to an actor by adding more topic items to the inventory.

Both conversation and combat use a 'scene interlude' which allows the author to interrupt one scene (dungeon exploration) with another and then resume the previous scene. This is all pretty much automatic.
`Talk to the Imp`
Suspends the current scene and starts the scene with id `#conversation_scene`.
`Finish the conversation`
Ends the conversation scene and restores the previous scene and its UI.
If you need to send data back to the original scene it's easy enough to specify other params:
`Finish the conversation`
In this case the extra parameter is picked up by the clock system and uses it to advance the turn counter before resuming.
-------------------------
sandbags | 2024-01-10 01:00:18 UTC | #8
I've uploaded a 6m52 [introduction video](http://rez-lang.com/videos/01_hello_rez_game.mp4) which shows the very basics of Rez, presenting & linking between content.
-------------------------
sandbags | 2024-01-11 19:25:58 UTC | #9
I have released Rez v1.1.4 which includes a new `@timer` element. For example:
```
@timer actor_timer {
autostart: true
repeat: true
interval: 60000
event: :move_actors
}
```
You might write an event handler like:
```
@game {
on_move_actors: (game, params) => {
game.forAll("actor").forEach((actor) => {
actor.moveToNextLocation()
});
return {
render: true
};
}
}
```
Every minute this will send the `move_actors` event to the game. This could, for example, move actors along a path, and then cause the view to update. So actors would potentially appear, or disappear, from player view as the game runs.
Because it uses the standard Rez event handling the handler will be looked up in the current card, then scene, then finally in the game. So different scenes could potentially handle timers in a different way. You can have as many timers as you like.
I've also fixed a number of bugs involving inventories, items, & effects. When you add an item to an inventory with an owner, effects carried by the item get applied, automatically, to the owner (and removed again when the item leaves the inventory). For example:
```
@effect eff_plus_one_fighting {
on_apply: (effect, params) => {
const actor = $t(params["owner_id"], "actor");
actor.fighting += 1;
}
on_remove: (effect, params) => {
const actor = $t(params["owner_id"], "actor");
actor.fighting -= 1;
}
}
@item moon_sword {
type: :edged_weapon
magic: false
effects: [#eff_plus_one_fighting]
name: "moon silver longsword"
damage: 1d8+1
}
```
In this case the moon sword carries an effect which applies +1 to the fighting skill of whichever actor wields it. Different inventory slots can have different rules about whether effects are applied or not. So if the sword is placed in a "main hand" slot it gets applied but if in "backpack" slot it does not.
-------------------------
sandbags | 2024-01-22 17:53:42 UTC | #10
Because I needed it, Rez 1.1.6 now includes the ability to bind keys to trigger events using a new `@keybinding` directive.
So, for example, to trigger showing the character sheet when the player presses the `c` key (my use case):
```
@keybinding c :show_char_sheet
```
We can add arbitrary modifiers like `ctrl` or `shift` to the key we bind to, e.g.
```
@keybinding shift+ctrl+C :show_char_sheet
```
In this case `show_char_sheet` is a standard event that gets sent to the Rez event dispatcher. For this we'd probably implement it on the `@game` element, e.g.:
```
@game {
@keybinding c :show_char_sheet
on_show_char_sheet: () => {
const scene = $("character_sheet");
return scene.$running ? {resume: true} : {interlude: scene.id};
}
}
```
This handler looks to see if the scene `character_sheet` is already running. If it is not, it instructs Rez to do a 'scene interlude' and make the character sheet the current scene (i.e. interrupting scene A, to run scene B, before resuming scene A where it left off), otherwise to resume the previous scene.

-------------------------
sandbags | 2024-01-23 16:00:17 UTC | #11
One of the drivers behind making Rez was that, working with Twine, I found myself implementing a number of [core game concepts](http://rez-lang.com/docs/element_catalog.html) in Javascript (I ended up using the Snowman story format because the others just got in my way) but it was difficult to make things hang together consistently. There was no 'system'.
So, when I set out to build Rez it was to create a "game system" rather than a "story system". Thus Rez includes out of the box support for:
* Actors: there's actually little practical support yet although systems & behaviour trees provide a solid basis for "complex" NPC support.
* Inventory, Items, & Effects: an incredibly flexible system for creating anything you can conceive of as an "inventory" I've used it for memories, dialog topics, spells, as well as, … items. Effects allow the possession of items to change the actor who has them (based on different conditions).
* Assets: images, sounds, movies or whatever. Rez will manage them, how you refer to them in your game templates, and copying them into the distribution automatically,
* Factions & Relationships: basic but functional
* Plots: essentially the plot clock concept from forged in the dark as used in Blades in the Dark and Star Forged
* Scenes & cards: composable elements of game play & of content. Scenes group cards with a configurable ux
* Events & Systems: everything is driven by events that game objects respond to in order to modify the "world" the player inhabits.
Originally Rez also included maps & locations. Over time I have removed them. There is a tension between a map & location system and using scenes & cards for content. In the context of a players experience a card becomes 'their location' since it is the content they can see.
Now I use a custom object for 'current location' and a location attribute in objects that need them so you can figure out what is what. I'm opening to bringing back locations if they can be seen to be pulling their weight.
Something else I have striven for in creating Rez is a logical and consistent model & syntax. The format is quite predictable, most things are just elements with attributes and you can go a fair way with just this. From here the complexity is, I hope, incremental. That is certainly my aspiration.
-------------------------
jkj_yuio | 2024-01-23 17:17:16 UTC | #12
Fantastic! I'm comparing some of the things i do in _Strand_ to _Rez_. What's interesting is both these systems are new-generation. Most people here are using systems designed many years ago. But now it's the 21st century and time for what I call "New IF".
New IF is the throwing away design-wise of all the dross and baggage that was necessary before due to system constraints. And have a fresh sheet of paper to design with.
Obligatory picture:
http://stvle.s3.amazonaws.com/ifchart1.png
For example;
I also discovered "locations" are bogus. _Strand_ is a little different from Rez because it also supports a world model for the purposes of scope. This is because it still has a parser for people that like it. Although I've discovered, once sensible choices are on offer, most of those people don't want to type anymore. But hey.
So locations;
my model is generalised in terms of simple containership. A thing can be in a thing (or actually more than one!). So the "current location" is the thing the player is in. the "inventory" is the things that are in the player. and so on.
Containership gives rise to a simple scope model; all things recursively in the thing the player is in.
The runtime has no special knowledge of locations as a distinct type.
-------------------------
sandbags | 2024-01-23 20:06:24 UTC | #13
This is very interesting, thanks for sharing. I agree with your characterisation in terms of what new technology enables and that the limits are no longer how much memory or how fast the processor.
I guess I see your long-form/short-form rather as low-interactivity/high-interactivity. And what I mean by interactivity is the degree to which choices can have impact upon the game state. In a visual novel interactivity is about reading pace. To that extent I wouldn't lump it in with CYOA which has high-interactivity from that perspective. I'm working on things at the simulation end of the spectrum where small changes in state can have big repercussions later on.
Where I can read more about Strand and your design goals? And thank you, this is exactly the kind of conversation I was hoping to have!
-------------------------
jkj_yuio | 2024-01-23 22:54:13 UTC | #14
Your point about "pace" is what i call game "cadence". Agreed that most CYOA are high-cadence but some are still long-form.
Generally i think people want more interactivity per text-output. Ie the opposite to walls of text. I put visual novels as high-cadence because they often tend to be like comics with short form dialog between inputs.
Strand. yes will dig up.
-------------------------
jkj_yuio | 2024-01-23 22:56:23 UTC | #15
Here is an overview of the Design of _Strand_. Goals are a bit different from Rez;
# The Design of Strand
## Intro
The Strand IF system was invented to fill an emerging gap between classic IF and New IF. New IF is a modern and increasingly innovative form of flow based narrative, where the story takes the lead and is supported collectively by text input, art and choices and clicks - both on text and pictures.
Casting this into the nomenclature of classic IF, we can say that Strand supports parser-input, choice-input as well as point-and-click, but yet it is not limited to either.
## Design Goal
It was never a requirement that Strand was to be "better" or "superior" to existing IF systems. Indeed, Strand's original mission was to achieve the, so called, 90% rule:
> Do 90% of what games want **really well** and leave the remaining 10% to game design.
The motivation for this goal was the supposition that 9/10 features could be made simply and easy to author whilst the annoying and complicated 10% could be isolated from poisoning the main 90%.
In other words, the central goal of Strand was _ease of authoring_ and therefore its corollary, _rapid development_.
## Strategy
Ease of authoring was to be achieved by inventing a non-technical domain specific language (DSL). It was important that this language did not resemble programming with its inevitable collection of weird symbols and annoying syntax.
Because, whether you're a programmer or not, this is still a development impediment.
How nice it would be to "bosh-out" whole swathes of gameplay without programming; without variables, loops, if-then-else statements and all the other rubbish programming languages force on you.
**But how?**
Following the 9/10 idea, it was envisaged that the DSL would handle the 90% _without_ programming and the remaining 10% would be relegated to a companion, traditional programming language.
Because, for example, if someone wanted to have a chess game coded inside their game, it would clearly require a general programming system to be implemented. Such a thing would be deemed to fall into the 10% and thus be implemented outside of the DSL.
The strand IF system would therefore consist of a complementary blend of _two_ languages; a DSL, called _Strand_ and a system language called _KL_.
## Models
However, it transpires that the DSL is, in fact, quite capable of implementing well over the 90% originally envisaged. In practice, more like 99%.
The difference between a flow-based paradigm and a rules-based one is that combining flow blocks gives rise to _models_.
A _model_ is group of flow blocks working together to implement a specific behaviour. Rules can do the same thing, but a model is more cohesive and can also have context.
Models are implemented in the DSL and factored into a "core" package. It is then _very easy_ to build standard IF behaviour just as if these models were actually part of the runtime system.
Standard IF concepts such as doors, locations, gettables and so on are **not** part of the system runtime _at all_, but are in fact just DSL that can be further modified and extended by authors.
## Really?
Indeed. Here is the implementation of get-ability. No special core logic is needed.
```
GETTABLE@ THING
* be it
IT
* get it
DOGET
* drop it
DODROP
DOGET > put it in PLAYER
* ok
You get IT.
*
You can't get IT.
DODROP > put it in here
* ok
You drop IT.
*
You can't do that.
```
How about inventory. Nope this is just a bit of DSL. Also handling clothing.
```
INV
INV1 INV2
YOURSTUFF > what is in PLAYER
*
> be LAST
YOURCLOTHES > what is on me
*
> be LAST
INV1 YOURSTUFF
* them
You are carrying, LAST.
*
You are empty handed.
INV2 YOURCLOTHES
* them
You are wearing LAST.
```
The same is true of location, clothing, doors, containers and so on. There is no need for any of these traditional IF concepts to be implemented by the runtime system.
-------------------------
sandbags | 2024-01-24 00:27:01 UTC | #16
I confess I don't quite understand how your system works but it's interesting to see someone take a very different approach to the problem.
I freely confess that Rez is in the "collection of weird symbols and annoying syntax" camp. I guess I've been a programmer for over 40 years and Rez is designed to suit me, first and foremost, so it was probably inevitable.
That said Rez is largely declarative with a fairly regular syntax. I've tried to make it as clean as possible. Dynamic behaviours are implemented with Javascript so there's probably no way of avoiding that. But, even here, I've made the use of pluggable functions the norm. But there's no getting away from it.
My target audience is people like me. Someone with at least some programming experience, with an ambitious choice-based game project, and finding that Twine's constraints aren't liberating.
I'll be interested to see a game made with Strand. Maybe one day there will be one made with Rez! :D
-------------------------
sandbags | 2024-01-24 15:15:16 UTC | #17
I've published a new [website](https://rez-lang.com) for Rez.
The previous site was Wordpress. I hated it and, so, did nothing with it. The new site is built using the static site generator [Hugo](https://gohugo.io) and I'm slowly getting to grips with it.
At the very least it now hosts & links to my own documentation & the generated API documentation, although the latter doesn't work properly yet.
-------------------------
jkj_yuio | 2024-01-24 17:46:35 UTC | #18
Looks good.
What? Your dad had a PDP?
-------------------------
sandbags | 2024-01-24 18:52:28 UTC | #19
[quote="jkj yuio, post:18, topic:66437, username:jkj_yuio"]
What? Your dad had a PDP?
[/quote]
Indeed, from memory, a PDP-11. Playing games is about the only thing I remember about it :)
-------------------------
jkj_yuio | 2024-01-24 19:56:51 UTC | #20
[quote="Matt, post:16, topic:66437, username:sandbags"]
I’ll be interested to see a game made with Strand
[/quote]
[Picton Files: The Peter & Paul Case](https://strandgames.itch.io/pictonpp)
[Picton Files: Murder at the Manor](https://strandgames.itch.io/murdermanor)
[Solaris](https://strandgames.itch.io/solaris)
[The Tin Mug](https://strandgames.itch.io/tinmug)
-------------------------
manonamora | 2024-01-24 20:26:22 UTC | #21
> 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:
https://intfiction.org/t/twine-resource-masterlist-wiki/65903
I'm gonna plug: [***DOL-OS***](https://ifdb.org/viewgame?id=vw0p2h2c6myqwsxh) and [***The Thick Table Tavern***](https://ifdb.org/viewgame?id=u5fx8uysjk7yiw3w) 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?
-------------------------
sandbags | 2024-01-24 20:39:12 UTC | #22
[quote="M., post:21, topic:66437, username:manonamora"]
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.
[/quote]
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.
-------------------------
manonamora | 2024-01-24 20:43:47 UTC | #23
[quote="Matt, post:22, topic:66437, username:sandbags"]
even complex games
[/quote]
I am not sure by what you mean by complex game, then.
-------------------------
sandbags | 2024-01-24 20:50:52 UTC | #24
[quote="M., post:23, topic:66437, username:manonamora"]
I am not sure by what you mean by complex game, then.
[/quote]
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.
-------------------------
manonamora | 2024-01-24 20:59:49 UTC | #25
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.
-------------------------
sandbags | 2024-01-24 21:00:04 UTC | #26
[quote="M., post:25, topic:66437, full:true, username:manonamora"]
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.
[/quote]
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.
-------------------------
Hituro | 2024-01-25 17:58:25 UTC | #27
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.
-------------------------
sandbags | 2024-01-25 21:31:28 UTC | #28
[quote="David Donachie, post:27, topic:66437, full:true, username:Hituro"]
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.
[/quote]
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.
-------------------------
HAL9000 | 2024-01-25 19:49:04 UTC | #29
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?
-------------------------
sandbags | 2024-01-25 21:30:06 UTC | #30
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?"
-------------------------
sandbags | 2024-01-31 20:38:39 UTC | #31
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](https://handlebarsjs.com). 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) {%
You are wounded, and cannot move very quickly.
%}, {%
You are fit and healthy and can move normally.
%}
```
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:
```
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.
-------------------------
Greyelf | 2024-02-01 20:43:54 UTC | #32
[quote="Matt, post:31, topic:66437, username:sandbags"]
The second expression is the “conditional”:
```
$if(cond) {% true output %}, {% false output %}
```
[/quote]
Do you intend to support an equivalent of `$elseif` ?
-------------------------
HAL9000 | 2024-02-01 21:43:37 UTC | #33
I wonder how that might be formatted, if it exists or is in the works...
```
$if(player.pristine) {%
OKAY
%}, (player.wounded) {%
NOT OKAY
%}, {%
NURSE!
%}
```
-------------------------
sandbags | 2024-02-01 22:18:35 UTC | #34
The need hasn't arisen for me personally, although I'm not opposed to adding it, depending on finding a good syntax.
-------------------------
sandbags | 2024-02-01 22:19:14 UTC | #35
This looks plausible although I will have to give it a little thought. Thanks for the suggestion!
-------------------------
BadSpaceKing | 2024-02-01 23:04:24 UTC | #36
I'm liking what I'm seeing, it gives me ideas. I'll have a tinker with it!
-------------------------
sandbags | 2024-02-02 09:49:29 UTC | #37
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.
-------------------------
HAL9000 | 2024-02-02 21:13:23 UTC | #38
[quote="Matt, post:37, topic:66437, username:sandbags"]
So `elseif` is possible with the existing syntax...
[/quote]
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! :-)
-------------------------
sandbags | 2024-02-03 12:01:43 UTC | #39
[quote="HAL9000, post:38, topic:66437"]
Do you make a case/switch statement structure with breaks too? Like, how far down the rabbit hole do you need to go?
[/quote]
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.
-------------------------
sandbags | 2024-02-03 12:02:47 UTC | #40
[quote="HAL9000, post:38, topic:66437"]
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.
[/quote]
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.
-------------------------
HAL9000 | 2024-02-03 18:37:49 UTC | #41
You might have heard of this guy; he's one of my favourite minds. ***[Dieter Rams](https://en.wikipedia.org/wiki/Dieter_Rams)***.
[details="10 Design Principles ← ( click to expand )"]
1. **is innovative** – The possibilities for progression are not, by any means, exhausted. Technological development is always offering new opportunities for original designs. But imaginative design always develops in tandem with improving technology, and can never be an end in itself.
2. **makes a product useful** – A product is bought to be used. It has to satisfy not only functional, but also psychological and aesthetic criteria. Good design emphasizes the usefulness of a product whilst disregarding anything that could detract from it.
3. **is aesthetic** – The aesthetic quality of a product is integral to its usefulness because products are used every day and have an effect on people and their well-being. Only well-executed objects can be beautiful.
4. **makes a product understandable** – It clarifies the product’s structure. Better still, it can make the product clearly express its function by making use of the user's intuition. At best, it is self-explanatory.
5. **is unobtrusive** – Products fulfilling a purpose are like tools. They are neither decorative objects nor works of art. Their design should therefore be both neutral and restrained, to leave room for the user's self-expression.
6. **is honest** – It does not make a product appear more innovative, powerful or valuable than it really is. It does not attempt to manipulate the consumer with promises that cannot be kept.
7. **is long-lasting** – It avoids being fashionable and therefore never appears antiquated. Unlike fashionable design, it lasts many years – even in today's throwaway society.
8. **is thorough down to the last detail** – Nothing must be arbitrary or left to chance. Care and accuracy in the design process show respect towards the consumer.
9. **is environmentally friendly** – Design makes an important contribution to the preservation of the environment. It conserves resources and minimizes physical and visual pollution throughout the lifecycle of the product.
10. **is as little design as possible** – Less, but better. Simple as possible but not simpler. Good design elevates the essential functions of a product.
[/details]
You can apply every aspect of his design principles to code bases too. Even ***#9*** can apply in that you're contributing to the health of the IF landscape and reducing the effort required for authors to construct relational game systems. The aesthetics details can apply to your syntax, etc. :-)
***Edit:** Keep in mind that these design principles (in respect to Rez) are for the programmer, not the player of the IF product the programmer is building. Rez is like a hammer. So when things like "leave room for the user’s self-expression" are mentioned, it means leave room for the programmer's self-expression. Anyway, I didn't realize how much Apple adhered to Dieter Rams' principles. They worship him, like a god!*
-------------------------
pieartsy | 2024-02-03 19:43:29 UTC | #42
I read through the documentation and got utterly lost with the bindings and rendering section, which is a shame because it seems important. I am a developer (albeit a jr. one) who's worked with javascript, typescript, etc. It seems very in the weeds and I think boiling it down to how bindings can make the story people want would be good.
In general the documentation seems very thorough at explaining what the language does and how from a...well, programmer's documenting mindset. But an IF author mindset is a little different, more focused on "what stories can I tell with this?"
In that respect, the reason I eagerly opened the documentation was because you mentioned NPC behavioral tree support and I didn't see it anywhere.
-------------------------
HAL9000 | 2024-02-03 20:53:12 UTC | #43
There are examples of bindings in Matt's sample [dungeon crawler ](http://rez-lang.com/games/dungeon/) ( [source code](http://rez-lang.com/games/dungeon/dungeon.rez) ). Tinker with the game and check out the source code; it may help.
-------------------------
sandbags | 2024-02-03 21:01:30 UTC | #44
Hi Aster.
That's valuable feedback, thank you.
Bindings are a part of the template system that I am not entirely happy with yet, although conceptually they are relatively simple. A binding is a "starting point" for referring to a value.
So for example you might have an `@actor player` element and want to get it's name. So you'd want to bind a variable to the element to do so.
```
bindings: {player: #player}
```
so that in a template you could refer to:
```
${player.name}
```
Where things get more complicated is when you want to bind to something that isn't a plain element. Or, for example, a dynamically chosen element. That's something you might do with a function binding, for example:
```
bindings: {monster: () => {return $("room").monster;}
```
You can bind to pretty much anything this way. There are other types of binding that are a bit more specific and I won't go into here. But that's the gist of it, a starting point for referring to a value.
Your observation about the documentation being more of a programmers mindset than an IF authors mindset is, I guess, kind of fair. I've been trying to document how it works because I figure Rez will probably appeal first to people with more programming experience. But that's not where I want to leave it.
The cookbook documentation is meant, over time, to be more about how to use Rez to achieve different ends, rather than the main docs which are describing capabilities.
The behaviour tree support is a little nascent. You can define behaviour trees as attributes, you can define query/action tasks (as well as Rez defining a set of common tasks), and you can run them. I'm still working out how to integrate this more completely.
It looks like the documentation for the behaviour tree attributes got lost when I rewrote the docs. I'll have to add that back in.
-------------------------
sandbags | 2024-02-03 22:14:15 UTC | #45
It's worth mentioning perhaps that the reason for bindings existing is that I need a way to make things in-scope for the rendering process.
If you want to know more take a look at [rez_view.js](https://github.com/mmower/rez/blob/main/assets/templates/runtime/rez_view.js) where the renderer is implemented.
I am not going to claim this is the easiest code to grok, it's a little bit tricky in parts. But I'm happy to answer questions.
What it boils down to is templates get compiled to a function that takes a set of bindings as its input and returns a string containing rendered HTML.
Those functions are generated in [template_compiler.ex](https://github.com/mmower/rez/blob/main/lib/compiler/template_compiler.ex) which is Elixir code and, again, probably not the easiest to follow, mainly because I'm code generating JS functions.
I don't like delving in there too much as it's messy and largely untested. But it works, mostly… :)
[quote="Aster F, post:42, topic:66437, username:pieartsy"]
But an IF author mindset is a little different, more focused on “what stories can I tell with this?”
[/quote]
It's probably also worth mentioning a few things that may not be obvious.
In the first place where Twine comes from the perspective of "narrative first", Rez comes from the perspective of "world model first" and narrative is a component. In Twine the passage has primacy and other aspects fit around that. In Rez the model comes first.
Why might this be? Well the kind of games I am primarily interested in making are what I would call a hybrid of simulation/rpg/emerging narrative games.
An author who mostly wants to tell a story (with some dynamic elements) might fight Twine a better tool as it mostly gets out of the way of that purpose. On the other hand someone who wants to build games like I describe could find Rez a better fit because it has a world model and a bunch of systems that can do a bunch of the heavy lifting.
To be clear: you can do either in both systems. Rez could serve as a perfectly functional replacement for Twine. And people do build complex games in Twine. But I'd argue that both have a different sweet spot.
-------------------------
sandbags | 2024-02-08 19:11:31 UTC | #46
The kind of games I want to make involve procedural generation.
While I am still developing this aspect Rez has already has some useful support for this.
In the first place most Rez elements support `copyWithAutoId()` which is useful with template elements that also use the various forms of dynamic attribute. This makes it relatively easy to create many customised variants of a set of base templates.
Next we have the `@list` element which is, more or less, exactly what you might guess:
```
@list female_names {
values: [
"Arata"
"Asa"
"Fujioka"
"Fujiwara"
"Fukaya"
"Funai"
"Furuse"
...
]
}
@list names begin
values: [
"Takahashi"
"Konishi"
"Yashiro"
"Nakazono"
"Seki"
"Ishikawa"
...
]
}
```
At run-time these become instances of `RezList` which is a wrapper around an array providing an API with functions I've found useful.
The most obvious is `randomElement()` which is just inherited from `Array` nothing special there. But there are others…
The next is `nextForCycle(cycle_name)`. This treats the values as a cycle and will go around and around them continuously in order. At which point a new walk is started.
Then there is `randomWalk(walk_name)`. This is a bit like `randomElement()` only it guarantees that you won't receive the same value twice until all values have been exhausted.
Lastly there is `randomRemaining(bag_name)` which treats the list like a bag of objects. When you take one out, it can't be taken again. The difference between this and the walk is that you can empty the bag.
Note that, in the case of `nextForCycle()`, `randomWalk()`, and `randomRemaining()` you can run multiple simultaneous instances against the same list. A small point but useful in some circumstances.
I'll give a passing nod to two other capabilities that can be useful here, those are the Tracery grammar and probability table attributes. The former allows you to embed and run [Tracery grammars](https://tracery.io/) and Rez comes with tracery.js included. The second allows you to do easy probability-based result generation:
```
eye_color: |:brown 48 :blue 29 :green 14 :grey 9|
loot_quality: |:poor 20 :okay 10 :great 5 :amazing 1|
meet_on_the_road: |#ranger 15 #wizard 10 #traveller 45 #evil 30|
```
The relative probability of each outcome is calculated and then sampled to return a result as the attribute value.
-------------------------
jkj_yuio | 2024-02-10 15:11:17 UTC | #47
Hi,
Just for comparison, i have similar list orderings in _Strand_.
This would generate a fruit at random:
```
ALIST
* apple
* banana
* cherry
* durian
* elderberry
* fig
* grape
```
You can add "indicators" on terms to alter the list. Random is the same as `~`. Then there is shuffle `&`
This will randomly choose without repeats. Same as your `nextForCycle`.
```
ALIST&
* apple
* banana
* cherry
* durian
* elderberry
* fig
* grape
```
A few extras i have are "Nonrandom", `#` and sequence `<` and what to do thereafter.
so `LIST<` will choose them in sequence, `LIST#` is a "non-random" list (explained later).
What do to thereafter indicates how to deal with the list after it has played out. This is done with a follow-on indicator, eg
`&&` will shuffle, then re-shuffle after (and forever after that).
```
ALIST&&
* apple
* banana
* cherry
* durian
* elderberry
* fig
* grape
```
And you can mix them, so `ALIST&~` means shuffle, then after all played, it will randomise. And `ALIST&<` will shuffle, then play out in order.
The one you don't have is `non-random`. If find this _incredibly_ useful for games and hardly ever use pure random `~` in my game code. Trouble is pure random tends to repeat too much. For example, people will get three `bananas` from the original random list _annoying a lot of the time_.
So non-random is a randomisation with starvation restriction. For example, you might spend _ages_ before you get `grape` just by bad luck. Non-random avoids this.
I could post some code, but it might be easier to explain;
Consider a 6 sided die (for discussion). You maintain an array of 6 slots (1 through 6), each containing the number of throws since that number has come up. So slot 1 contains how many throws since the last 1 was emitted. and so on.
You have a max history value. say 8, which is used to prevent starvation.
pseudo-code:
initialise `stats[]` to zero.
To throw a non-random of 6:
1. find j, where stats[j] is the most.
2. if stats[j] + 1 >= max-history, then choice=j
3. else choice = random value 1-6.
4. increment stats[i], 1 through 6.
5. set stats[choice] = 0
6. emit choice
I find a good choice for games for max-history is the non-random size is: n+(n+2)/3. So 6 gives 8 and 10 gives 14 etc. Also "warm-up" the stats array in your "init" for a bit to populate them.
-------------------------
HAL9000 | 2024-02-10 17:50:07 UTC | #48
[quote="jkj yuio, post:47, topic:66437, username:jkj_yuio"]
Also “warm-up” the stats array in your “init” for a bit to populate them.
[/quote]
Very clever. :-)
-------------------------
Greyelf | 2024-02-10 19:45:03 UTC | #49
[quote="jkj yuio, post:47, topic:66437, username:jkj_yuio"]
`&&` will shuffle, then re-shuffle after (and forever after that).
[/quote]
I'm curious, what reason is there to re-shuffle a list that is already in a random order?
How does doing that make that list "more random" than the outcome of the first shuffle?
-------------------------
Draconis | 2024-02-10 20:39:38 UTC | #50
If I understand right, this means it re-shuffles whenever it runs out.
So if you had four playing cards, "shuffle then shuffle" would give you 1, 4, 2, 3, 2, 4, 3, 1, 2, 3, 1, 4…
While "shuffle then sequence" would give you 1, 4, 2, 3, 1, 4, 2, 3, 1, 4, 2, 3…
-------------------------
jkj_yuio | 2024-02-10 21:14:58 UTC | #51
Yes, it reshuffles after it's played out.
eg
```
GENFRUITS
GENFRUIT4 GENFRUIT4
GENFRUIT4 GENFRUIT4
GENFRUIT4 GENFRUIT4
GENFRUIT4
\
ALIST ALIST ALIST ALIST
ALIST&&
* apple
* banana
* cherry
* durian
```
emitted,
```
cherry apple durian banana
banana cherry durian apple
apple banana cherry durian
durian banana apple cherry
durian cherry banana apple
apple durian cherry banana
```
-------------------------
sandbags | 2024-02-11 19:57:45 UTC | #52
[quote="jkj yuio, post:47, topic:66437, username:jkj_yuio"]
pseudo-code:
initialise `stats[]` to zero.
To throw a non-random of 6:
1. find j, where stats[j] is the most.
2. if stats[j] + 1 >= max-history, then choice=j
3. else choice = random value 1-6.
4. increment stats[i], 1 through 6.
5. set stats[choice] = 0
6. emit choice
[/quote]
Can you check my homework here?
```
function next_non_random(list) {
let stats = list.stats ?? Array.from({length: list.length}, () => 0);
const len = stats.length;
let max = Math.floor(len + (len+2)/3);
let choice = stats.findIndex((element) => element+1 >= max);
if(choice == -1) {
choice = Math.floor(Math.random() * stats.length);
}
stats = stats.map((element) => element+1);
stats[choice] = 0;
list.stats = stats;
return list[choice]
}
```
It's an interesting idea, thanks for sharing.
-------------------------
sandbags | 2024-02-12 09:53:31 UTC | #53
I also think it would be helpful to have a different name to 'non-random' because this is certainly a random process and `non-random` doesn't express the protection from starvation that is at the heart of the method:
Something like `randomWithoutStarvation` suggests itself although it's a bit of a mouthful.
I'm also curious how you ended up at `n+(n+2)/3`?
-------------------------
jkj_yuio | 2024-02-12 15:21:03 UTC | #54
From your code, it looks like the right algorithm.
Some considerations:
You will need to prepare the stats list before using it. So you can't just call this method cold. What I do is initialise the stats list to zeros, then call the non-random method for, say 100, times to initialise it. After that, I start using the output.
Secondly, if you're going to simultaneously have multiple list lengths, you need to manage those lists. Say you need a non-random-6 and a non-random-10. You need separate lists for this.
Some background:
This (and other ideas) came out of some thinking about that i call "non-random" numbers. Let's say you have a random sequence. The notion is to limit the things that are sufficiently improbable. Say I'm flipping a coin. Then I flip "heads" 6 times in a row. This is improbable - but it happens all the time in real life.
Now the notion of "improbable" is vague because _any_ given sequence is just as likely as any other (for the same length). 6 heads is just as likely as what appears to be a "random" sequence of 6 heads and tails.
I also realised there is no definition of what is "random". Some people say it's something that can't be predicted or having no pattern, but many unpredictable things are not random - by most people's understanding.
Example sequence: 1,1,1,1,1,1
Does this have a pattern? Does it have a pattern when i tell you the next number in the sequence is 7. After that, the sequence goes; 1,1,1,1,2.
It doesn't look very random, but it has no pattern and is not predictable.
This leads to the idea of washing patterns out of sequences to make then "more random" (whatever that means). But it actually has the opposite effect and this is what I call a non-random sequence - A random sequence with patterns washed out. By "random" here, i mean the output of another supposedly random sequence generator.
So;
First I tried removing repeats, then runs of numbers then absent numbers (ie starvation). These methods became complicated and I eventually discovered removing starvation does as good job as all the others put together - because starvation also breaks repeats and runs.
Finally; n+(n+2)/3 is chosen because i found adding about 50% extra works quite well. So for 6 dice set list length to 9.
I wanted to tweak this back a bit for games because you still get a lot of repeats with 50% - although it does look quite random.
For use in games, i wanted to constrain a bit more. So increase your list length to become more "random" and decrease it to become more "non-random".
If you set list length to n (eg 6 for a 6-dice), you get a shuffle sequence that repeats. I actually use this idea in my shuffle generator rather than having two different bits of code.
-------------------------
sandbags | 2024-02-12 18:34:34 UTC | #55
[quote="jkj yuio, post:54, topic:66437, username:jkj_yuio"]
You will need to prepare the stats list before using it. So you can’t just call this method cold. What I do is initialise the stats list to zeros, then call the non-random method for, say 100, times to initialise it. After that, I start using the output.
[/quote]
I wonder if we, like `max` can determine the number of rounds of warming based on `length`. Presumably we'd need more rounds for longer lists. I have some lists with 50+ items in them
[quote="jkj yuio, post:54, topic:66437, username:jkj_yuio"]
This (and other ideas) came out of some thinking about that i call “non-random” numbers. Let’s say you have a random sequence. The notion is to limit the things that are sufficiently improbable. Say I’m flipping a coin. Then I flip “heads” 6 times in a row. This is improbable - but it happens all the time in real life.
[/quote]
Interesting. On a different, but similar, tack, the Rez stdlib adds `Math.clrand_int` that takes advantage of the central limit theory to return normally distributed random numbers. I often use this in preference to uniform random.
I think there's lots of scope for interesting takes on randomness, for example Perlin noise. Which (IIRC) has applications in city/landscape generation.
-------------------------
jkj_yuio | 2024-02-14 00:47:17 UTC | #56
You can just run `n` warmup rounds where `n` is your list length.
-------------------------
sandbags | 2024-02-19 08:11:14 UTC | #57
Okay so I have implemented
```
$if
(cond1) -> {% if_body1 %}
(cond2) -> {% else_if body2 %}
() -> {% else_body %}
```
which compiles to
```
if(cond1) {
…
} else if(cond2) {
…
} else if(true) {
…
}
```
An example could be:
```
$if(player.wounded)->{% your wounds are slowing you down. %}
$if(player.wounds>0)->{%
You are
$if
(player.wounds>5)->{% seriously %}
(player.wounds>2)->{% badly %}
()-> {% mildly %}
wounded.
%}
```
Although this latter example is a touch contrived and could be cleaner using a filter, esp if it were going to be used in several places:
```
$if(player.wounds>0)->{% You are ${player.wounds | wound_level} wounded. %}
@filter wound_level {
name: "wound_level"
impl: function(l) {return l > 5 ? "seriously" : l > 2 ? "badly" : l > 0 ? "mildly" : "not";}
}
```
If you omit the `()` case you get `""` returned by default. I dropped the `,` between clauses as, in this syntax, each `condition-body` is discrete.
I'm not 100% sold, but this will be the `$if` syntax in v1.1.7 and I'll play with it myself and see how I feel in practice.
-------------------------
sandbags | 2024-02-20 17:12:26 UTC | #58
I released v1.1.7 which includes the new `$if` syntax, an option to warn the user before leaving or reloading the page, and `@list`/`RezList` gains a new `randomWithoutStarvation()` method based on @jkj_yuio approach earlier in the thread.
-------------------------
Hituro | 2024-02-20 22:56:54 UTC | #59
[quote="Matt, post:53, topic:66437, username:sandbags"]
I also think it would be helpful to have a different name to ‘non-random’ because this is certainly a random process and `non-random` doesn’t express the protection from starvation that is at the heart of the method:
[/quote]
I believe this is often called a "random with pity timer" or "perceptual random" because people expect a random output to actually give all results evenly, which isn't *right* but feels right
-------------------------
sandbags | 2024-02-24 15:03:32 UTC | #60
I've just pushed v1.2 which mainly involves a complete overhaul of the behaviour tree functionality and [documentation](https://rez-lang.com/docs/rez.html#_behaviour_trees). For those not familiar, a behaviour tree is an AI technique popular in video games as a flexible, scalable approach to creating complex NPC behaviours.
The BT code had gotten out of step with some of the new Rez internals and was broken. It's working again.
I've also made a minor, but meaningful, change to the syntax. In the past the children of a behaviour had to be wrapped in their own list. Now children are inserted directly into their parent behaviour, so that:
```
[$sequence [
[child1 ...]
[child2 ...]
[child3 ...]]]
```
is now written:
```
[$sequence
[child1 ...]
[child2 ...]
[child3 ...]]
```
The [core behaviours](https://rez-lang.com/docs/behaviour_catalog.html) have been renamed so `SELECT` becomes `$select`, `SEQUENCE` becomes `$sequence` and so on. This is part of a general trend towards using the `$` prefix for "built in stuff, caveat auctor!"
Somehow the behaviour trees section got lost when I moved the docs from Markdown to ASCIIDoc so they have been [recreated](https://rez-lang.com/docs/rez.html#_behaviour_trees) and expanded on.
This is a sufficiently complex topic that I am sure the guide I have written can be greatly improved and certainly my examples can be. Grateful for feedback here.
Tagging @pieartsy as you were particularly interested in the behaviour tree support.
-------------------------
sandbags | 2024-02-24 15:02:26 UTC | #61
One of the things that I think has so much potential about behaviour trees is the ability for authors to create NPC behaviours that they be unlikely to code for themselves. Especially with a little editor support.
Writing a complex behaviour in Javascript quickly gets unwieldy, even for an experienced developer. The same behaviour as a behaviour tree is easier to reason about and adjust. It's composed from smaller behavioural units that are easier to write.
With an, outline based, node editor that shows composite structure you could go a long way. I've thought about exporting behaviour tree attributes as OPML to create some kind of bi-directional editing support but perhaps a custom interface wouldn't be too much work.
-------------------------
HAL9000 | 2024-02-24 18:06:06 UTC | #62
Kind of unrelated, but what does Rez stand for? Why is the IF engine named Rez?
-------------------------
sandbags | 2024-02-24 19:53:54 UTC | #63
It doesn't stand for anything.
It was originally called Twist (since it started as a variation on the Twine concept) but I came to my senses.
Then for quite a while it was called Grue. Only Robin released Gruescript before I released anything.
I flailed around trying to come up with another name but pretty much everything was used in some way.
I think I'd watched Tron quite recently and the name Rez suggested itself and was fairly short.
-------------------------
sandbags | 2024-02-25 20:47:52 UTC | #64
I've just committed v1.2.3. The main change is to the @rel (relationship) element. The old syntax forced a specific way to express relationships with an affinity constrained to the range -5.0 … +5.0:
```
@rel #player #kaspar_gutman -2.5 #{:suspicious}
```
and I've changed my mind. Now `@rel` works more or less like any other element exception that, as before, you don't specify an id, rather source & target ids (relationships are unidirectional).
```
@rel #player #kaspar_gutman {
affinity: -1.0
}
```
Of course now you can call `affinity` whatever you like and use any scale that makes sense to you (e.g. -1.0 … +1.0, or even 0 … 100).
```
@rel #player #miss_wonderly {
love: 75
trust: 20
}
```
If you want tags just use a `tags:` attribute as normal:
```
@rel #joel_cairo #kaspar_gutman {
tags: #{:suspicious}
}
```
It's also worth mentioning that relationships can be specified from any element to any element so:
```
@actor player {…}
@actor joel_cairo {…}
@faction gutman {…}
@item falcon {…}
@rel #player #gutman {…}
@rel #gutman #falcon {…}
@rel #joel_cairo #player {…}
```
Are all legal. Express any kind of relationship, between any kind of element, that makes sense in your game.
In game, you can obtain a relationship using the API:
```
$game.getRelationship(source_id, target_id)
```
or, if you have a reference to an element:
```
element.getRelationshipWith(target_id)
```
If you have a `RezRelationship` for the relationship `A->B` you can obtain the inverse relationship `B->A` using:
```
rel.inverse
```
Although I've not tested it yet, I am pretty sure that you can add a `container:` attribute to a relationship and specify an `@inventory` that could, for example, contain `@item` elements defined as rumours, facts, or memories. Here I am thinking about the question I was pondering earlier about [enabling players to ask NPC's questions about the world](https://intfiction.org/t/how-can-we-enable-players-to-ask-npcs-questions-about-their-world/67016/26).
-------------------------
sandbags | 2024-02-27 19:52:05 UTC | #65
I've managed to create a nice dev workflow, figure its worth sharing.
I run the following command in my game project folder:
```
fswatch -o src | xargs -n1 ./build
```
On macOS `fswatch` is a utility for monitoring changes to the file system. Linux/Windows have `inotify`/`inotify-win` instead.
My `build` script is:
```
#!/bin/bash
clear
echo "Files change. Rebuilding."
rez compile src/rotn.rez && osascript -e 'tell application "Google Chrome" to reload active tab of front window'
```
Whenever I save any of the source files I am working on this will recompile the game and reload it in the Chrome window.
If I need to test a specific scene or card I just set `initial_scene_id:` or `initial_card_id:` appropriately.
-------------------------
sandbags | 2024-03-01 15:00:14 UTC | #66
As a concrete example of how I am using behaviour trees:
In my city I have stores which may, or may not, be open. Here are a couple of simplified examples (I tend to use a relevant prefix such as `v_` or `s_` to make it clear when I am talking about, e.g. a vendor or scene):
```
@vendor v_apothecary {
name: "The Apothecary"
description: "Potions, herbs, poultices and such like"
scene_id: #s_use_vendor
available_from: [08 00]
available_until: [17 00]
player_knows: true
}
@vendor v_inn {
name: "The Inn"
description: "A comfortable bed and a meal."
scene_id: #s_use_vendor
available_from: [06 30]
available_until: [23 00]
player_knows: true
}
@vendor v_black_market {
name: "Black market peddlar"
description: "Dangerous if caught by the city guard but some things you cannot acquire elsewhere."
scene_id: #s_use_vendor
available_from: [22 00]
available_until: [03 00]
player_knows: false
}
```
Now I want to display a list of vendors that the player knows about and a link to use them, assuming they are open. Time is represented like this:
```
@object clock {
time: [08 00]
day: 1
week: 1
cycle: 1
}
```
As you can see I am using lists to represent times, e.g. `[08 00]` is 8:00am.
Rez provides a handy object called `RezDecision` which exposes an API `hide()`, `no(reasons)`, `yes()`. We can pass a `RezDecision` to any code that should decide something for us.
* `hide()` means this option shouldn't be seen by the player, e.g. the player doesn't yet know about this vendor
* `no(reasons)` means this option should be shown to the player but disabled with an optional reason why they can't select it, e.g. it's too early
* `yes()` means this option should be shown and available to the player
So, we could write something like this:
```
@object t_vendor {
$template: true
is_available: function(decision) {
decision.default_yes();
if(!this.player_knowns) {
decision.hide();
} else {
const clock = $("clock");
if(clock.before(this.available_from)) {
decision.no("Not open yet");
} else if(clock.after(this.available_until)) {
decision.no("Closed for the day");
}
}
}
}
@vendor v_apothecary {
...
}
```
And now when we go through the list of vendors and generate the appropriate markup depending on whether the player can see vendor, and visit them, or not.
```
$if (decision.result) -> {%
${vendor.name}
%}
(decision.hide) -> {%%}
() -> {%
${vendor.name}
%}
```
Not that the `is_available` function above is particularly difficult to write or understand, but the code itself slightly obscures what is going on. And if we were to add a few more conditions this could become more complex and harder to write & maintain. Using behaviour trees (which are really decision trees) it could look like:
```
@object t_vendor {
is_available: ^[$select
[$sequence [$invert [player_knows_vendor]] [decision_hide]]
[$sequence [vendor_before_open] [decision_no msg="Not open yet"]]
[$sequence [vendor_after_close] [decision_no msg="Closed for the day"]]
[decision_yes]
]
}
```
Once you understand some basic behaviours such as `$select`, `$sequence`, and `$invert` the structure of the decision becomes much clearer.
It means implementing behaviours such as `player_knows_vendor`, `vendor_before_open`, `decision_hide` and so on. But these can be quite compact:
```
@behaviour vendor_before_open {
execute: (behaviour, wmem) => {
return {
success: $clock.before(wmem["vendor"].available_from),
wmem: wmem
};
}
}
@behviour decision_no {
execute: (behaviour, wmem) => {
const decision = wmem["decision"];
decision.no(behaviour.option("msg"));
return {
success: true,
wmem: wmem
}
}
}
```
Once you have defined these behaviours they are easily composed and their implementation doesn't get in the way. The behaviour tree more clearly (at least in my view) exposes the logic.
I have this view that an author with less technical expertise could assemble these behaviours to create the meaning and rely on a developer, more adept with Javascript, to build the implementation of the various behaviours for them. Madness? I don't know.
-------------------------
HAL9000 | 2024-03-01 15:20:22 UTC | #67
[quote="Matt, post:66, topic:66437, username:sandbags"]
I have this view that an author with less technical expertise could assemble these behaviours to create the meaning and rely on a developer, more adept with Javascript, to build the implementation of the various behaviours for them. ***Madness? I don’t know.***
[/quote]
Welcome to the freak show. :crazy_face:
All you can do is make it the best it can be and true to your vision. In my research, I've found an appreciation for the divide between narrative games and simulation games. I think the tools in Rez lean towards a simulated game world to that of what parser games typically offer, yet Rez is more likely to be used in a choice-based interface design. I think Rez is filling a void in the IF authoring community. Even if Rez remains a niche solution, in a niche gaming genre, I think you're on to something worthwhile. :-)
-------------------------
sandbags | 2024-03-01 17:08:03 UTC | #68
Thanks :)
You're on the money that the games I am building are IF/procgen/simulations with emergent narrative, rather than heavily story-based games.
I think Rez is well suited to be a `Twine++` type of tool: all you need to master is `@scene` and `@card`, and HTML, absolute no scripting required.
Though a Rez source file does look different and perhaps a little daunting to someone without programming experience?
I don't have any frame of reference to work out if Rez is easy to learn or not. Perhaps alternatives like [ifinity](https://intfiction.org/t/introducing-iffinity-a-new-minimal-if-engine-with-iffinite-possibilities/65783/45) seem more familiar? Or appear to have a shallower on-ramp?
I do know that it offers a lot of power for a relatively low investment of effort compared to building the same systems for yourself.
But I will keep plugging away and, of course, producing an actual game wouldn't hurt :slight_smile:
-------------------------
HAL9000 | 2024-03-01 15:44:35 UTC | #69
[quote="Matt, post:68, topic:66437, username:sandbags"]
But I will keep plugging away and, of course, producing an actual game wouldn’t hurt :slight_smile:
[/quote]
This is the most important thing a new IF framework can do. *"Show, don't tell"* is the mantra for good movie directors/writers... why not for game engine programmers too? ;-)
-------------------------
sandbags | 2024-03-03 14:18:13 UTC | #70
I've published the [v1.2.5 release](https://github.com/mmower/rez/releases/tag/v1.2.5) including binaries for macOS, linux, and Windows.
The main change is an overhaul and fixes to behaviour trees that have come from my now [using them](https://intfiction.org/t/rez-1-2-0-open-source-engine-for-choice-based-html-games/66437/66?u=sandbags) to convert some scripted behaviors in my own game.
In the future I am going to look for a way to allow authors to 'compose' behaviours more easily.
-------------------------
sandbags | 2024-03-04 16:57:10 UTC | #71
[quote="Matt, post:70, topic:66437, username:sandbags"]
In the future I am going to look for a way to allow authors to ‘compose’ behaviours more easily.
[/quote]
I think, I hope, I have come up with an elegant solution to this problem.
I'm writing behaviour trees which have common elements. For example "Does the player have enough money for the cost of this item?" ends up being a behaviour that most items share:
```
^[$sequence [query elem=#player if={player.cash < obj.cost}] [decide_no msg="You cannot afford it."]]
```
But we might have another condition: Does the vendor like you enough to sell this to you?
```
^[$sequence [query mem=:vendor if={vendor.relationshipWith("player").affinit < -0.1}] [decide_no msg="They are not willing to sell this to you."]]
```
Or, perhaps, the item is of the wrong class:
```
^[$sequence [query elem=#player if={player.class != obj.class}] [decide_hide]]
```
Having to write these out each time gets to be a pain and error prone. So, what I've done is introduce the `@behaviour_template` directive. Example:
```
@behaviour_template player_cannot_afford ^[$sequence [query elem=#player if={player.cash < obj.cost}] [decide_no msg="You cannot afford it."]]
```
And this is now used in a behaviour tree:
```
@item long_sword {
is_available: ^[$select &player_cannot_afford
&player_is_wrong_class
[...]
[decide_yes]]
}
```
Templates can also contain other templates allowing complex behaviours to be built from less complex behaviours.
Also if you're still unsure about what behaviour trees are, or how they work, [this video](https://www.youtube.com/watch?v=J7RnUc4g9Pk&t=525s) does a pretty good job outlining this key AI technique. Most examples are some kind of real-time FPS game but the same techniques can work just as well in an IF simulation.
-------------------------
sandbags | 2024-03-13 17:15:58 UTC | #72
How I am working with nested objects right now.
Rez supports table attributes, so you can write:
```
@actor player {
attrs: {
str: {
cur: 3d6
}
dex: {
cur: 3d6
}
con: {
cur: 3d6
}
}
}
```
The downside is that the `attrs:` attribute is a JS object representing the entire table of nested values. If you want to change the current value of `str:` you have to get the `attrs` object, futz with the nested value, then put the whole object back.
This is not a problem, per se, but I don't feel its a good solution (esp. as Javascript doesn't have great support for working with nested values (where Clojure for example has excellent support).
A while back I realised that I'd made Rez into an "id based" programming system. Every element has an id, there is the `$(id)` function that returns the object for a given id. Then I implemented a feature that automatically generates properties for attributes with an `_id` suffix.
So, if you have an attribute `attrs_id: #attrs_obj` it will automatically create a companion JS property `attrs` that is a reference to whatever object represents `#attrs_obj`.
So now I would write this:
```
@actor player {
attrs_id: #player_attrs
}
@object player_attrs {
str_id: #player_str
dex_id: #player_dex
con_id: #player_con
}
@object player_str {
cur: 3d6
}
```
Now I have objects delegating to each other, I can write `$player.attrs.str.cur += 1` and the lookups happen in the background.
There is a serialisation benefit underlying this, because references are "by id" there is no possibility of cyclic references. Rez actually deals with references as part of its built in save/load mechanic but it's finicky and I'm looking to simplify it.
-------------------------
sandbags | 2024-03-18 13:33:46 UTC | #73
Released [v1.2.6](https://github.com/mmower/rez/releases/tag/v1.2.6) with binaries for macOS, Linux, and Windows.
The main feature added was the `@behaviour_template` directive for specifying chunks of behaviours to share between different objects.
I've also been doing a lot of work on the documentation including starting on an [authoring guide](https://rez-lang.com/docs/authoring/) which I am writing in conjunction with building, which should be less overtly about technical detail and more leading you through the process of creating a game with the tool.
-------------------------
sandbags | 2024-04-25 10:23:10 UTC | #74
In v1.2.7 I've removed a thorn from my paw. When you make bindings you now use a list, which means that one binding can depend upon another:
````
@card foozle {
bindings: [
game: #game
title: game.title
]
content: ```
${title}
```
}
````
In such a simple case you'd probably be okay writing `${game.title}` but in more nested contexts (`game.player.attributes.strength.current_value`) I've found it gets tedious and this makes life a little easier.
This wasn't possible before because bindings were specified using a table (map) value and JS objects do not guarantee order, so that you could not be sure that `title` wouldn't be evaluated before `game` was, causing an exception.
-------------------------
tsanderdev | 2024-04-25 10:35:00 UTC | #75
[quote="Matt, post:74, topic:66437, username:sandbags"]
and JS objects do not guarantee order
[/quote]
But they do?
[Object.keys():](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys)
> `Object.keys()` returns an array whose elements are strings corresponding to the enumerable string-keyed property names found directly upon `object`. This is the same as iterating with a [`for...in`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in) loop, except that a `for...in` loop enumerates properties in the prototype chain as well.
[for ... in loop:](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in)
> The traversal order, as of modern ECMAScript specification, is well-defined and consistent across implementations. Within each component of the prototype chain, all non-negative integer keys (those that can be array indices) will be traversed first in ascending order by value, then other string keys in ascending chronological order of property creation.
And if I interpret the [ECMAScript standard](https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-runtime-semantics-propertydefinitionevaluation) correctly, the order of property creation using an `{...}` is also guaranteed to be in-order.
-------------------------
sandbags | 2024-04-25 10:51:11 UTC | #76
Thanks. Of course I could be wrong, but while for…in is guaranteed because it is enumerating array indices, does Object.keys make guarantees about the order in which properties are enumerated? My understanding is not.
Also, I didn’t mention that Elixir maps (how the Rez compiler represents tables internally) do not guarantee order. So even if the compiled JS object does, I can’t guarantee that object has the right order in the first place.
As a rider, I think conceptually a list is a more appropriate choice given order dependency. It doesn’t then depend upon an implementation detail of maps. I didn’t think too hard about this when I first introduced bindings. A table was obvious (for defining key pairs) but wrong.
-------------------------
tsanderdev | 2024-04-25 10:49:38 UTC | #77
[quote="Matt, post:76, topic:66437, username:sandbags"]
Thanks. Of course I could be wrong, but while for…in is guaranteed because it is enumerating array indices, does Object.keys make guarantees about the order in which properties are enumerated? My understanding is not.
[/quote]
My quotes are from the MDN documentation of the JS functions. `for...in` doesn't just work for arrays, but also for objects. In that case it returns the keys of the properties. And since `for...in` guarantees order, so does `Object.keys()`.
[quote="Matt, post:76, topic:66437, username:sandbags"]
Also, I didn’t mentioned that Elixir maps (which Rez tables are implemented as in the compiler) do not guarantee order. So even if the compiled JS object does, I can’t guarantee that object has the right order in the first place.
[/quote]
Oh, so Rez is implemented in Elexir which is compiled to JS?
-------------------------
sandbags | 2024-04-25 10:55:51 UTC | #78
I’m on my phone so a bit linited right now but it may be that when i researched this i came up with out of date info.
You got it. Rez is an Elixir compiler app that produces HTML scaffold, JS source, and associated resources.
-------------------------
tsanderdev | 2024-04-25 11:04:40 UTC | #79
I looked it up, Elixir maps are un-ordered. But surely there is an ordered map implementation for Elixir as a library - even C++'s notoriously lacking standard library has an ordered map.
-------------------------
sandbags | 2024-04-25 11:15:01 UTC | #80
Yes, I did find someone had built an OrderedMap implementation that was a little old but serviceable. But essentially what this is doing is adding a List that holds the insertion order of keys.
Switching to a List is conceptually simpler and, as I mentioned above, would have been the sounder choice in the first place (esp when you consider `bindings:` to be a Clojure `let`).
-------------------------
sandbags | 2024-04-27 16:46:33 UTC | #81
Released [v1.2.7](https://github.com/mmower/rez/releases/tag/v1.2.7) with binaries for macOS, Linux, and Windows.
Changes:
Bindings are now specified as a list where a binding can depend on previous bindings.
Prefix a binding value with * to auto-dereference ids (object or array)
Input bindings can now bind to scene & game
Stdlib adds:
````
Boolean.rand()
RezList.randomFromBag()
````
Filters:
````
char_at
````
-------------------------
sandbags | 2024-04-29 09:41:10 UTC | #82
Over the weekend I spent a little time working on a new [authoring guide](https://rez-lang.com/docs/authoring/) that is intended to come at things from a 'how to get things' done perspective and derived from the lessons I am learning as I build with it myself.
I've put up the first draft of Chapter 1 and I would be grateful for any feedback. There is a risk it may already be too fast paced and even that feedback would be useful.
Later chapters will build on the foundations and introduce more concepts.
-------------------------
HAL9000 | 2024-04-29 12:06:36 UTC | #83
This is really good! It's so useful for people like me who know the basics of HTML/JS/CSS, but not many deeper programming concepts or languages. I haven't installed Rez yet, but there was nothing that I didn't understand. I felt like I was typing and creating a Rez game... without typing and creating a Rez game. :-)
The biggest thing I get from the tutorial is that Rez appears much less intimidating to me now. I can't express enough how nice this is for newcomers.
-------------------------
sandbags | 2024-04-29 12:19:20 UTC | #84
That's great to hear, thank you. I'll carry on with this in my spare moments.
The next chapter will go into more depth about structuring with scenes.
Then through some of the built in objects like inventory & items, actors & relationships, and so on.
Then likely covering events and where Javascript code plugs in to create dynamic behaviour.
Finally tee'ing up the cookbook and more advanced topics.
-------------------------
sandbags | 2024-04-29 18:47:35 UTC | #85
Released [Rez v1.2.8](https://github.com/mmower/rez/releases/tag/v1.2.8) for macOS, Linux, and Windows.
The main change for authors are:
* Implements the new simplified link syntax
Instead of writing `` you can write `` and the link will get rewritten in the background.
Also supports ``, ``, and ``. To pass params just add `data-foo="bar"`.
* Improvements to event param handling
Params are now easily obtained via bindings.
* Improvements to event handling
Event Handlers now return a RezEvent instead of a plain object with an API to specify how the event should be handled.
-------------------------
sandbags | 2024-04-29 20:11:00 UTC | #86
There are still some rough edges here or there but I think 1.2.9 (which will come with a couple more bugs fixes) could be it. The _true_ 1.0. I'm working with it now and finding it pretty nice.
The way bindings work now has made things a lot easier. The new link syntax. The ability to pass params between cards & scenes to make them more customisable. The flexible code blocks.
I'm not finding myself doing a lot of "well I wish I could …" thinking recently.
That is going to make it a lot easier to focus on documentation and examples.
-------------------------
jkj_yuio | 2024-04-29 22:48:30 UTC | #87
I like the tutorial. Nicely done.
I find myself trying to relate Rez concepts to traditional IF concepts. This is going to be a thing, depending on whether the author is coming _fresh_ into IF or has used previous systems.
For example, the concept of "player location" appears to be represented by the current scene card. So then i think, if you had an inventory, how would that work? Perhaps the next tutorial will cover this. hope so.
Then i think, in general, will Rez support a world model, and if so, how will that work. If it does, it would definitely be worth explaining how it relates to the way traditional IF systems work. For example, scope and all that.
good stuff.
-------------------------
sandbags | 2024-04-30 10:10:24 UTC | #88
[quote="jkj yuio, post:87, topic:66437, username:jkj_yuio"]
I like the tutorial. Nicely done.
[/quote]
:) thank you!
[quote="jkj yuio, post:87, topic:66437, username:jkj_yuio"]
I find myself trying to relate Rez concepts to traditional IF concepts.
[/quote]
We all have different histories here, what are the key concepts you see as traditional/expected?
[quote="jkj yuio, post:87, topic:66437, username:jkj_yuio"]
For example, the concept of “player location” appears to be represented by the current scene card.
[/quote]
In an early version of Rez, I had concepts for map & location but they didn't sit well with the idea of scene & card, and the progression from card-to-card and scene-to-scene. As you observe "the player is seeing the current card, it's where they are".
In the end I realised that cards can fulfil all the useful properties of a location (esp since they can have arbitrary attributes) and if necessary I just add an attribute like `location: :foo` to tie together cards that "belong in the same place". Assuming you need that.
[quote="jkj yuio, post:87, topic:66437, username:jkj_yuio"]
So then i think, if you had an inventory, how would that work?
[/quote]
In the game I am working on the players inventory is a separate `@scene` that you interlude to (an interlude allows you to jump to a new scene, then resume the original scene at the point where you left off. Interludes can also have interludes, I keep them in a stack).
A forthcoming chapter will definitely build an inventory.
[quote="jkj yuio, post:87, topic:66437, username:jkj_yuio"]
Then i think, in general, will Rez support a world model
[/quote]
In fact this is axiomatic to my approach. The `@game` is the model containing the actors, relationships, inventories, items, effects, factions, plots, and so on. With systems designed to help these objects dance around each other.
I guess I haven't made that point clearly in the authoring guide and perhaps I should (but without getting too much into the mechanics).
Thanks for your thoughts.
Matt
-------------------------
jkj_yuio | 2024-04-30 13:45:57 UTC | #89
It's great to see how Rez is thinking out of the box. Of course, none of the traditional IF concepts need to be done in the same way, but only talked about. so people understand how the new way relates to the old way.
I had the same thing with locations in Strand. In fact strand doesn't have locations as special objects at all. This created a problem when I wondered how would I implement something like "go to X". Only recently I managed to get the system to _deduce_ objects that are locations and their connectivity. Kind of working backwards.
A core feature of trad IF implementation is "scope". Essentially this is the list of all things the player can currently interact. It's needed for parser resolution. But maybe you don't need it for choices. Something to watch, as it may pop up. I use it a bit also for clickable objects in text, in order to resolve. But in most cases, those clicks can use an internal ID, which is global.
Darkness is another irritating concept that's part of trad IF. I decided to mostly ignore it and certainly not put it into the core system. The way you'd implement darkness in Strand is, when dark, to seed the scope with the player rather than the player's location. This would make all things in the location unreachable, except for those on the player's person. So there's that scope thing again.
Other things popular in trad if:
* doors
* containers
* surfaces (aka supporters)
* cardinal directions
* body parts
* scenery
* wearables
* edibles
Well, all of these are in Strand, but _none of them_ are part of the runtime code, only the DSL.
The question for Rez is whether any of these things are needed for the choice system. That's because, as i understand it, Rez isn't a _pure choice_ system because it will have/has a world model, absent in pure-choice systems.
In any case, these are the sorts of things authors will probably want to ask about. The answer for Rez may well be - you don't get them. But if there's a Rez way to have something like it, that should be mentioned.
Best of luck.
-------------------------
sandbags | 2024-04-30 14:51:30 UTC | #90
[quote="jkj yuio, post:89, topic:66437, username:jkj_yuio"]
Essentially this is the list of all things the player can currently interact. It’s needed for parser resolution. But maybe you don’t need it for choices.
[/quote]
Ah, yes, I think this is a very different problem in a choice based game as the context is (typically) far less implicit. The card (or cards) you are looking at delineate the choices available, so they do much of the heavy lifting (at least in Rez).
For example if the card is presenting a location, how do we present links that can take the player to other locations?
I've solved this in a couple of ways. For example just adding an `exits:` attribute that maps potential exits to other cards representing other locations. Then I can use an embedded card that displays exits (it's template uses `$foreach()` to dynamically render those exits appropriately).
Because an exit is just a data structure it's easy to handle things like hidden exits, locks, and so forth. The difference being that I would naturally build an "unlock" action into the interface for changing location. It's less generic and more explicit (and I hate puzzles :))
Containers I have in the item/inventory system. It is flexible enough to handle wearables, body parts, etc…
Surfaces is an interesting one. I suspect it is easily handled mechanically by the inventory using an "on top" slot, for example. But I would have to test & see.
[quote="jkj yuio, post:89, topic:66437, username:jkj_yuio"]
The question for Rez is whether any of these things are needed for the choice system. That’s because, as i understand it, Rez isn’t a *pure choice* system because it will have/has a world model, absent in pure-choice systems.
[/quote]
I think what you are calling "hybrid" is linked to my distinction between IF as a (potentially) branching narrative system & as a game. If you'll allow me to avoid having to define narrative or game and just accept that — given sufficient time & alcohol we could hammer out some way to determine what is a game & what is a story that would cover 80% of the cases.
Now in a pure-choice narrative there is a little need for a world model because it's "all in the cards". Each card presents exactly what is needed in that moment and there is a 1:1 mapping between every situation and card.
But in the kind of games I have in mind this is not so and so a world model is required to hold the state that the player is exploring. In this sense while the modality is totally different (choice vs parser) the underlying conceptual framework is very similar.
However where a parser based game is usually (usually!) a semi-linear narrative unlocked by puzzles that require game model for state, the kind of games I am interested in require the state to drive a simulation loop and emit narrative (almost as a by-product).
[quote="jkj yuio, post:89, topic:66437, username:jkj_yuio"]
Well, all of these are in Strand, but *none of them* are part of the runtime code, only the DSL.
[/quote]
That's interesting. I feel like Strand may be operating at a higher conceptual-level. In Rez there is, more or less, a 1:1 correspondence between the `@element`s that you author and some backing object representing the element concept in the runtime system.
-------------------------
sandbags | 2024-05-04 11:21:15 UTC | #91
Okay teeny tiny milestone but I've just created my first, fully procedurally generated, NPC.
It's taken a little bit of figuring out to make the bits dance together between the template `@actor` the initialisation & copying code, and how dynamic properties get made & initialised. I'm not sure this is "right" yet, but it works.
Rez has three types of element:
* static — defined at author time with attributes defined at author time.
* dynamic — defined at author time with some attributes defined at runtime, for example using the [`^i{}` initialiser](https://rez-lang.com/docs/language_reference/#_dynamic_initializer).
* generated — defined at runtime, based on an author time template, with mostly runtime attributes (because you want them to vary from each other) initialised by the `on_copy` event handler.
Example:
```
@actor t_base_npc {
$global: true
$template: true
given_name: _
family_name: _
name: ^p{return `${this.given_name} ${this.family_name}`;}
skills_id: _
on_copy: (copy, params) => {
copy.given_name = $given_names.randomElement();
copy.family_name = $family_names.randomFromBag();
const skills = $t_skills.copyWithAutoId();
$game.addGameObject(skills);
copy.skills = skills;
}
}
// somewhere else
const npc = $t_base_npc.copyWithAutoId();
$game.addGameObject(npc);
console.log(npc.name); // Prints "Kira Lewis"
```
The placeholder values (`_`) for `given_name` and `family_name` ensure that properties are created for those attributes at runtime even if they have no author time value.
The `name` property is defined to use those properties. The values themselves are filled in during `on_copy`. In this case we want each NPC to have their own skills, so skills are created by copying a template `$t_skills` element.
I think there is probably a lot more to do here and it occurs to me that I might want some kind of "world generator" that can coordinate how the generated elements are created and relate to each other. Doing it from the individual `on_copy` handlers is possible but likely to be messy and there may be sequencing issues.
As an aside `$global` is a new system attribute specifying whether a global variable, `$`, should be created to refer to this element. It's a trivial improvement but I find it tidies up references to common lookup objects.
-------------------------
sandbags | 2024-05-09 19:51:06 UTC | #92
Released [v1.2.9](https://github.com/mmower/rez/releases/tag/v1.2.9) for macOS, Linux, and Windows.
* Adds `$global` attribute for creating global references to elements
* When doing a resume merge new params
* Fixes some bugs
* Renames & reorganises a bunch of the rng JS functions
* Drops `^v{}` code block which was deprecated in favour of `^p{}`
The RNG functions are:
* `Number.chance()`
* `Math.rand_int(lim)`
* `Math.rand_int_between(lo, hi)`
* `Math.rand_f_between(lo, hi)`
* `Math.cl_rand_f_between(lo, hi)`
* `Math.cl_rand_int(lim)`
* `Math.cl_rand_int_between(lo, hi)`
The `cl_` variants uses central limit to create normally distributed random values.
-------------------------
Draconis | 2024-05-09 21:13:51 UTC | #93
What's the standard deviation of the `cl` ones? Presumably the mean is halfway between `lo` and `hi`, but there are a lot of different normal distributions with a given mean.
-------------------------
JoshGrams | 2024-05-09 22:57:42 UTC | #94
[Looks like](https://github.com/mmower/rez/blob/main/assets/templates/stdlib.rez.eex#L506) it's a triangular distribution, not a normal one? Average two uniform random numbers between lo and hi.
ALso @sandbags does that even run? Looks to me like it should be calling `rand_f_between` and you have `randf_between`.
-------------------------
sandbags | 2024-05-10 07:37:28 UTC | #95
Good catch. Was renaming and moving the randomisation functions to group & clarify them and missed this one.
I'm a long way from any real math but I believe that the average of two uniform distributions is a normal one.
-------------------------
sandbags | 2024-05-10 07:44:12 UTC | #96
Sure and I couldn't tell you off-hand what the σ is, I'd have to go work it out. In practice I just plotted the distribution and said "looks about right".
-------------------------
Hanna | 2024-05-10 10:34:08 UTC | #97
[quote="Matt, post:95, topic:66437, username:sandbags"]
I’m a long way from any real math but I believe that the average of two uniform distributions is a normal one.
[/quote]
The average of n samples gets closer and closer to a normal distribution as n increases, but for n = 2 it’s still a good way off: the probability density function (histogram, if you put the float values in discrete buckets) is a pyramid/triangle rather than a bell-shaped curve. You can get a much better approximation by just averaging more samples, maybe up to a dozen seems to be in common use. Wikipedia has a [figure](https://en.wikipedia.org/wiki/File:Irwin-hall-pdf.svg) of what the sum (i.e., before dividing by n) looks like for min = 0, max = 1 and several choices of n.
-------------------------
sandbags | 2024-05-10 11:35:34 UTC | #98
Right, that makes sense. In a different library I had a way to specify how many samples to average. In practice I found `n=2` gives just enough pull to the middle to satisfy my needs but in general it might be useful to have a way to do that. Thanks.
-------------------------
sandbags | 2024-05-13 00:18:50 UTC | #99
I've been putting it off but the need finally arose for rendering a card from another cards `$foreach`.
You can already render cards-within-cards in static form using the `blocks:` attribute to specify the `#id` of cards you want to render from within the `content:` template. A common example is rendering a card as a sidebar:
````
@card card_1 {
blocks: [#sidebar]
content: ```
${sidebar}
Other stuff
```
}
````
When `#card_1` gets rendered the card `#sidebar` gets rendered first and the rendered content is automatically bound under the same name. So the `sidebar` binding contains the rendered content of the `#sidebar` card and can be interpolated using the expression `${sidebar}`.
However this doesn't help if you (a) don't know which card you want to render, or (b) want to render it more than once as you do in a `$foreach`.
I just about got this working by implementing a `render_card` filter but it was pretty… unergonomic:
````
@card c_char_list
bindings: [
characters: *game.characters
]
content: ```
$foreach(c:characters) {%
${"c_char_desc" | render_card: c, $block, …}
%}
```
}
````
First this is rather opaque looking, it's not at all easy to see what is happening here.
Second, passing the card id as a string expression to be filtered is just janky.
Third, having to pass the `$block` binding to the filter. You have to because the renderer can't do its thing without it but, ugh.
Lastly, how to specify parameters? Actually this is what broke me, filters aren't meant for this level of complexity and filter parameters don't parse object syntax.
At this point I threw out the `render_card` filter approach and started working on a `$partial` template expression:
````
$partial(#card_id, {param1: value1, param2: value2, …})
````
Here is an example showing how it works within a `$foreach`, and passing params to the card being included so that it can customise its own content:
````
@card c_char_list {
bindings: [
characters: *game.characters
]
content: ```
Cast of characters
$foreach(c:characters) {%
$partial(#c_char_desc, {target: c})
%}
```
}
@card c_char_desc {
bindings: [
name: *params.target.name
]
content: ```
${name}
```
}
````
You can also pass a bound expression instead of an `#id` so if our character had something like:
````
@actor gubbins {
template_id: #c_main_actor
}
````
then you could use something like:
````
@card c_char_list {
bindings: [
characters: *game.characters
]
content: ```
$foreach(c:characters) {%
$partial(c.template_id, …)
%}
```
````
and dynamically specify the template at the same time, allowing different characters to be rendered with different partial templates.
I'm actually a little surprised how easy this was to implement, I hate looking at the renderer code, and the template compiler even more so, but it was actually fairly straightforward and worked first time.
With luck this is the last change I need to make to the template system and rendering code.
This will be in v1.2.10.
-------------------------
sandbags | 2024-05-23 10:27:15 UTC | #100
I knew there was a reason I implemented the `$do{…}` template expression.
In practice because Rez has a good set of event handlers you should never need to run code within a template, for example a `@card` can have an `on_render` handler that is called just prior to rendering, or `on_start` that is called before even the render process begins. It's cleaner and neater than littering templates with ad hoc code.
However, what is useful is to be able to write:
```
$do{debugger;}
```
inside a template and drop into the JS debugger in the template rendering context. I can see all of the bindings that have been made and quickly debug why something didn't work as expected.
-------------------------