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

9.6 KiB

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.

Literals and strings

How a literal is interpreted depends on its context:

  • Text in double quotes ("...") is always treated as a STRING.
  • 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 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 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).

Calling another type-specific method (e.g. GETCFRAMEINEVENT on ANIMO) usually crashes the engine. To work around this, AidemMedia scripts use the following pattern: store the object's name in a STRING 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

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, EPISODE, and SCENE — 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) — 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, DOUBLE, INTEGER, BOOL).
  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.