This page is in construction! It is marked for review/completion for version 70.
This guide covers how the Castagne Script language works (.casp files), and is a recommended read when starting out.
The language is set to have an upgrade in v0.7, with the rewrite of the compiler.
The current compiler (more like a parser) is already working overtime compared to the initial draft in v0.2, so you may find plenty of bugs and weird behavior if you try to find the edge cases.
Castagne at its core works by treating characters as state machines, where every state can represent an attack, a movement option, a hit state, or whatever else. To know what to do during each, it executes a state script, which is created in Castagne Script.
# Comment Move(200) F5-10: Attack(500, 20) Hitbox(-500, 20000, 5000, 10000) else Move(-100) endif
Castagne Script has two basic types of instructions:
FunctionName(Param1, Param2). These affect the game state and are provided by the modules, and thus are described in their respective pages.
[LETTER][CONDITION]:, which control which parts are going to be executed or not (for instance have an hitbox only on frames 5 to 10). Branches can be followed up by a
elsewhich will execute if the condition is false instead, and must end with an
It is possible to add comments by starting a line with
During a loop, Castagne goes through several phases, and each state script will be precompiled for each phase. The full list is available in the Engine Core advanced documentation page, but the most important for us are:
In most cases, as a user, all you need to think about is the action phase. The init phase is mostly contained in states starting by
Init-, and the reaction phase is handled by the base skeleton.
Some state scripts are given special treatment by the engine, and may be recognized by their names:
Characterholds basic metadata for the character. All variables entered here will be available both for the state scripts in game and menus outside of the main loop.
Variables-hold variable declarations.
Init-are meant to be executed during the Init phase and set up a new entity.
Casp files can hold several entities. One of them is the
Main entity and serves as a default one, while others are signaled by using the
It is not necessary for a state to be meant to be executed alone, and the language provides some tools to help flag them.
A file can include a
Skeleton attribute in the
Character script. This is a reference to another file that may be loaded beforehand. States from files loaded later can override those with the same name, allowing them to extend or replace behavior. By default, Castagne will load the base skeleton, but this can be stopped by setting the value to
Castagne Script is meant to be worked on in concert with the Castagne Editor, and thus can feature a few QoL features to that effect. While possible to edit Castagne Script in a different text editor, it is recommended to use the one provided by the Castagne Editor (this behavior may be supported more in the future).
The branch types are as follows:
Frame branches allows the script to execute only on certain frames.
F20:will only execute on frame 20, the first frame starting to count at 1.
F10-20:will only execute on frames 10 to 20, both included.
F10+:will execute on frames 10 (included) and up.
Variable branches check the value of a variable through a condition.
VVariableName:will execute when VariableName >= 1
VVariableName==5will only execute when VariableName is equal to 5
VVariableName>=2will execute when VariableName is superior or equal to 2
VVariableName<2will execute when VariableName is strictly inferior to 2
Lag branches check if a flag has been raised by a module or through the Flag function before continuing.
Phase branches will execute only during a specific phase. When in the true part of the branch, all functions will execute forcibly.
PFreeze:will execute only in the Freeze phase.
PReaction:will execute only in the Reaction phase, etc.
Input branches will check if an input is pressed.
IA:will check if A is held.
IAPress:will check if A is just pressed.
Sequential branches are not yet implemented.
A variable declaration follows the syntax given here:
MUT NAME TYPE() = VALUE # Example var WalkSpeed int() = 500 internal _PositionX
MUTis the mutability of the variable, and may be
internal. Variables marked
NAMEis the name of the variable. It should be unique from other variable names (except when overriding) and other internal variable names.
TYPEis the type of the variable (number, string...). You may add additional hints between the parenthesis. (not used yet)
VALUEis the default value of the variable when the character is created.
Mutability is a key part of the variable, and changes how it is used and treated by the engine.
var(Variable) is for data that can change. This is meant for operations and more complex behaviors.
def(Define) is for constant data. This is meant for parameters of the character (like walk speed, or max health).
internalallows access to internal variables of the engine, provided by the modules. By convention, these start with
_. This is meant for complex behavior that requires internal engine data, and modifying them might result in weird behavior.
Type is the other key part of the variable, and can be either one of:
str: Strings of characters
Variables can be declared in variable declaration blocks. These are defined by their name stating with
Variables-, or just being
Variables (referred to as the main variable block), in order to help with organization. Variables can't be declared in two different blocks.
Variables of mutability
def have some specific behaviors:
CallParentcalls. As an example, state A has define
d = 5, state B has define
d = 10, and state B calls state A. If state A is executed, d will be equal to 5 for its code, but when called from inside B, d will be equal to 10. This can allow smart reuse of code.
Variables can be set using the
Set family of functions of the Core module.
Each state may hold a reference to another file, called the skeleton. This allows a character to start from a common base to other characters, and as such reduce development time. This is also very useful for system mechanics
Skeleton is set in the
Character block and can be either of those values:
res://): This will load the specific file as a skeleton
base: This will load the base skeleton
none: This won't load any skeleton.
The compiler will load every file in memory, and start compiling in reverse order, starting by the last skeleton and finishing with the main file. Files compiled later may override parts of the earlier ones as follows:
Parent:StateName(which may also push other overridden states further back), and the new one will take its place.
The skeletons may be set inside the config menu, and the base skeleton has its own documentation page for its features.
A .casp file may hold several entities at once, which is useful for projectiles for instance.
Every file has a
Main entity, which is the considered the default one in situations where no sub-entity is specified. Every entity may have its own variables and states, which are going to be used by the compiler when working it out.
Marking to which entity a specific state script belongs to is done through its name. With
ENAME as the name of the entity, the convention is as follows:
This is made easier through the entity templates in the editor.
To create a new entity, one must use
CreateEntity(ENAME) from the Core module, which will automatically look for these states.
Castagne Script in practice can be full of "gotchas", small insidious mistakes that can be easy to make and hard to spot (for instance, forgetting to call the code that handles being hit). Therefore, in order to make this easier, the editor has a few quality of life features which have some support in the language.
A lot of this support is done through the Editor module, which provides functions that are not executed but serve to flag properties for the editor. These start with
_ by convention and may be ignored for code comprehension.
Callfunction towards a state marked as a Base State (done through the
_BaseState()function). Alternatively, a state may be marked as a Helper if it is meant to be closer to a "function call", through the
_Category()function. These can be nested using
TNAMEas the template name
EntityTemplate-TNAME-Initfor the init script,
EntityTemplate-TNAME-Variablesfor the variable declaration, and
EntityTemplate-TNAME-Actionfor the default state the entity will use after initialization. Not all of them need to be filled out.
_StateFlag(), or may be added automatically based on the code itself. They can be inherited through Base States.
More of these may be found in the Editor Guide and the Editor Module documentation
Castagne Script is made to be able to be run in parallel, and as such can't access immediate parameters from other entities, nor set them immediately. Inter-entity communication functions are available in the Core Module.
It is meant to be compiled as bytecode, although this is not done at the moment. (v0.7 rewrite)
In the text file format, the states are separated by
Editing the files outside of the editor is possible, but not supported by the engine in a nice way. This may be improved in future versions but is not a priority.
The compiler does some optimizations, which are detailed in the Castagne Compiler page.