Files
Patryk Gensch df6cf2f3d3
Some checks failed
docs / deploy (push) Has been cancelled
docs / build (push) Has been cancelled
Added part of docs
2026-05-19 20:51:59 +02:00

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.