Lua API for OpenITG / NotITG

Basics of XML/Lua

Introduction

The Lua API is supported in OpenITG / NotITG through XML. With this, you can easily create "modded simfiles", by manipulating elements, adding images, and much more.

"Elements" are called "Actors". An Actor can be a rectangle, a picture, some text, or even 3D objects, the player, or the screen itself ! In your code, you will manipulate existing Actors and also create some. You can control almost everything in lua, but NotITG provides even more tools and some new Actors to make beautiful simfiles !


ITGGetting started

First, DO NOT CODE WITH WINDOWS NOTEPAD ! This software doesn't help you at all to code something. You can't indent, the text isn't colorized, etc. You should use a better text editor. I'd personally recommend Sublime Text because there are really useful features and shortcuts, but there are also other softwares, such as Notepad++ or Visual Studio Code. If you love command-line prompts, you can also use vim or emacs !

I made syntax highlighting files for the XML/Lua and CRS files, for Sublime Text 3. You can find them at the homepage of this website.

Let's assume your simfile looks like that :

You probably don't have the "lua" folder. Just create it, and create an empty text document called "default.xml".

To include an XML file in the foreground (a file that will do mods, and add elements at front of playfields), replace your #FGCHANGES line by :

#FGCHANGES:0.000=lua=1.000=0=0=1=====;

To make short, this line will include the lua/default.xml file at the very beginning of the song. Ignore the other arguments for now.

