Lua scripting in AGen

Lua is a general purpose scripting language. Entire books have been written about it. The purpose of this article is to highlight the peculiarities of writing games in AGen by using Lua. It is presumed that you are already familiar with Lua itself and if not, please refer to the official Lua 5.1 documentation.

Running and debugging scripts

The engine selects which Lua script to run via the command line. If you fail to supply a file name to the command line, the engine will attempt to run the default script "Scripts\main.lua". A convinient way of using the command line is creating a batch file. Simply open Notepad or any text editor, paste the following line (replacing 'myscript.lua' with your file name) and save it as a plain text file with a ".bat" extension.

Agenoria.exe Scripts/myscript.lua
Download:  run.bat

Whenever you run the engine, it outputs two log files in its working directory folder: "log.txt" and "lua.txt". The former log file contains engine initialization messages, failed texture loading notifications and so on. Most of the time you'll be looking at the latter log file which shows your Lua scripting errors. The script file name and the line number where an error occurred are logged along with a backtrace or the sequence of function calls that lead to the error. In addition, users will be notified of all scripting errors through a Windows alert dialog.

AGen has multiple plug-ins for graphics, audio and input. The currently selected plug-ins are defined in the "settings.ini" file located in the engine's directory. The default plug-ins are most stable and it is usually unnecessary to change them unless if you are having hardware issues with your machine.

Working with AGen objects

AGen's components are represented as userdata objects in Lua. Each object exports its properties as well as its functionality through the userdata. To create a new AGen object, you need to call its "pseudo constructor". I have used the word "pseudo" since Lua doesn't have constructors in the C++ sense. Basically, you will be calling a global function within Lua which returns a reference to the object of the respective type. The reason why I liken these functions to constructors is because they are named like the type of object they return. Now that I've confused you enough, the following example should clear things up. It shows the creation of a "color" object using two different constructors... or rather "pseudo constructors"

c = Color ( )
c2 = Color ( 255, 255, 255 )

The different types of AGen objects have their own unique properties. The color object for example has "red", "green" and "blue" properties. These properties belong to the color object itself and (in this case) their value can only be a number. Apart from properties, most AGen objects also have "member functions" or as C++ programmers would say, "methods". The functions of an object are usually called using the colon ( ":" ) operator. On the other hand, its properties can be accessed either via the dot ( "." ) operator or square bracket delimiters ( "[' ']" ).

c.red = 255
c['green'] = 0

c:set_blue ( 255 )

The assignment operator ( "=" ) in Lua is so easy to mishandle that it deserves special attention. Let us say that you have two separate tables "stored" in two variables and you tried to assign one of them to the other. Lua will simply turn your first, destination variable into a reference pointing to the second, source variable. This rule applies for AGen objects too. So how can you then make a copy of an AGen object? With the "color" object, the easiest way is to use its "copy constructor".

c = c2 -- reference

c = Color ( c2 ) -- copy

Pre-compiling scripts

Using "luac.exe" you can pre-compile your script files into Lua bytecode. This speeds up the runtime loading of Lua files and obfuscates your code. Notice that the process is not easily reversible so you only want to apply it to code that you are going to distribute. Don't pre-compile your working code because you won't be able to edit it later. You can use the following ".bat" file to compile a directory of script files:
for /r %%i in (*) do luac.exe -s -o "%%i" "%%i"
Notice that the above batch file recursively overwrites all files in the current and sub directories.

floats instead of doubles

AGen runs a custom Lua build that uses floats instead of doubles. This has several implications. For example, timestamps returned by "os.time" have a precision of around 128 seconds. This affects third party libraries too, including LuaFileSystem which could be used to get the time of last access, modification or status change for files.

Power of 2 textures

This is one of the most common issues that you may encounter when using the image object. For optimal compatibility, image files should be square in size where the sides should be a power of 2. Therefore, make sure that your textures are 16x16, 32x32, 128x128, 256x256 or 512x512 pixels. This means that you may often have to leave transparent padding in your .png and .bmp files. Textures that are non-power of 2 may be rendered incorrectly by certain video cards.

tolua

The engine's functionality was exported using the C++ library called tolua. tolua offers several functions (accessible through the global "tolua") which could be used to inherit or extend AGen objects:
cast ( var, type )
Changes the metatable of var in order to make it of type type. type needs to be a string with the complete C type of the object (including namespaces, etc).
getpeer ( object )
Retrieves the peer table from the object (can be nil).
inherit ( table, var )
Causes tolua++ to recognise table as an object with the same type as var, and to use var when necesary.
releaseownership ( var )
Releases ownership of the object referenced by var.
setpeer ( object, peer_table )
Sets the table as the object's peer table (can be nil). The peer table is where all the custom lua fields for the object are stored. When compiled with lua 5.1, tolua++ stores the peer as the object's envirnment table, and uses uses lua_gettable/settable (instead of lua_rawget/set for lua 5.0) to retrieve and store fields on it. This allows us to implement our own object system on our table (using metatables), and use it as a way to inherit from the userdata object.
takeownership ( var )
Takes ownership of the object referenced var. This means that when all references to that object are lost, the objects itself will be deleted by lua.
type ( var )
Returns a string representing the object type. For instance, tolua.type(tolua) returns the string table and tolua.type(tolua.type) returns cfunction. Similarly, if var is a variable holding a user defined type T, tolua.type(var) would return const T or T, depending whether it is a constant reference.

stdout

stdout is not very practical when programming games and is not available in the Windows build of AGen. This means that Lua functions like "print" will have no output.