281 lines
9.6 KiB
Markdown
281 lines
9.6 KiB
Markdown
# Scripts
|
|
|
|
The Piklib/BlooMoo engine drives game logic by interpreting text-based scripts. This chapter describes the syntax of those scripts, how the engine loads them, and the order in which objects are initialised.
|
|
|
|
## File formats
|
|
|
|
Scripts are stored in files with the `.CNV`, `.DEF`, `.CLASS`, and `.SEQ` extensions. They all share the same basic textual structure and differ only in their intended use.
|
|
|
|
The engine treats all script content as uppercase and is case-insensitive. By convention, scripts are written in uppercase.
|
|
|
|
### Encryption
|
|
|
|
Files shipped with the game are encrypted by default using a transposition cipher with a variable offset. An encrypted file begins with a header of the form:
|
|
|
|
```
|
|
{<X:N>}
|
|
```
|
|
|
|
where `X` is a letter indicating the direction of the offset (`D` means a negative offset) and `N` is the offset value. The engine detects this header automatically and decrypts the rest of the file before parsing. Files without this header are read as plain text.
|
|
|
|
## Object declaration
|
|
|
|
An object begins with a line containing the `OBJECT` keyword:
|
|
|
|
```
|
|
OBJECT=OBJECT_NAME
|
|
```
|
|
|
|
Lines between consecutive `OBJECT=` lines define properties of the current object. The definition lasts until the end of the file or the next `OBJECT=` line.
|
|
|
|
If the same object is declared more than once in a single file, its properties are merged — later entries override earlier ones.
|
|
|
|
## Object properties
|
|
|
|
Properties are written as `objectName:property=value`:
|
|
|
|
```
|
|
OBJECT_NAME:PROPERTY=VALUE
|
|
```
|
|
|
|
Signals can take an additional parameter after a caret (`^`):
|
|
|
|
```
|
|
OBJECT_NAME:ONBRUTALCHANGED^3=PROCEDURE_NAME
|
|
```
|
|
|
|
In both cases, the engine accepts both `KEY=VALUE` and `KEY = VALUE` (with spaces around the equals sign).
|
|
|
|
## Variable type
|
|
|
|
The type is essential — without it, the engine does not know how to handle the object, and the result is usually a hard crash. The type is declared with the `TYPE` property:
|
|
|
|
```
|
|
OBJECT_NAME:TYPE=STRING
|
|
```
|
|
|
|
The full list of available types is in the [Type reference](../reference/index.md).
|
|
|
|
## Literals and strings
|
|
|
|
How a literal is interpreted depends on its context:
|
|
|
|
- Text in double quotes (`"..."`) is always treated as a [`STRING`](../reference/STRING.md).
|
|
- Unquoted text is first checked against the names of existing variables — if a variable with that name exists, its value is used. Otherwise the text is taken literally.
|
|
|
|
Floating-point numbers accept both standard notation (`1.234`) and scientific notation with the letter `e` or `d` (`1.23e4`, `1.23d4`).
|
|
|
|
## Code blocks
|
|
|
|
Code blocks — used as the value of a signal or as the body of a procedure — are written inside curly braces. Statements are separated by semicolons; the final statement must also end with a semicolon, otherwise it may not be executed.
|
|
|
|
```
|
|
OBJECT_NAME:ONCHANGED={VARIABLE2^PLAY("TADA");}
|
|
```
|
|
|
|
The entire code block must fit on a single line — the engine does not support multi-line blocks directly inside a script file.
|
|
|
|
## Comments
|
|
|
|
The engine recognises two forms of comments:
|
|
|
|
- **Line comment** — a line starting with `#` is skipped entirely.
|
|
- **Statement comment** — a single statement preceded by `!` is treated as commented out, up to the next semicolon.
|
|
|
|
## Method calls
|
|
|
|
Methods are called using the caret (`^`):
|
|
|
|
```
|
|
OBJECT_NAME^METHOD(arg1, arg2);
|
|
```
|
|
|
|
## Arithmetic expressions
|
|
|
|
Computational expressions are written inside square brackets:
|
|
|
|
```
|
|
VARIABLE_NAME^SET([VARIABLE_NAME^GET()+"2"]);
|
|
```
|
|
|
|
Operators and typing rules are described in the [Arithmetic](arithmetic.md) chapter.
|
|
|
|
## String pointers
|
|
|
|
A `*` before a variable name or an expression means that the value is to be used as the name of another variable. This allows dynamic references built from text:
|
|
|
|
```
|
|
*VARIABLE_NAME^PLAY();
|
|
*["ANIMO_"+_I_]^PLAY();
|
|
```
|
|
|
|
In the first form, `VARIABLE_NAME` should be a [`STRING`](../reference/STRING.md) containing the actual object's name. In the second, the object name is constructed from an arithmetic expression.
|
|
|
|
## Procedure arguments
|
|
|
|
Inside the body of a procedure, arguments are accessed with a dollar sign followed by a number (numbered from `1`):
|
|
|
|
```
|
|
PROCEDURE:CODE={VARIABLE_NAME^SET($1);}
|
|
```
|
|
|
|
## The THIS variable
|
|
|
|
Inside a block that handles a signal, an implicit `THIS` variable is available, set to a reference to the object that fired the signal. `THIS` is also accessible from procedures called from within such a block.
|
|
|
|
`THIS` behaves unusually: calling `GETNAME` on it returns the string `"temp"`, suggesting that under the hood it is a temporary wrapper. The following work reliably on `THIS`:
|
|
|
|
- `GET` and `SET` for primitive types,
|
|
- `SHOW`, `HIDE`, `PLAY`, `PAUSE`, `STOP`, and `RESUME` for graphical objects ([`ANIMO`](../reference/index.md)).
|
|
|
|
Calling another type-specific method (e.g. `GETCFRAMEINEVENT` on [`ANIMO`](../reference/index.md)) usually crashes the engine. To work around this, AidemMedia scripts use the following pattern: store the object's name in a [`STRING`](../reference/STRING.md) variable first, then call `^RUN(string_variable, method_name)`, which internally resolves the string pointer to the actual object.
|
|
|
|
## Loops
|
|
|
|
### @LOOP
|
|
|
|
```
|
|
@LOOP(BEHAVIOUR code, INTEGER start, INTEGER delta, INTEGER increment)
|
|
```
|
|
|
|
Executes `code` for counter values `_I_` in the range `[start, start + delta)` with step `increment`. In pseudocode:
|
|
|
|
```
|
|
for (int _I_ = start; _I_ < start + delta; _I_ += increment) {
|
|
code;
|
|
}
|
|
```
|
|
|
|
### @FOR (BlooMoo)
|
|
|
|
```
|
|
@FOR(INTEGER counter, BEHAVIOUR code, INTEGER start, INTEGER delta, INTEGER increment)
|
|
```
|
|
|
|
Identical to `@LOOP`, except that the first argument selects a custom variable to act as the counter instead of the default `_I_`.
|
|
|
|
### @WHILE
|
|
|
|
```
|
|
@WHILE(mixed value1, STRING comparator, mixed value2, BEHAVIOUR code)
|
|
```
|
|
|
|
Executes `code` as long as the condition `value1 comparator value2` holds. The list of comparators is described below in [Conditional](#conditional).
|
|
|
|
## Conditional
|
|
|
|
The engine provides two forms of `@IF`.
|
|
|
|
### Simple condition
|
|
|
|
```
|
|
@IF(mixed value1, STRING comparator, mixed value2, BEHAVIOUR codeTrue, BEHAVIOUR codeFalse)
|
|
```
|
|
|
|
Available comparators:
|
|
|
|
| Comparator | Meaning |
|
|
|---|---|
|
|
| `_` | equal to |
|
|
| `!_` | not equal to |
|
|
| `<` | less than |
|
|
| `<_` | less than or equal |
|
|
| `>` | greater than |
|
|
| `>_` | greater than or equal |
|
|
|
|
### Compound condition
|
|
|
|
```
|
|
@IF(STRING condition, BEHAVIOUR codeTrue, BEHAVIOUR codeFalse)
|
|
```
|
|
|
|
Compound conditions add logical operators:
|
|
|
|
- `&&` — conjunction (and)
|
|
- `||` — disjunction (or)
|
|
|
|
In a compound condition, the equals sign is written as an apostrophe (`'`) rather than an underscore (`_`):
|
|
|
|
| Comparator | Meaning |
|
|
|---|---|
|
|
| `'` | equal to |
|
|
| `!'` | not equal to |
|
|
| `<` | less than |
|
|
| `<'` | less than or equal |
|
|
| `>` | greater than |
|
|
| `>'` | greater than or equal |
|
|
|
|
## Dynamic variable creation
|
|
|
|
Variables can be created on the fly inside a code block:
|
|
|
|
```
|
|
@INT(STRING name, INTEGER value)
|
|
@DOUBLE(STRING name, DOUBLE value)
|
|
@STRING(STRING name, STRING value)
|
|
@BOOL(STRING name, BOOL value)
|
|
```
|
|
|
|
Each statement creates a variable of the matching type with the given name and initial value.
|
|
|
|
## Jump operators
|
|
|
|
Inside loops and procedures, control flow can be redirected with:
|
|
|
|
- `@CONTINUE()` — skips the remaining statements in the current loop iteration and moves to the next one.
|
|
- `@BREAK()` — aborts the entire call tree started by the current signal or invocation.
|
|
- `@ONEBREAK()` — aborts the current procedure only.
|
|
- `@RETURN(mixed value)` — sets the value returned by the procedure but does not stop it from executing.
|
|
|
|
## Script loading order
|
|
|
|
Scripts in the engine are organised hierarchically: scripts at lower levels can see variables from their own scope and from all ancestors, but not the other way around.
|
|
|
|
### Entry point
|
|
|
|
The engine starts from `Application.def` in the `dane` subdirectory. This file defines objects of types [`APPLICATION`](../reference/index.md), [`EPISODE`](../reference/index.md), and [`SCENE`](../reference/index.md) — other types in this file are ignored.
|
|
|
|
Example contents:
|
|
|
|
```
|
|
OBJECT=GAME
|
|
GAME:TYPE=APPLICATION
|
|
GAME:PATH=GAME
|
|
GAME:EPISODES=PRZYGODA
|
|
GAME:STARTWITH=PRZYGODA
|
|
|
|
OBJECT=PRZYGODA
|
|
PRZYGODA:TYPE=EPISODE
|
|
PRZYGODA:PATH=GAME\PRZYGODA
|
|
PRZYGODA:SCENES=START,CREDITS,LEBIODKA
|
|
PRZYGODA:STARTWITH=START
|
|
|
|
OBJECT=START
|
|
START:TYPE=SCENE
|
|
START:PATH=GAME\PRZYGODA\START
|
|
```
|
|
|
|
### Loading subsequent files
|
|
|
|
After `Application.def` is read, the engine loads a `.CNV` file for each defined object. The file path is built from the object's `PATH` attribute (relative to the `dane` directory), the object's name, and the `.CNV` extension. If the file does not exist, loading is silently skipped.
|
|
|
|
The loading order is:
|
|
|
|
1. The file bound to the `APPLICATION` object.
|
|
2. The file of the first episode (`STARTWITH` attribute of `APPLICATION`).
|
|
3. The file of the first scene of that episode (`STARTWITH` attribute of `EPISODE`).
|
|
|
|
When locating files, the engine also takes the currently selected language into account (see [`APPLICATION.SETLANGUAGE`](../reference/APPLICATION.md#setlanguage)) — the chosen language identifier points to a subfolder of localised assets that is consulted while loading game files.
|
|
|
|
### Variable initialisation
|
|
|
|
Within each file, variables are created and initialised in a fixed order by type:
|
|
|
|
1. Procedures.
|
|
2. Primitive types ([`STRING`](../reference/STRING.md), [`DOUBLE`](../reference/DOUBLE.md), [`INTEGER`](../reference/INTEGER.md), [`BOOL`](../reference/BOOL.md)).
|
|
3. Arrays and conditions.
|
|
4. Animations, images, sounds, and fonts.
|
|
5. Buttons, text fields, sequences, mouse, keyboard, canvas observer.
|
|
|
|
For each variable in this phase the `ONINIT` signal is fired. Once all variables are initialised, the engine calls the `__ONINIT__` procedure if one is defined.
|