To add one, but in the background (a file that won't do any mod, but that will add some elements in the background), replace your #BGCHANGES line with this one :

#BGCHANGES:0.000=bg=1.000=0=0=1=====;

Here, the file located in bg/default.xml will be loaded. Please note that BGCHANGES doesn't support InitCommands, Messages, and Models. See NotITG#BETTERBGCHANGES to use a better alternative of these, if you really need those missing features.

The XML file should look like this :

<ActorFrame><children>

    <Layer
        Type="Quad"
        InitCommand="hidden,1;sleep,9999"
    />

    <!-- You will add elements there -->

</children></ActorFrame>

If you already did XML or any SGML-based language (for example, HTML), you will be familiar with this syntax.

Let's explain this code :

  • A XML file must "give" only one Actor to the game. How could we do that ? An ActorFrame is an Actor that contains Actors. Here we're defining an ActorFrame by using the "ActorFrame" tag.
  • The "children" tag is needed for Actors that contains other Actors.
  • Here, we have an Actor of type Quad (a rectangle). We will hide it because we don't need to show it to the player, and we will apply a "sleep" effect for 9999 seconds. Why ? Because the game will stop any XML file that isn't doing anything. Adding this Actor fixes this behavior because the file will always do something. We are using a Quad here because it's the lightest Actor (we don't need a picture or a text). It is only needed in a foreground file (imported with #FGCHANGES). You don't have to put this Quad on a background file (imported with #BGCHANGES, or #BETTERBGCHANGES).
  • Here, we're just closing the tags we opened before.

Now, you know how to add Actors to your file, but... We aren't doing any Lua until now. We will put our main code into... An Actor ! Now our file looks like this :

<ActorFrame><children>

    <Layer
        Type="Quad"
        InitCommand="hidden,1;sleep,9999"
    />

    <Layer
        Type="Quad"
        InitCommand="hidden,1"
        OnCommand="%function(self)
            SCREENMAN:SystemMessage('Hello world !');
        end
"
    />

</children></ActorFrame>

Again, let's explain what we added :

  • We added another Quad in our ActorFrame, also hidden.
  • Inside the OnCommand, there is some lua ! We're actually just showing "Hello world !" to the screen.

Let's talk now quickly about lua : Lua is the scripting language used in OpenITG / NotITG. It allows us to manipulate elements.

Let's add a little picture in the game, for example, the OpenITG icon. Download this icon, and put it in the lua folder.

Now to show it in game, we will add this to our ActorFrame (I won't re-write the entire code, you know how to add elements inside the ActorFrame :P) :

<Layer
    File="itg.png"
/>

Yaay, now we have an icon, but it is located in the top left corner. Let's center it now :

<Layer
    File="itg.png"
    InitCommand="%function(self)
        self:x(SCREEN_CENTER_X);
        self:y(SCREEN_CENTER_Y);
    end
"
/>

Yay, it is centered now ! Let's explain what happened :

  • self is the keyword to target the current Actor. Here it targets the picture.
  • We're telling self to move in the horizontal (X) axis to the center of the screen. We're doing the same for the vertical (Y) axis.
  • SCREEN_CENTER_X and SCREEN_CENTER_Y are "constants". They represents here a number that we will give to the {{Actor_x}} method. If you're curious, check SCREEN_CENTER_X and SCREEN_CENTER_Y on the constants section !

For more explanations about commands (InitCommands, OnCommands, and more), go here !

Let's now try to draw a green rectangle of 300x200 pixels !

<Layer
    Type="Quad"
    InitCommand="%function(self)
        self:x(SCREEN_CENTER_X); -- Centers horizontally the rectangle
        self:y(SCREEN_CENTER_Y); -- Centers vertically the rectangle
        self:zoomto(300, 200); -- Defines its size to 300x200
        self:diffuse(0, 1, 0, 1); -- Colors it in green (0% red, 100% green, 0% blue, 100% opaque)
    end
"
/>

Easy, right ? If you have questions regarding these new methods, see {{Actor_zoomto}} and {{Actor_diffuse}} (Don't forget that the main purpose of this webpage was to provide every method available :P).

To learn further with video guides, watch the tutorial videos made by Puurokulho ! To learn Lua, read the official Lua documentation. (Keep in mind that OpenITG and NotITG are using Lua 5.0). If you're "good" in lua modding, and want to learn further, watch these advanced tutorials by WinDEU !


ITGActor Commands

Actor Commands are just instructions that will be executed in Lua. There are 2 "magic" commands that are executed at a specific moment :

  • InitCommand is a command that will be executed as soon as possible. It is useful to manipulate an Actor (for example, moving it and resizing it), but please avoid to access to other elements, because the command is executed so early that other elements may not be accessible (And will crash the game with an "Access Violation" error). They don't work in background (file imported with #BGCHANGES) !
  • OnCommand is a command that will be executed when everything will be ready. You can do everything there, without limitations.

You can call commands when you want with {{Actor_playcommand}} and {{Actor_queuecommand}}. {{Actor_playcommand}} will instantly call the specified command. {{Actor_queuecommand}} will call it when it is possible. You can combine it with {{Actor_sleep}} to do things like :

self:sleep(2); -- Wait 2 seconds ...
self:queuecommand('Hello'); -- before calling 'HelloCommand'.

Even if you want to play a command instantly, using {{Actor_queuecommand}} is strongly recommended, because it can cause issues that may crash the game.

Often, in a modded simfile, we want a command that will be executed at each frame. We do it this way :

<Layer
    Type="Quad"
    OnCommand="queuecommand,Update"
    UpdateCommand="%function(self)
        SCREENMAN:SystemMessage('Hello world !');
        self:sleep(0.02); -- Waits 20ms (= 50fps)
        self:queuecommand('Update'); -- Repeats the command.
    end
"
/>

What would be the purpose of having an "UpdateCommand" ? If you need to do specific things at a specific moment (like, zooming a rectangle at beat 36 to make it 2+currentSecond/100 times bigger, you can't do that in an OnCommand. You will use an if statement, but it would be checked only once in an OnCommand.

But what is that OnCommand="queuecommand,Update" ? Where is the %function(self) ? Well, there is 2 ways of writing a same command :

<Layer
    Type="Quad"
    OnCommand="queuecommand,Update"
/>

<!-- is equivalent to -->

<Layer
    Type="Quad"
    OnCommand="%function(self)
        self:queuecommand('Update');
    end
"
/>

This way, writing :

self:x(SCREEN_CENTER_X);
self:y(SCREEN_CENTER_Y);
self:zoomto(300, 200);
self:diffuse(0, 1, 0, 1);

Is totally equivalent to x,SCREEN_CENTER_X;y,SCREEN_CENTER_Y;zoomto,300,200;diffuse,0,1,0,1 ! This notation allows us to execute methods to self. (You can't execute something that isn't related to self, like SCREENMAN:SystemMessage() !)

In NotITG only, you can use {{Actor_cmd}} to execute commands with that notation, inside a lua function, like :

self:cmd('x,SCREEN_CENTER_X;y,SCREEN_CENTER_Y;zoomto,300,200;diffuse,0,1,0,1');
SCREENMAN:SystemMessage('My command was succesfully executed ! :D');

ITGMessages

The messages are "special commands". When we will call a message, every Actor having this message registered will be executed. For example :

<Layer
    Type="Quad"
    InitCommand="x,SCREEN_CENTER_X-50;y,SCREEN_CENTER_Y;zoomto,100,100;diffuse,1,1,1,1"
    ChangeColorMessageCommand="linear,0.5;diffuse,1,0,0,1"
/>

<Layer
    Type="Quad"
    InitCommand="x,SCREEN_CENTER_X+50;y,SCREEN_CENTER_Y;zoomto,100,100;diffuse,1,1,1,1"
    ChangeColorMessageCommand="linear,0.5;diffuse,0,1,0,1"
/>

We have 2 quads there. They are both white. We will use {{MessageManager_Broadcast}} to execute a message in Lua :

MESSAGEMAN:Broadcast('ChangeColor');

The ChangeColorMessageCommand of every Actor will be called. In our example, the left quad will become red, and the right one will become green, both within a half-second. (For more explanations about {{Actor_linear}}, go just below !


ITGConditions

Actors can take a Condition attribute, that will be evaluated before the other commands. This attribute always take a lua expression. If this expression doesn't return true, the Actor won't be executed at all. It will be like "non-existent". The expression will only be evaluated once, so don't check for beat/seconds there. Also, this is evaluated very early (even before the InitCommand), so don't check anything with the screen, or the game may crash with an "Access Violation" !

Also, in OpenITG and in the first public release of NotITG, you can't put a Condition attribute in the main ActorFrame ! You need to have a normal ActorFrame, and inside it, your Actor with the Condition, or use the latest public release.

<!-- Will be shown only if the "Insane" difficulty is played by P1. -->
<Layer
    Type="Quad"
    Condition="GAMESTATE:IsPlayerEnabled(0) and GAMESTATE:GetCurrentSteps(0):GetDifficulty() == 4"
    InitCommand="x,SCREEN_CENTER_X;y,SCREEN_CENTER_Y;zoomto,100,100;diffuse,1,1,1,1"
/>

For this example, don't hesitate to check both {{GameState_IsPlayerEnabled}}, {{GameState_GetCurrentSteps}} and {{Steps_GetDifficulty}} !

Since every lua expression inside this attribute is valid, we could put a anonymous closure there ! Let's try with our main ActorFrame, for example, to check if the user is using OpenGL for the NotITGActorFrameTextures !

<ActorFrame
    Condition="(function(videoRenderers)
        if not string.find(videoRenderers, 'opengl') -- OpenGL isn't in the list
        or string.find(videoRenderers, 'opengl') > 1 then -- Or OpenGL isn't the first renderer
            SCREENMAN:SystemMessage('You need to play this chart with OpenGL !');
            return false;
        end
        return true;
    end)(string.lower(PREFSMAN:GetPreference('VideoRenderers')))
"
><children>

Reminder: You can't put a Condition in the main ActorFrame. You need to have an ActorFrame, and inside it this ActorFrame with condition, and inside it your elements.

Closures are often used in languages like JavaScript. They're a bit complex at first to understand, but are really easy to use, and useful in our case ! We can't use the usual %function(self) there, because, like I said, this attribute always take a lua expression, and "%" isn't valid in Lua (well, we can't modulo a function definition).


ITGTweens

Tweens are a way to smoothly manipulate elements. For example, let's do :

self:linear(1);
self:zoom(2);

This will linearly zooms the Actor to be 2 times bigger than before, within one second.

Every function that is called after a tween will be tweened. This way, you can tween multiple operations, for example :

self:linear(1);
self:zoom(2); -- Will go from 1 to 2 in one second.
self:addx(100); -- Will move 100px to the right in one second.
self:addy(50); -- Will move 50px to the bottom in one second.
self:rotationz(30); -- Will rotate from 30 degrees in one second.

If you want to tween only specific functions, but not some others, you can use {{Actor_sleep}} this way :

self:linear(1);
self:zoom(2); -- Will go from 1 to 2 in one second.
self:addx(100); -- Will move 100px to the right in one second.
self:addy(50); -- Will move 50px to the bottom in one second.
self:sleep(0); -- "Resets" the linear(1) for the next commands.
self:rotationz(30); -- Will rotate from 30 degrees *instantly*.

This will act like before, but when the tween will be finished, a rotation will be instantly applied.

There are 7 tweens available for now :

  • {{Actor_linear}}, the simplest. It will tween linearly.
  • {{Actor_accelerate}} will tween slowly at the beginning, and the transition will become faster until the end.
  • {{Actor_decelerate}} will tween quickly at the beginning, and the transition will become slower until the end.
  • {{Actor_spring}} will tween slowly, will become faster until the tween will be after the end, and come back to the expected end (for example, if you did self:addx(100), it will go to ~110, and come back to 100).
  • {{Actor_bouncebegin}} will bounce at the beginning, before going to the end.
  • {{Actor_bounceend}} will bounce at the end.
  • {{Actor_sleep}}. Yes, this is a tween ! It just waits before applying instantly the commands.

ITGActor Effects

An Actor Effect is an Effect that is modifying on its own the position, the size, or the colors of a specific Actor. They can be enabled using a simple method.

Here is a list of every Actor Effect for now :

  • {{Actor_bob}} : Moves the Actor up and down, like a mathematic sinus.
  • {{Actor_bounce}} : Bounces the Actor.
  • {{Actor_diffuseblink}} : Blinks the Actor between 2 colors.
  • {{Actor_diffuseramp}} : Different from diffuseblink, it goes linearly from the second color to the first one, and come instantly back to the second color.
  • {{Actor_diffuseshift}} : Like {{Actor_diffuseblink}}, but more smoothly (The colors will toggle sinusoidally).
  • {{Actor_glowblink}} : Glows instantly the Actor between its original color and a glow color.
  • {{Actor_glowshift}} : Glows smoothly the Actor between its original color and a glow color.
  • {{Actor_pulse}} : Grows and shrinks the Actor.
  • {{Actor_rainbow}} : yeaaaaaaaaaaah.
  • {{Actor_spin}} : Spins the Actor.
  • {{Actor_vibrate}} : Vibrates the Actor.
  • {{Actor_wag}} : Wags the Actor.

You can configure these effects by calling these methods :

  • {{Actor_effectcolor1}} and {{Actor_effectcolor2}} : Sets the 2 colors for {{Actor_diffuseblink}}, {{Actor_diffuseramp}} and {{Actor_diffuseshift}}.
  • {{Actor_glow}} : Sets the glow color for {{Actor_glowblink}} and {{Actor_glowshift}}.
  • {{Actor_effectclock}} : Sets the clock of the effects.
  • {{Actor_effectdelay}} : Sets the delay of the effects.
  • {{Actor_effectmagnitude}} : Sets the magnitude for each axis of the effects that are affecting the position, like {{Actor_vibrate}}.
  • {{Actor_effectoffset}} : Sets the offset of the effects.
  • {{Actor_effectperiod}} : Sets the period of the effects.
  • {{Actor_stopeffect}} : Stops the current effect(s).

You can also use {{Actor_GetSecsIntoEffect}} to get the amount of seconds an effect is running.


ITGAux variables

An aux value is a variable of type number (a float to be exact), that can be tweened. You can, for example, change the value from 0 to 360 in one second. This way, if you check this aux value at 0.5 seconds, it will be 180. You can read this number from everywhere in your code. It's usually used into mathematic formulas.

To ease our job, we will use a separate Actor (a hidden Quad, as usual). It will be used to set the aux values. It will look like this :

<Layer
    Type="Quad"
    InitCommand="%function(self)
        self:hidden(1);
        auxvar = self; -- The 'aux' variable will target this Actor.
        self:aux(0); -- The 'default value' of the aux value will be 0.
    end
"
    RotateMessageCommand="finishtweening;linear,1;aux,360"
/>
  • First, as usual, we hide the quad.
  • Then, we set the 'auxvar' variable to self. It allows us to access this Actor from another location, to get the current aux value.
  • And we set the default value of the aux var to 0. {{Actor_aux}} is used to set the aux value.
  • We defined a MessageCommand that will finish the current and queued tweens on this Actor, and set the aux value to 360 within one second.

For example, if you want to move a Quad like a circle shape (assuming the square variable represents an Actor), you'll do something in an UpdateCommand like :

square:x(SCREEN_CENTER_X + math.sin(auxvar:getaux())*200);
square:y(SCREEN_CENTER_Y + math.cos(auxvar:getaux())*-200);

Here, we use {{Actor_getaux}} to get the current aux value. Let's imagine we call the Rotate Message at the second 2 :

  • If we do getaux() at the second 2, it will return 0.
  • If we do getaux() at the second 3, it will return 360.
  • But if we do getaux() at the second 2.5, it will return 180 !

Using {{Actor_aux}} and {{Actor_getaux}} is not the only solution ! You could also have additional "aux" values by using {{Actor_x}} and {{Actor_GetX}}, {{Actor_y}} and {{Actor_GetY}}, or even {{Actor_z}} and {{Actor_GetZ}} on the same Actor ! Coordinates have the benefit of not needing a default value. Of course, you can have multiple hidden Quads that you will use to have aux values. Just don't forget to edit the auxvar = self; line to something like auxvar_2 !


NotITG Version 2#BETTERBGCHANGES

The files imported with #BGCHANGES unfortunately don't support some features, like InitCommands, MessageCommands, and including Models. The second public release of NotITG fixed that by adding the #BETTERBGCHANGES :

  • InitCommand will now work properly.
  • MessageCommands will also work.
  • It will be possible to include 3D Models.
  • Like a classic #BGCHANGES, you don't need to include a sleeping quad, like #FGCHANGES.

The syntax of the tag stays the same as the others.

#BETTERBGCHANGES:0.000=(folder)=1.000=0=0=1=====;

NotITG Version 2Tweens As Mods

The second release of NotITG includes now a way to call some commands with mods. It allows to sync perfectly these commands with the standard mods. There is a list of every "Tween As Mod" available :

  • x, y, z : 1% will move the playfield by 1 pixel.
  • rotationx, rotationy, rotationz : 1% will rotate the playfield by 1 degree.
  • zoomx, zoomy, zoomz : By default, these mods are at 100%. Doing 200% will make the playfield 2 times bigger. (Equivalent to self:zoom(2).)
  • skewx : 100% = self:skewx(1).
  • hide : Equivalent to self:hidden(1), but is called "hide" because the "hidden" mod already exists. Any non-zero percentage will enable it.

Since these are normal mods, they can be called by #MODS tags (via a CRS file, or via the SM file), or with the #ATTACKS tag in the SM file.


NotITGActorFrameTexture

The ActorFrameTexture is an Actor who captures every elements under it (drawn before it), to finally put the result into a texture. IT DOESN'T WORK WITH DIRECTX ! You can, after, put the texture onto a sprite, and apply some beautiful effects with it !

It's usually setup like this :

<ActorFrame
     InitCommand="%function(self)

        -- The multiplier of the aft sprites' alpha value.
        aftMult = 1;

        -- If the user is using a Nvidia graphic card, they have some issues with alphas of sprites.
        if tonumber(GAMESTATE:GetVersionDate()) >= 20170405 and string.find(string.lower(DISPLAY:GetVendor()), 'nvidia')
        or string.find(string.lower(PREFSMAN:GetPreference('LastSeenVideoDriver')), 'nvidia') then
            aftMult = 0.9; -- Setting the alpha multiplier to 0.9.
        end


    end"
><children>

    <!-- ... -->

    <Layer
        Type="Sprite"
        Texture="white"
        InitCommand="%function(self)

            aftSprite = self;
            self:basezoomx((SCREEN_WIDTH/DISPLAY:GetDisplayWidth()));
            self:basezoomy(-1*(SCREEN_HEIGHT/DISPLAY:GetDisplayHeight()))
            self:x(SCREEN_CENTER_X);
            self:y(SCREEN_CENTER_Y);

            -- Set this like you want
            self:zoom(1.05);
            self:diffusealpha(0.95*aftMult);

        end
"
    />

    <Layer
        Type="ActorFrameTexture"
        InitCommand="%function(self)

            aft = self;
            self:SetWidth(DISPLAY:GetDisplayWidth());
            self:SetHeight(DISPLAY:GetDisplayHeight());
            self:EnableDepthBuffer(false);
            self:EnableAlphaBuffer(true);
            self:EnableFloat(false);
            self:EnablePreserveTexture(true);
            self:Create();

            aftSprite:SetTexture(self:GetTexture()); -- Put the texture into the sprite above.
            
        end
"
    />

    <!-- ... -->

</children></ActorFrame>

Let's explain what happened there :

  • Little note : an ActorFrame is an Actor. Nobody said that we couldn't put commands into it. :P
  • Sprites have an issue with Nvidia cards, the sprites' alpha is way stronger on Nvidia cards than on other cards (Intel, AMD). I found how to fix this problem : we just need to multiply the alphas by 90%. So, we're checking if the user is playing with a Nvidia card with {{RageDisplay_GetVendor}}, and if yes, we set a variable to 0.9 instead of 1. We will multiply the alphas on the sprites by this variable later. Since {{RageDisplay_GetVendor}} isn't present in the first release of NotITG, you can use this (less accurate: will not work on PCs with both Intel integrated card active and a Nvidia card) alternative instead : string.find(string.lower(PREFSMAN:GetPreference('LastSeenVideoDriver')), 'nvidia').
  • In the sprite, we're setting a variable to access this Actor. We're doing things to make the sprite appear correctly, and then we're zooming a bit the Sprite, and setting its alpha to 0.95 (multiplied by the variable set above). YOU MUST HAVE A PICTURE CALLED white.png WITH A WHITE PIXEL INSIDE IT IN THE FOLDER TO MAKE THE SPRITE WORK ! You can download it here !
  • We're defining an ActorFrameTexture, setting a variable, defining properties of the AFT, and then "create" it (making it work). Then we apply the texture to the sprite above.

If you try it, you will see a nice trail effect. Why ? I said that the AFT captures everything before it. The Sprite here is before the AFT, so it will be captured. It's exactly like when you have a camera connected to a TV, and you are pointing the camera to the TV, the image will repeat, repeat, repeat, ...


ITGBlend Modes

Blend modes are a way of modifying the apparence of a texture, or a sprite. They exactly work as layer modes in image manipulation programs such as GIMP or Adobe Photoshop. Blend modes can be applied using {{Actor_blend}}. Here is the full list : (A means the pixel value of the image behind (already present, known as "destination" in OpenGL), B means the pixels value of the actual texture (where a blend mode is applied, known as "source" in OpenGL), and R means the final result, shown to the user. Pixels values are between 0 and 1, like {{Actor_diffuse}}.)

  • normal: The texture acts as usual, no transformation is done.
  • add: The pixels values are added into the image behind. Formula: R = A + B
  • subtract: The pixels values are subtracted from the image behind. Formula: R = A - B
  • modulate: Formula: R = A*B
  • copysrc: Copies A a frame behind to B, and apply an additive blend.
  • alphamask: Applies the alpha of the texture to the images behind. Formula: The colors of R will be the colors of B, and the alpha of R will be the alpha of A.
  • alphaknockout: Applies the inverted alpha of the texture to the images behind. Formula: The colors of R will be the colors of B, and the alpha of R will be the alpha of (1-A).
  • alphamultiply: Like weightedmultiply, but for the alpha value.
  • weightedmultiply: With this mode enabled, darker colors will darken the image, and brighter colors will brighten the image. Formula: R = 2*(A*B)
  • invertdest: Acts like the reverse of subtract. Formula: R = B - A
  • noeffect: Nothing is done. Formula: R = A

Troubleshooting

Assertion 'XXX' failed

The Assertion 'XXX' failed are really common errors, where something that wasn't expected, just occured.

Here is a (non-exhausive, there are around 800 different assertions !) list of the most common :

  • !lua_isnil(L, -1): Something just went wrong somewhere in the system. Restart your game and see if it happens again.
  • iter != subs.end(): You defined the same command twice on an Actor. Please rename/delete the unwanted command.
  • fPercent >= 0 & fPercent <= 1: You called a method (like cropright, valign, or similar) with an incorrect argument. These methods are wanting an argument between 0 and 1. Check the method in this webpage to see what kind of argument it's wanting.
  • !sEffectFile.empty(): The file/folder you specified as a background effect doesn't exist
  • iter != m_mapNameToData.end(): Your current noteskin has an error
  • size_t(iInputSpeed) <= sizeof(samps)/sizeof(*samps): Just don't even try to play a song with a >2 rate.
  • stack.size() < 100: You tried to draw a shape with too many polygons.
  • stack.size() > 0: You tried to draw a shape without any data.
  • 0: You did something that the game doesn't like. For example, calling SetNewScreen() in a simfile in edit mode is doing that.
  • false: see 0

Classes & Methods

Enumerations

For most of these enumerations, you can use constants instead of integers.

Global functions

Constants

Mods

WIP. ITGOpenITG Mods NotITGNotITG + OpenITG Mods

Useful resources