Riven scripts
Riven | |||
Mohawk | Overview | ||
BLST | CARD | FLST | HSPT |
MLST | NAME | PLST | RMAP |
SFXE | SLST | tBMP | tMOV |
tWAV | VARS | VERS | ZIPS |
Scripts | Variables | ||
External commands |
This document talks about the Riven scripting protocol.
Contents
- 1 Overview and main data structures
- 2 Command 1: draw tBMP resource
- 3 Command 2: go to card
- 4 Command 3: play tWAV resource mix
- 5 Command 4: play local tWAV resource
- 6 Command 7: set variable value
- 7 Command 8: conditional branch
- 8 Command 9: enable hotspot
- 9 Command 10: disable hotspot
- 10 Command 12
- 11 Command 13: set mouse cursor
- 12 Command 14: pause script execution
- 13 Command 17: call external command
- 14 Command 18: transition
- 15 Command 19: reload card
- 16 Command 20: disable screen update
- 17 Command 21: enable screen update
- 18 Command 24: increment variable
- 19 Command 27: go to stack
- 20 Command 28
- 21 Command 29
- 22 Command 31
- 23 Command 32: play foreground movie
- 24 Command 33: play background movie
- 25 Command 34
- 26 Command 36
- 27 Command 37
- 28 Command 38
- 29 Command 39: activate PLST record
- 30 Command 40: activate SLST record
- 31 Command 41
- 32 Command 43: activate BLST record
- 33 Command 44: activate FLST record
- 34 Command 45: do zip mode
- 35 Command 46: activate MLST record
Overview and main data structures
The Riven engine is programmed using scripts attached to objects. The architecture resembles the HyperCard philosophy. A script is subdivided in what I call handlers; each handler is associated to an event which may happen to the script's object and it contains the actions to be executed when that event happens. The actions are described by a list of commands. Cards and hotspots are the only entities with attached scripts.
A script is represented by this data block:
unsigned short | handler_count |
variable size | handler list |
Each handler is made of this block:
unsigned short | event_type |
unsigned short | cmd_count |
variable size | command list |
The following table gives event types identified until now. Types 0 ... 5 are reserved to hotspots, 6 ... 10 are reserved to cards.
event_type | What happened |
---|---|
0 | Mouse button pressed inside the hotspot rect |
1 | Mouse button pressed inside the hotspot rect (never seen in the game) |
2 | Mouse button released inside the hotspot rect |
3 | Mouse moved, pressed or released inside the hotspot rect (never seen in the game) |
4 | Mouse held inside the hotspot rect (the event is sent repeatedly) |
5 | Mouse moved inside the hotspot rect (not sure) |
6 | Card "loaded". This is the first event sent to a card. Usually cards enable things here. Screen updates are disabled during the execution of this handler, and the display is updated at the end. |
7 | Leaving card. |
9 | Card opened. This is sent at the end, when the card has been loaded and the display has been updated. Usually cards start movies and ambient sounds here. |
10 | Display being updated; for example this event is triggered when the handler for event 6 completes, or by command 21. The handler is executed before actually updating the display. |
In general, each command is an opcode followed by a number of arguments. The same command can be passed a variable number of arguments, though only a few commands actually behave like that. The general structure is:
unsigned short | cmd |
unsigned short | arg_count |
unsigned short | args[arg_count] |
The following subsections explain what I've discovered about each command. Unlisted opcodes have never been seen in Riven.
Command 1: draw tBMP resource
- Argument count: 9
- Arguments: tbmp_id left top right bottom u0 u1 u2 u3
Draw bitmap from tBMP resource tbmp_id inside the rectangle described by left, top, right, bottom. If the rectangle dimensions do not match the bitmap size, the bitmap will be attached to the top-left corner and clipped to the rectangle. Other arguments are always zero and seem ignored by the engine.
The command will automatically update the display unless command 20 was used before.
Command 2: go to card
- Argument count: 1
- Arguments: id
Go to card id. In most cases this command is used in very short hotspot scripts containing just a transition command followed by the "go to card" one. In these cases, the following event chain takes place:
- the old card receives event 7;
- the new card receives event 6;
- the new card receives event 10 (because the display is begin updated);
- the transition effect is played and the new card image is displayed;
- the new card receives event 9.
However, it is also used in card event handlers, like 9 or 10. Sometimes it appears multiple times in the same script. The event sequence for these complicated cases is not yet fully understood.
Command 3: play tWAV resource mix
- Argument count: variable
- Arguments: N ids[N] fade_flags loop volume u0 u1 volumes[N] balances[N] u2[N]
Play a mix of N tWAV sounds from the associated sounds Mohawk archive. The argument list is structured like an SLST resource record. This command stops previously active ambient sounds.
Command 4: play local tWAV resource
- Argument count: 3
- Arguments: id volume u1
Play tWAV resource id from the Mohawk archive containing the script. volume seems always 256, u1 always 0. Ambient sounds are not touched; the sound is mixed over them.
Command 7: set variable value
- Argument count: 2
- Arguments: var value
Set variable var to value value.
Command 8: conditional branch
This doesn't follow the command arg_count args scheme. It's similar to a C "switch" statement: given a variable, there is a list of possible variable values and a corresponding command list to be executed. The structure of command 8 is:
unsigned short | 8 |
unsigned short | 2 |
unsigned short | var |
unsigned short | value_count |
Then value_count blocks follow, each with this structure:
unsigned short | value |
unsigned short | command_count |
commands to execute if var contains value |
If value is 0xffff, the block is the "default" block exactly like in the C statement. As an example, let's compare the script syntax with a C-like switch statement:
0008 0002 beef 3 0001 0001 a 0002 0001 b ffff 0002 c d |
switch (variable 0xbeef) { case 1: a; break; case 2: b; break; default: c; d; break; } |
The conditional branch command is used on levers, switches and every place where a variable value makes the difference. It's often used as a simple if-then-else mechanism.
Command 9: enable hotspot
- Argument count: 1
- Arguments: hotspot_id
Enable card hotspot with the specified hotspot_id.
Command 10: disable hotspot
- Argument count: 1
- Arguments: hotspot_id
Disable card hotspot with the specified hotspot_id. A disabled hotspot will not respond to events.
Command 12
- Argument count: 1
- Arguments: u0
Unknown. u0 is 0, 1 or 2. This command is used only a few times, for example near "go to stack" commands like in the "play riven" button. It doesn't seem related to anything special (e.g. movies or sounds). Setting u0 to some other value or replacing the command with others doesn't produce readily observable effects. I suspect it has something to do with variables, but I have yet to realize how to check this.
Command 13: set mouse cursor
- Argument count: 1
- Arguments: cursor
Set mouse cursor icon to cursor, whose meaning is given by the picture to the left.
Command 14: pause script execution
- Argument count: 2
- Arguments: ms u0
Pause script execution for ms milliseconds. ms is signed and the pause is effective only if ms > 0. u0 is always 0 and its value seems ignored by the engine.
Command 17: call external command
- Argument count: variable, at least 2
- Arguments: cmd count arg1 arg2 ...
Call an external piece of custom code ("external command"), which is probably hardcoded inside the Riven executable. cmd tells which external command to call, and count tells how many arguments to pass to it. Then count arguments follow. NAME resource 3 contains the name of each external command: cmd is the corresponding NAME record index. See the external command list for more info on external commands.
Command 18: transition
- Argument count: 1 or 5
- Arguments: code [left top right bottom]
Schedule a transition effect described by code (see below for what code means). The transition will actually play when a display update occurs, for example with command 21, 39 or after going to another card (it doesn't seem to work for command 1, however). The optional 4 args left, top, right, and bottom specify a clip rectangle in pixels, but are never really used in the game.
code | Effect |
---|---|
0...15 | Scroll. Bits 0 and 1 control the direction (0=left, 1=right, 2=top, 3=bottom); bit 2 tells if the new image should move (1) or stay fixed (0); bit 3 tells if the old image should move or stay fixed. |
16 | Dissolve. |
17 | Seems to behave like 16, rare (t_Data, CARD 155). |
Note that Riven uses only transitions from 12 to 17; other effects were discovered by hacking scripts. Maybe there are more.
Command 19: reload card
- Argument count: 0
It's something like "refresh card": it acts like a "go to this card" command. Often used to update the card display after an hotspot script has altered a variable.
Command 20: disable screen update
- Argument count: 0
This seems to disable the automatic display update for commands 1 and 39. It doesn't work like a "toggle" command: using it multiple times won't re-enable the automatic update (it is re-enabled by command 21 instead). Its effect persists even after the script ends (extending to any successive script, e.g. hotspot ones) but is reset on card switches. Command 20 often precedes command 18.
Command 21: enable screen update
- Argument count: 0
This seems the opposite of command 20: it re-enables the automatic display update for commands 1 and 39, triggers event 10, updates the display and plays the transition effect if one was scheduled before.
Command 21 shows inconsistent behavior if used alone (sometimes it updates the display, sometimes it doesn't, sometimes it skips the transition, etc.); the correct way of using commands 20 and 21 seems to be the following construct, which is also what is usually found in scripts: disable screen update schedule transition activate PLST records / draw tBMP resources enable screen update
This construct will compose a final picture (by overlaying the specified PLST records and tBMP bitmaps, in the same order as they appear in the script) and then play the specified transition effect from the previously displayed picture to the new one. Note that such a complex effect wouldn't be possible without commands 20/21.
Command 24: increment variable
- Argument count: 2
- Arguments: var value
Add value to variable var.
Command 27: go to stack
- Argument count: 3
- Arguments: stack_name code_hi code_lo
Go to another stack. The destination stack is specified by stack_name; you get the actual stack name string by looking up record stack_name in NAME resource 5. The destination card is specified through code_hi (higher 16 bytes) and code_lo (lower 16 bytes) which together form an unsigned long RMAP card code.
Command 28
- Argument count: 1
- Arguments: code
Unknown. It should have something to do with movies: it comes almost always after command 32 with the same argument. However, changing code or even removing the command itself seem to have no effect neither on the movie nor on anything else. Maybe it purges the memory used by the movie or something like that.
Command 29
- Argument count: 0
Unknown. Seems related to movies since in most cases it comes after command 32. Used quite rarely.
Command 31
- Argument count: 1
- Arguments: code
Unknown. Should have something to do with movies, it comes often before command 33 with the same argument. Changing code seems to have no effect.
Command 32: play foreground movie
- Argument count: 1
- Arguments: code
Play a movie listed in the MLST resource, blocking the script execution until movie ends. code matches the code field of the MLST record that should be played. The MLST record must be first enabled with command 46, otherwise some other random movie will play instead (or nothing will happen at all). If multiple records have that code value, one will be chosen randomly, I guess. Sometimes this command follows command 33 with the same parameter: probably it just means "wait until the movie ends" in that case.
Command 33: play background movie
- Argument count: 1
- Arguments: code
Start a "background" movie from MLST record with specified code. Script execution will go on (and eventually terminate) while the movie is playing. Often this command follows command 31. The MLST record must be first enabled with command 46, otherwise some other random movie will play instead.
Command 34
- Argument count: 1
- Arguments: u0
Unknown. u0 is always 1.
Command 36
- Argument count: 0
Unknown.
Command 37
- Argument count: 0
Unknown.
Command 38
- Argument count: 5
- Arguments: u1 u2 u3 u4 u5
Unknown, but should have something to do with movies. u1 seems to refer to a MLST record code, u5 seems to be a SLST record index and u4 seems always 40. u2 and u3 seem to form a 32-bit value set to large numbers (4000, 9000...), which I suspect are milliseconds.
This command seems to perform complex actions: if u4 is changed to 0, u5 is interpreted instead as a tBMP resource ID and that bitmap is drawn right after the movie.
Command 39: activate PLST record
- Argument count: 1
- Arguments: record
Activate the specified record in the PLST resource, making one of the card bitmaps visible. The actual screen update takes place immediately (with a transition effect, if one was scheduled before) unless command 20 was used before or the command is called from handlers 6 or 10.
Command 40: activate SLST record
- Argument count: 1
- Arguments: record
Activate the specified SLST record, so start ambient sounds. This command seems to have effect just once: it won't restart the same sound if it is already playing (even if the SLST tells not to repeat it). Activating another SLST record works always: the current sound is then stopped and replaced with the new one.
Command 41
- Argument count: 1
- Arguments: u0
Unknown. Should have something to do with movies because it comes often before command 32 with the same argument. A hypothesis is that it activates a MLST record like 46, but using its code field rather than the MLST record index.
Command 43: activate BLST record
- Argument count: 1
- Arguments: record
Activate the specified record in the BLST resource, so enable or disable one of the card hotspots (depending on the record info).
Command 44: activate FLST record
- Argument count: 1
- Arguments: record
Activate the specified record in the FLST resource, so enable a realtime SFXE effect.
Command 45: do zip mode
- Argument count: 0
Find the correct ZIPS record using the hotspot name, get the destination card from the found record and go to that card. Though likely, this behavior has not been confirmed yet.
Command 46: activate MLST record
- Argument count: 2
- Arguments: record u0
Activate the specified record in the MLST resource. This command won't actually start the movie; it will only "enable" it for later playing. u0 seems 0 or 1 if the movie is to be played once or looped forever, respectively. This is redundant, since the same info can be found in the MLST record; moreover, u0 seems to be ignored by the engine.