Rez includes many elements that you will combine to create your game, starting
with the @game
element that wraps the whole thing.
All elements in a Rez source file are prefixed by @
.
A few Rez elements like @game
and @zone
contain other elements but most do
not.
Elements
Directives
Alias (Directive)
The @alias
directive allows the author to refer to a particular kind of element using a convenient and meaningful name. It is usually used in conjunction with parent specifiers. For example it might be more meaningful to define @sword
and @shield
as aliases for particular kinds of @item
.
Example
In our Maltese Parrot game hats are a big deal and a range of hat items will be needed but we don’t want to repeat ourselves defining each one using @item
so we can create an alias that specifies that a hat is an item and how hats are, generally, configured. Then our hat definition just needs to supply what’s different about that hat.
Here’s an example:
@item hat { type: :hat wearable: true usable: false bogie_would_approve: false } @alias hat = item<hat> @hat wool_fedora { material: :wool colour: :black description: "A Messer black wool fedora hat" bogie_would_approve: true } @hat brown_derby { material: :felt colour: :brown description: "Battered brown derby" }
In this example we have made an alias, @hat
for @item
specifying a parent id hat
. Using @hat
creates new @item`s that specify the `hat
item as their parent. Equalivent to:
@item wool_fedora<hat> { ... }
Note that an alias can specify multiple parents.
Actor (Element)
An actor represents an in-game character which could be the player avatar or a non-playable character that the player interacts with. Define an actor with the @actor
element. In-game actors are represented by the RezActor object.
Actors are an optional concept and a simple game might not need them, choosing instead to represent any actors via attributes of the game or scene. But in more complex games it’s useful to be able to model actors separately.
Example
In this example of our game the player can decide which of the antagonists they wish to play as. Each has different abilities and trust other characters different amounts.
@actor sam_spade { name: "Sam Spade" stats: { gunplay: 6 fisticuffs: 7 drinking: 8 flirting: 6 sluething: 9 chat: 6 } container: #sams_stuff } @rel #sam_spade -> #miss_wonderly {affinity: +2} @rel #sam_spade -> #joel_cairo {affinity: -2} @rel #sam_spade -> #kaspar_gutman {affinity: -4} @actor joel_cairo { name: "Joel Cairo" stats: { gunplay: 3 fisticuffs: 3 drinking: 5 flirting: 9 sleuthing: 6 chat: 8 } container: #joels_stuff } @rel #joel_cairo -> #sam_spade {affinity: 1} @rel #joel_cairo -> #miss_wonderly {affinity: -1} @rel #joel_cairo -> #kaspar_gutman {affinity: -3} @actor miss_wonderly { name: "Ruth Wonderly" stats: { gunplay: 4 fisticuffs: 2 drinking: 5 flirting: 10 sleuthing: 4 chat: 9 } container: #ruths_stuff } @rel #miss_wonderly -> #sam_spade {affinity: 4} @rel #miss_wonderly -> #joel_cairo {affinity: 1} @rel #miss_wonderly -> #kaspar_gutman {affinity: -2} @actor kaspar_gutman { name: "Kaspar Gutman" stats: { gunplay: 1, fisticuffs: 3, drinking: 9, flirting: 2, sleuthing: 7, chat: 9 } container: #kaspar_stuff } @rel #kaspar_gutman -> #sam_spade {affinity: 2} @rel #kaspar_gutman -> #miss_wonderly {affinity: -2} @rel #kaspar_gutman -> #joel_cairo {affinity: 1}
By using a set of @actor`s we can keep things separate and easier to understand and use the built-in `@rel
directive to create relationships between the actors.
Required Attributes
Optional Attributes
|
Set |
a set of keyword tags |
|
Element Ref |
id of the inventory that represents items carried by this actor |
Event Handlers
on_accept_item
on_accept_item(actor, event) => {...}
The event
argument is a map in the form:
{ decision: <decision_obj>, inventory_id: <id>, slot_id: <id>, item_id: <id> }
This is a script that can be called to check whether an item can be placed into an inventory slot of a container that they are owner of (See also: inventory#owner)
on_accept_item: (actor, event) => { event.decision.no(actor.name + " doesn't want to be burdened by worldly goods."); }
on_init
on_init: (actor, event = {}) => {...}
This script will be called during game initialization and before the game has started.
on_enter
on_enter: (actor, event) => {...}
The event
argument is a map
{ location_id: <id> }
This callback will be received when the actor is moved to a new location and is passed the id of the location to which the actor has moved.
on_leave
on_leave: (actor, event) => {...}
The event
argument is a map
{ location_id: <id> }
This callback will be received when the actor has left a location and is passed the id of the location which has been vacated.
on_turn
on_turn: (actor, event = {}) => {...}
If the game turn mechanism is being used this callback will be received on each game turn. This is intended for simple cases and if you need to coordinate behaviours across multiple elements it may be better to use a system instead.
Asset (Element)
An @asset
element refers to a file on disk, typically an image, audio, or video file, that will be presented in game.
Rez automatically copies asset files into the game distribution folder when the game is compiled and manages pathing so that assets can be referred to in game without worrying about filenames and paths.
Assets can be collected into groups (using @group
) dynamically choose from among related assets.
Example
@asset hat_01 { file_name: "hat_01.png" tags: #{:hat} }
This defines an asset that will be copied into the game when built and which can be referred to in-game by it’s id.
Rez will ensure that all assets are available during compilation.
Assets are the key to using asset groups that can be used for showing different but randomised media.
Required Attributes
|
String |
name of the asset file in the assets folder |
Optional Attributes
Event Handlers
on_init
on_init: (asset, event = {}) => {...}
This script will be called during game initialization and before the game has started.
Behaviour (Element)
Behaviours are elements that describe components of a behaviour tree. There are four types of behaviour:
-
condition — these test some property of the game world
-
action — these modify the game world
-
composite — these act on a group of 'child' behaviours
-
decorators — these modify other behaviours
While the difference between conditions and actions are fairly intuitive, the difference between composites and decorators is more subtle. Composites are about coordinating between a series of other behaviours, while a decorator typically modifies the results of another behaviour.
For example the $sequence
core behaviour executes its children in turn and succeeds or fails based on them, while the $invert
core behaviour turns its childs succees into failure (or vice verca).
When a behaviour is executed it either succeeds or fails.
As we have seen from the examples above, a composite behaviour usually succeeds or fails based on the success or failure of its children. A decorator typically modifies the success or failure of another behaviour. Conditional behaviours succeed or fail based on a test and action behaviours succeed based on whether their implied action is successful.
From these four simple concepts some very powerful behaviours can be built.
Rez defines a number of 'core' behaviours. By convention these have $
prefix to their id to separate them from author written behaviours. The core behaviours are mostly composites and decorators that are intended to be building blocks for author written behaviours.
The core of a behaviour element is its execute:
script attribute. This is intended to implement the functionality of the behaviour and return a value whether it succeeds or fails.
Each behaviour can, optionally, receive options and, again optionally, a list of child behaviours. Conditions and actions are not expected to have children while composites and decorators don’t make sense without at least one child.
When a behaviour tree is run it gets passed an empty object {}
as "working memory" to allow different behaviours to communicate state required to run the tree. As a new working memory is used each time the tree is run, any persistent state changes should be in the world model.
Let’s look at an example. We want a condition that tests whether a given actor is in a certain location. Here’s how we could implement it.
Example
@behaviour actor_in { options: [:actor :location] execute: (behaviour, wmem) => { const actor_id = behaviour.option("actor"); const actor = $(actor_id); const location_id = behaviour.option("location"); if(actor.location == location_id) { return {success: true, wmem: wmem}; } else { return {success: false, error: "Actor is not in location", wmem: wmem}; } } }
Here we define the actor_in
condition behaviour that tests whether a specified actors is in a specifed location. We might use it like this:
In this example we have defined a condition behaviour to test whether a specified actor is in a given location. This could be used in a sequence to ensure that an action only gets performed if in the correct location.
^[$sequence [actor_in actor=sam_spade location=sams_office] ... ]
The rest of the behaviours in this sequence will only be run if Sam is in his office, otherwise the sequence will fail.
Required Attributes
|
List |
keywords describing the options that this behaviour uses. If there are no options use the empty list |
|
Script |
script that takes two parameters |
Optional Attributes
Behaviour Template (Directive)
A behaviour template is a composable element of behaviour. When writing behaviour trees you may find yourself wanting to use some behaviours over and over but not want to copy a whole tree. That’s where behaviour templates come in. With a template you can include just the parts of behaviour you need.
Syntax
The syntax for a behaviour template look like:
@behaviour_template <template_id> ^[...]
Behaviour template id’s are separate to element id’s and can overlap without conflict.
Usage
Let’s look at an example. Here is an actor with some behaviours:
@actor sam_spade { behaviours: ^[$select [$sequence [actor_in location_type=:bar] [actor_is state=:thirsty] [actor_says msg="Give me a whisky."]] [..more behaviours..]] }
Maybe it’s not just Sam that you want to be able to order liquor at the bar. But you don’t want to copy Sam’s entire behaviours:
attribute as it contains some behaviours that are unique to Sam. We can move this specific behaviour into a template and share it among multiple actors (or any other behaviour supporting object in your game):
@behaviour_template order_whisky ^[$sequence [actor_in location_type=:bar] [actor_is state=:thirsty] [actor_says msg="Give me a whisky."]] @actor sam_spade { behaviours: ^[$select &order_whisky [..behaviours unique to Sam..]] } @actor joel_cairo { behaviours: ^[$select &order_whisky [..behaviours unique to Joel..]] }
Now both Sam and Joel can make use of the behaviour.
Templates can also include other templates allowing for clean composition of many complex behaviours.
Card (Element)
Cards are the basic unit of content & interaction in a Rez game. Cards are "played" into a scene to present what is happening to the user and offer them choices about what to do next. In this they serve a similar role to Twine passages.
The content
attribute is key as it defines the template that is rendered each time the card is played. Optionally a card may also define flipped_content
which is what is displayed in a scene using a stack layout after the card has been used (i.e. the player has followed a link from that card).
Cards can be part of the main interface but can also be used as blocks in other cards. For example a card could be defined to represent a sidebar and included into scene layout.
Internally the content
and flipped_content
attributes of the card are converted into template expressions (a kind of Javascript function) so that they render quickly.
Example
@card intro_part_1 { content: ``` You are in a mazy of twisty passages all alike. [[Go forward|intro_part_2]] ``` } @card intro_part_2 { content: ``` You get the idea! [[Go backward|intro_part_1]] ``` }
Required Attributes
|
Template |
primary content to be displayed when this card is played into a scene |
Optional Attributes
|
Template |
content that is presented after the card is used in a stack layout |
|
List |
List of element-ids of the cards that can be referenced in the |
|
Table |
keys to bindings which can either be game object ids or functions returning a value. E.g. |
|
String |
custom CSS classes to apply, "information is-primary" |
Event Handlers
on_init
on_init: (card, event = {}) => {...}
This script will be called during game initialization and before the game has started.
on_enter
on_enter: (card, event = {}) => {...}
on_render
on_render: (card, event = {}) => {...}
on_ready
on_ready: (card, event = {}) => {...}
on_leave
on_leave: (card, event = {}) => {...}
Notes
Card content is written in Markdown and converted to HTML. It’s somewhat similar to a Twine passage and some of the basic syntax, e.g. [[Go forward|intro_part_2]]
translate across to Rez.
However there are a number of additional syntaxes. For example it’s possible to hijack this mechanism using a script:
@card intro_part_1 { content: ``` You are in a mazy of twisty passages all alike. [[Go forward]] ``` on_go_forward: (game, evt) => {return Scene.load_card(game, "intro_part_2");} }
Here we define an event handler which will respond to the link being clicked. By default Rez will automatically convert a link such as "Go forward" into the equivalent "go_forward" by downcasing and replacing whitespace with a single _
character.
Rez also has support for more dynamic types of links:
@card intro_part_1 { content: ``` You are in a mazy of twist passages all alike. [[Go forward|go_forward]] ``` go_forward: (game, evt) => {evt.choice.show("Go forward);} on_go_forward: (game, evt) => {return Scene.load_card(game, "intro_part_2")} }
When a card link is written in this format, Rez will look inside the card for an attribute with the same name and a function value. It will call the function which can determine whether the link should be shown or hidden and, if it is shown whether it should be enabled or disabled. whether it is enabled or disabled.
However there is support for other kinds of actions and dynamic links. See the COOKBOOK for more information.
Component (Directive)
A @component
directive is used to specify an HTML component used in templates.
For example we may have specified a button like this:
<button class="button is-small" data-event="reload">…</button>
There’s nothing wrong with this but the details are obscured by the attribute syntax, what if we could write:
<.event_button event="reload">…</.event_button>
The .
prefix in <.event_button>
indicates that this tag is implemented as a user component.
Let’s write this component:
@component event_button (bindings, assigns, content) => { return `<button class="button is-small" data-event="${assigns["event"]}">${content}</button>`; }
Container components like <.event_button>
have their contents available in the content
argument, attribute values in assigns
, and all bindings available at the component site in bindings
. Self contained components have no content specified.
Declare (Directive)
A @declare
directive is a shorthand for defining an @object
element without
attributes. Typically you use this for declaring an object to be the target of
relationships but which does not, itself, need to be defined in terms of a set
of attributes.
Example
In this example we are specifying that the player hates the dark (affinity -5) but we don’t need "the dark" to be more than a placeholder to be the target of the relationship
@declare the_dark @rel #player -> #the_dark { affinity: -5 }
Defaults (Directive)
A @defaults
directive is a way to setup default attributes for a type of element or alias.
The syntax is simple:
@defaults <element_or_alias> { attribute: value attribute: value }
Example
@defaults card { hub: false }
From this point in the source file all @card
elements will pick up a hub: false
attribute without you having to set it.
Note that you can later change issue a new default for @card
and any @card
elements defined from that point will inherit the new default instead.
It is possible to set defaults for an @alias
that will only be set for elements that use the alias. So this is legal:
@defaults card { is_storylet: false } @defaults storylet { is_storylet: true storylets: function() { return []; } } @alias storylet = card
Now a card defined using the @storylet
alias will has is_storylet: true
and the default implementation of the storylets:
attribute while regular cards get is_storylet: false
and have no storylets:
attribute.
See stdlib.rez
for modifiable system defaults.
Derive (Directive)
The @derive
directive is used to form keywords into hierarchies of types for items, effects, and so on.
Let’s take an example of where this might be useful: inventories.
We setup a hierarchy as follows:
@derive :weapon :item @derive :sword :weapon @derive :mace :weapon @derive :potion :item
The result is that an item with type: :sword
, type: :mace
, or type: :potion
can be placed into a slot that accepts: :item
. It’s not required to list all the different types of items that are legal in that slot. Equally our sword can be placed into a slot that accepts: :sword
but an item type: :mace
cannot, nor can an item type: :potion
.
An item hierarchy can be as simple of complex as you need. At run-time all of the item type information is converted into tags. For example an item with type: :sword
would have tags as if we had written tags: #{:sword :weapon :item}
.
Effect (Element)
Effects are modifiers to aspects of the game that can be applied and removed dynamically as the game progresses.
For example an item, when worn, might convey a bonus to the actor wearing it. In this case the effect, attached to the item, is applied when the item is worn and removed when the item is removed.
Effect support is limited in v0.8. Effect scripts will be called and its up to the caller to ensure these work. In particular there is no support yet for effects that, for example, wear off over time.
Example
@effect drunk { name: "Drunk" description: "you're drunk, it's so much harder to concentrate" on_apply: (evt) => { const actor = $(evt.actor_id); // Add drunkness effects } on_remove: (evt) => { const actor = $(evt.actor_id); // Remove drunkness effects } }
Required Attributes
Optional Attributes
Event Handlers
on_init
: (effect, event = {}) ⇒ {…}
This script will be called during game initialization and before the game has started.
on_apply
: (effect, event = {}) ⇒ {…}
on_remove
: (effect, event = {}) ⇒ {…}
on_turn
: (effect, event = {}) ⇒ {…}
If the game turn mechanism is being used this callback will be received on each game turn. This allows an effect to, for example, grow or decline over time.
Faction (Element)
Factions represent in-game groups with their own agenda, reputation, and views
of others. Define a faction using a @faction
element.
Example
@faction police { ... } @faction gutman { ... } @faction player { ... }
Required Attributes
Optional Attributes
Event Handlers
on_init
: (faction, event = {}) ⇒ {…}
This script will be called during game initialization and before the game has started.
Enum (Directive)
An @enum
directive defines a set of legal values for an attribute. The syntax is:
@enum <attr-name> [:value1 :value2 :value3]
Here’s an example:
@enum color [:red :green :blue] @object { color: :orange }
This would cause a compilation error because the color
attribute does not use one of the legally defined values.
'color' attribute value 'orange' is not legal enum value ('red', 'green', 'blue')
Notes:
-
an enum applies over all uses of that named attribute, you can’t use
color: [255, 0, 255]
in another element. -
an enum only applies to attributes using keyword values,
@enum size [1.0 2.0 3.0]
is not a legal enum.
Filter (Directive)
A @filter
directive defines a filter function that can be used in a subsitution Template Expression. A filter has a name which is how you refer to it in a template expression, e.g. capitalize
and an impl function that takes a variable number of parameters (but at least one).
Example
Let’s say we wanted to be able to output a numeric attribute replacing any value over 4 with "a suffusion of yellow". Here’s a filter that would do that:
@filter SUFFUSION_OF_YELLOW_FILTER { name: "soyf" impl: (n) => { if(n < 4) { return ""+n; } else { return "a suffusion of yellow"; } } }
and the expression would be
${number_value | soyf}
As of v0.11.0 the Rez stdlib defines a number of filters and you can see how they are implemented by reading the stdlib.rez
.
See also the filter_catalog.
Game (Element)
The game element is the top-level specification of the game and its metadata. It also defines the scene entry point of the game.
The @game
element has an implicit ID of game
.
Example
@game { name: "The Maltese Parrot" author_name: "Dachshund Hamlet" IFID: "D2050DE2-97A2-1ED1-4CCA-AF9D3B0DD883" created: "2022-08-31 22:13:43.830755Z" version: 10 layout: ```${content}``` initial_scene_id: #sam_and_wonderly_meet }
Required Attributes
|
String |
name of the game |
|
Element Ref |
id of the scene the game begins with |
|
String |
ID of the game in the IFID database (an ID will automatically be generated when the game is created, it’s up to you whether you register it or not) |
Optional Attributes
Event Handlers
on_init
on_init: `(game, event = {}) => {...}`
This script will be called during game initialization and before the game has started.
on_start
: (game, event = {}) ⇒ {…}
The on_start
event is triggered right after the Rez framework has initialized
itself and before the first scene or card gets rendered. It’s an opportunity
to customise game setup.
on_scene_change
: (game, event) ⇒ {…}
event = { scene_id: <id> }
The on_scene_change
script is called whenever a new scene gets started.
The callback happens between the on_finish
and on_start
scripts of the
scenes that are ending and beginning respectively.
on_card_change
: (game, event = {}) ⇒ {…}
The on_card_change
script is called whenever a new card is played into the
current scene.
The callback happens between the on_leave
and on_enter
scripts of the card
that is being played.
Group (Element)
A group specifies a collection of assets that can be selected from. Groups can be static by defining the id of member assets, or dynamic by specifying a set of tags. In the latter case the group will collect together all assets with any of the specified tags.
A group can be used to select an image at random, or cycle through the collection one-by-one.
Example
Required Attributes
|
Keyword |
One of |
|
Set |
Set of tags that appear on assets that should be included in the group |
|
Set |
Set of tags that appear on assets that should be excluded from the group |
Optional Attributes
Event Handlers
on_init
on_init: (group, event = {}) => {...}
This script will be called during game initialization and before the game has started.
Inventory (Element)
The @inventory
element creates a container that can hold `@item`s through the use of `@slot`s. Rez inventories are deliberately flexible to handle a range of use cases for example working memory (where items are thoughts) or spell books (where items are spells).
Rez has a fairly flexible inventory system that is based around 'slots' that define how items can be held. This allows an inventory to hold different kinds of items: you could have an inventory for items as well as an inventory for spells (spell book).
Inventory slots are matched against items to determine whether it’s possible to put an item in a slot.
Inventories are defined using the @inventory
tag.
Inventories have a category which determines the kind of items that can be added to their slots. For example "spell" could represent a spell book, while "equipment" could represent the players inventory.
Example
@inventory player_inventory { slots: #{#hat_slot #jacket_slot #trousers_slot #shoes_slot #holster_slot} }
Required Attributes
|
Set |
Set of element ids of `@slot`s that are included in this inventory |
Optional Attributes
Event Handlers
on_init
on_init: (inventory, event = {}) ⇒ {…}
This script will be called during game initialization and before the game has started.
on_insert
on_insert: (inventory, event) ⇒ {…}
event = { slot_id: <id>, item_id: <id> }
This script will be called when an item has been added to the specified slot of this inventory.
on_remove
on_remove: (inventory, event) ⇒ {…}
event = { slot_id: <id>, item_id: <id> }
This script will be called after an item has been removed from the specified slot of this inventory.
Item (Element)
The @item
element defines a conceptual item the player the player (or potentially an NPC) can acquire and add to an inventory. Items don’t have to represent physical objects but anything a player has for example a spell could be an item or even a memory.
Items are required to have a type
keyword-attribute that connects them to compatible slots in inventories. That might include a shop, a wardobe, and a players backpack inventories.
However the Item/Inventory system is quite flexible so we can also think about spells as Items with the Inventory being a spell-book, or knowledge as Items with an Inventory being memory.
Items may be usable in which case they may have a limit to the number of times they can be used.
Some items can grant effects, either when the item is acquired, put into a specific slot (e.g. equipped), or when it is used.
The can_equip/on_equip scripts are used to decide whether the player can put an item in a given inventory & slot, and to process what happens when doing so.
For example equipping a magic ring might confer an effect on the player. But first it may be necessary to check that the player doesn’t already have a magic ring equipped.
A potion on the other hand confers no effect until it is used and might have only one use after which is presumed to be consumed.
Example
@item black_fedora { type: :hat description: "black fedora" wearable: true description: "A Messer wool fedora hat. Classy." }
Note that this example throws up a design issue to be aware of: tags and boolean attributes are equivalent. For example wearable: true
can also be represented by presence or absence of a tag wearable
. In the case of Item
elements its further possible to use the type system:
@derive :wearable :item @derive :hat :wearable
In this case an Item
with type: :hat
will automatically be tagged as :wearable
and :item
.
Required attributes
|
Keyword |
a keyword representing the type of the item, e.g. |
|
String |
the name of the item |
Optional attributes
|
String|Heredoc|Template |
player description of the item |
|
Number |
where inventories should manage size, defaults to |
|
Boolean |
if the item can be used, defaults to |
|
Number |
if |
|
Element Ref |
Container this item begins the game inside |
|
Script |
|
|
Script |
|
|
Script |
|
|
Script |
Event Handlers
on_init
: (item, event = {}) ⇒ {…}
This script will be called during game initialization and before the game has started.
Keybinding (Directive)
Use the @keybinding
directive to generate custom events from the user pressing a specific key, optionally with modifiers.
The syntax is:
(modifiers)? + keyName
Example
@keybinding ctrl+shift+C :show_character_sheet
Notes
Available modifiers are:
-
shift
-
ctrl
-
meta (the Command key on Mac computers)
-
alt (the Option key on Mac computers)
Modifiers are optional. Where the shift modifier is used the keyName should be in upper case.
KeyNames follow the Javascript KeyboardEvent rules.
Event processing follows the usual custom event processing rules (card → scene → game) allowing for processing events in different places.
List (Element)
A list is a named collection of values that can be used by other in-game elements, for example lists of names, locations, actors, and so on. Lists are defined using the @list
element.
The run-time API supports selecting randomly from lists including with & without replacement.
Example
@list antagnoists { content: [#sam_spade #miss_wonderly #kaspar_gutman #joel_cairo] } @list lines { content: [ "I distrust a man that says when. If he's got to be careful not to drink to much it's because he's not to be trusted when he does." "The cheaper the crook, the gaudier the patter." "I couldn't be fonder of you if you were my own son. But, well, if you lose a son, its possible to get another. There's only one Maltese Falcon." "What do you want me to do, learn to stutter?" ] }
Required Attributes
Optional Attributes
Event Handlers
on_init
: (list, event = {}) ⇒ {…}
This script will be called during game initialization and before the game has started.
Object (Element)
An @object
element describes an author-driven concept. Isn’t everything in Rez an object of some kind? Yes, but elements like @author
, @item
, and @plot
have built-in meaning and functionality. By contrast @object
is a blank canvas that an author can use for anything they think of.
Example
Imagine we are building a role-playing game and we want to introduce the notion skills and perks. Rez does not provide either of these concepts out of the box but we can use the @object
element to make them ourselves.
@object skill { $template: true description: "Something an actor has acquired the ability to do" min: 0 max: 5 cur: 0 } @alias skill = object<skill> @object perk { $template: true cost: 1 } @alias perk = object<perk> @perk gun_license { description: "Without this cops might pick you up for flashing your lead pumper." } @perk dont_go_down_easy { description: "Takes more than a bullet to put you down." } @perk beguile { description: "One look into your eyes and they're putty in your hands." cost: 2 } @skill puzzling { description: "Figuring out how the clues fit together." ... } @skill gunplay { description: "Shooting straight, esp. when it matters." ... } @skill drinking { description: "Hold your liquour, yes sir!" ... } @skill fisticuffs { description: "Marquis of Queensbury be damned, hit 'em where it hurts." ... } @skill intimidate { description: "You don't actually **need** to shoot 'em." ... } @skill evade { description: "Never end up in the wrong place at the wrong time." ... } @skill fast_talk { description: "They'll think it was you doing a favour for them!" ... } @skill scheming { description: "They'll never see it coming." ... }
In a real-game we’d expect to see more definition of what skills & perks do but at least we can talk about them meaningfully even though Rez knows nothing about them. As a consequence Rez cannot validate them or their attributes.
Extra care should be taken here that they are well-formed.
Plot (Element)
Example
Required Attributes
|
Number |
from 1 to 100, higher priorities break plot deadlocks |
Optional Attributes
Event Handlers
on_init
: (plot, event = {}) ⇒ {…}
This script will be called during game initialization and before the game has started.
Relationship (Element)
The @rel
directive describes the relationship between two game elements called the source
(the element which has the relationship) and the target
(the element the source has relationship with).
A relationship is unidirectional from source to target. Where applicable use a second @rel
to describe the relationship in the opposite direction.
A relationship can be specified between any two elements with an id. The most obvious example being between one actor and another, but you could equally define relationships between actors and factions, factions and factions, or — if it makes sense in your game — factions and items (the holy grail anyone?).
Example
The @rel
element does not follow the usual element syntax. Instead it looks like this:
@rel source_id -> target_id { <attributes> } @rel #player -> #gutman_faction { affinity: -1.0 }
A relationship element isn’t assigned an id but automatically derives its id from the source and target id, in the example above the id would be rel_player_gutman_faction
.
The syntax uses the →
symbol to help understand the unidirectionality of a relationship as being from a source element upon a target element.
The getRelationship(source, target)
API on the RezGame
object is a short-
hand for doing this lookup manually.
We can use @rel
to define all kinds of relationships:
%% the Gutman faction loves the Falcon @declare falcon @rel #gutman_faction -> #falcon { affinity: 1.0 } %% the player hates brocolli @declare brocolli @rel #player -> #brocolli { affinity: -1.0 }
In these examples we have used an affinity:
attribute (range: -1.0 to +1.0) to define the strength of the relationship but you can use any attributes you like. The following would be equally valid:
@rel #player -> #miss_wannalee { love: 65 suspicion: 25 }
An alternative approach is to use tags:
@rel #player -> #miss_wannalee { tags: #{:lover :suspicious} }
Required Attributes
Optional Attributes
Event Handlers
on_init
on_init: (relationship, event) => {...}
event = {}
on_change_affinity
Scene (Element)
A Game in Rez is authored in terms of @scene`s and `@card`s. Each `@card
represents some content that is presented to the player. By contrast the @scene
represent the structure and intelligence about which `@card`s to represent and how to respond to player input.
If you are familiar with Twine then a @card
is roughly equivalent to a Twine passage. A Twine game is one long stream of passages woven together. Rez differs from Twine in that it uses the @scene
to organise how the player interacts with the game and which/how the content is presented.
For example you might use different scenes for moving around the map, examining items, interacting with NPCs, buying from shops, and so on. You don’t have to, you could implement the game in a single scene, but the different layout and event handling possibilities make it easier.
A @scene
requires an initial_card: #card_ref
attribute that identifies the card that will be rendered when the scene begins. Additionally it requires a layout:
attribute that specifies the surrounding markup.
Within the layout using the ${content}
template expression to specify where scene content is inserted.
A @scene
requires a layout_mode:
attribute which must be either :single
or :stack
. In the :single
layout mode only a single @card
is ever displayed. While in :stack
mode each new @card
is layed out after the previous one.
Lastly a @scene
may optionally have a blocks: [#card_id_1 #card_id_2 …]
attribute. Each referenced @card
will be rendered and it’s content can be inserted into the layout using ${card_id_1}
, ${card_id_2}
, etc.
Example
@scene introduction { title: "Introduction" initial_card: #intro_part_1 blocks: [#sidebar_1 #sidebar_2] layout_mode: :single layout: """ <div class="sidebar"> {{{sidebar_1}}} {{{sidebar_2}}} </div> <div> {{{content}}} </div> """ on_new_card: (game, evt) => {...} }
Required Attributes
|
String |
what you present to the user to tell them what scene they are in |
|
Element Ref |
id of the |
|
Keyword |
One of |
|
Template |
template containing the scene content in which cards are embedded |
Optional Attributes
|
Table |
See [Card] |
|
List |
See [Card] |
|
Boolean |
In reverse mode new cards are played at the top of the stack (default: false) |
|
String |
Markup content to be inserted between cards when in stack mode (defaults: "") |
Event Handlers
Scenes support a range of events:
on_init
: (scene, event = {}) ⇒ {…}
The on_init
script is called during game initialization and before the player has been able to take any actions. It will be passed an empty map of arguments.
on_start
: (scene, event) ⇒ {…}
event = { card_id: <id> }
The on_start
script is called when a new scene is started. It will receive a map containing the scene_id.
on_finish
: (scene, event = {}) ⇒ {…}
The on_finish
script is called when a scene has eneded.
on_interrupt
: (scene, event = {}) ⇒ {…}
The on_interrupt
script is called when a scene is being interrupted by an interlude.
on_resume
: (scene, event = {}) ⇒ {…}
The on_resume
script is called when a scene is being resumed after an interlude.
on_render
: (scene, event = {}) ⇒ {…}
The on_render
script is called every time the scene is being rendered.
on_start_card
: (scene, event) ⇒ {…}
event = { card_id: <id> }
The on_start_card
script is called when a new card is played into the scene. It will be passed a map containing the id of the card that has been played.
on_finish_card
: (scene, event) ⇒ {…}
event = { card_id: <id> }
The on_finish_card
script is called when when a card has 'finished' as a new card is being played into the scene. It will be passed the id of the card that is finished.
Script (Directive)
A script is used to include arbitrary Javascript code into the compiled game. Specify a script using the @script
directive.
The @script
directive consists of a string containing the code to include between {
and }
markers.
The code defined in the game’s @script
directives will be automatically included as <script> tags before the end of the <body> element of the generated HTML template.
Example
@script { function customFunction() { // Javascript code here } }
Slot (Element)
A @slot
describes a component of an @inventory
so that an inventory can hold different types of things.
For example an inventory representing what a player is wearing might have slots for coats, trousers, and so forth while an inventory representing a spell book might have slots for different levels of spell.
See also: [Type Hierarchy]
Example
@slot holster_slot { accepts: :pistol }
Required Attributes
|
String |
name of the slot e.g. "Holster" that could be displayed to the player |
|
Keyword |
a keyword representing the type of Items that are permitted to be in the slot |
Optional Attributes
|
Number |
the sum of the sizes of |
Event Handlers
on_init
: (slot, event = {}) ⇒ {…}
This script will be called during game initialization and before the game has started.
on_insert
: (slot, event) ⇒ {…}
event = { inventory: <id>, item: <id> }
When an @item
is placed into a @slot
the on_insert
event handler will be
called.
on_insert: (inventory_id, item_id) => { // Do something }
on_remove
: (slot, event) ⇒ {…}
event = { inventory_id: <id>, item_id: <id> }
When an @item
is taken out of an inventory @slot
the on_remove
event
handler will be called.
on_remove: (inventory_id, item_id) => { // Do something }
Styles (Directive)
The @styles
directive is used to include arbitrary CSS into the compiled game.
The styles defined within a @styles
directives will be automatically included as <style> tags before the end of the <head> element of the generated HTML template.
Example
@styles { .card { /* My custom styles here */ } }
Required Attributes
Optional Attributes
Event Handlers
API
System (Element)
The @system
element describes an author defined system that can respond to events generated in the game and modify the game world.
Systems are orthogonal to event handlers that are specific to a given event. For example, when a user clicks a link this has a specific outcome that will be meaningful to the player. However any number of systems might also respond to this event.
For example we might want to model weather in our game world and have the weather change, automatically, over time. This change is not necessarily related to any specific player activity (e.g. clicking a link to move between locations) but any event might trigger such a change.
Whenever the player generates an event all @system`s whose `enabled:
attribute is true
get the opportunity to process, and potentially modify, the event before normal processing and to change the result afterward.
Every @system
must have a priority:
attribute that is a number greater than 0
. @system`s are run in highest-priority order (so priority `100
runs before priority 99
).
Every @system
must define at least one of before-event:
or after-event:
but can potentially define both.
Example
%% Here is a system that maintains wall clock time and when an event changes %% the time, calculates new weather @system weather_system { enabled: true priority: 25 %% low-priority wall_time: 0 past_wall_time: _ %% just so we get an accessor weather: "It is sunny" before_event: (system, event) => { system.past_wall_time = system.wall_time; } after_event: (system, event, result) => { if(system.wall_time != system.past_wall_time) { system.calculate_weather(); } return result; } calculate_weather: function() { this.weather = ["It is raining", "It is sunny"].randomElement(); } }
Required Attributes
-
enabled
[Boolean]: if false, this system will not be run -
priority
[Number]: systems are run in descending priority order
Optional Attributes
Event Handlers
on_init
on_init: (system, event = {}) ⇒ {…}
This script will be called during game initialization and before the game has started.
before_event
before_event: (system, event = {}) ⇒ {…}
This handler will be called before the event has been processed by handleBrowserEvent()
. If the handler modifies the event, the modified event will be passed on to successive systems and handleBrowserEvent()
.
after_event
after_event: (system, event = {}, result) ⇒ {…}
This handler will be called after the event has been processed by handleBrowserEvent()
and receives both the event in question and also the result that has been generated.
If the handler modifies the result, the modified result will be passed back through successive systems and to the browser itself. Modifying the event does nothing as it has already been processed.
Timer (Element)
The @timer
element describes a game component that generates events after specific
time interval has passed, either once or repeatedly.
Use a timer element when you want something to happen irrespective of player input.
For example a timer could be used to create a proper "wandering monster" scenario, where every minute the player is at risk of a monster wandering into their location.
Example
… @timer wandering_monsters { auto_start: true repeat: true interval: 60000 event: :wandering_monster } …
Pre-Defined Attributes
|
Boolean |
If true, this timer will start when the game starts. |
|
Boolean |
If true this timer will keep sending events until it stops, otherwise it will only send one event. |
|
Integer |
With a repeated timer this specifies the number of times it should repeat. |
|
Keyword |
Specifies the name of the event that will be sent when the timer runs down. The event follows the normal rules for custom events. |