Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Funny Wars Platformer Lua Documentation

This site contains everything necessary for a team of people to build a thing codenamed “Funny Wars”.

If you found this on Google or otherwise do not expect this to be useful, as of writing this is relevant to a private project.

Used Tools

You probably already know which tools the project is using at this point but fuck it I’ll document them here for posterity and give links.

The engine itself is done in C++/Rust but the game allows interacting with the engine via Lua, which is the most important part here as this is what the levels are made with.

Maps are done with the Tiled Editor

You should use Aseprite for images.

Assets Directory

The assets directory is the core place for any content the game needs to load at runtime. When the game is packaged, this will likely be a .zip file that the game automatically extracts, but either way, the idea will remain the same.

Most of the folders in assets can be named anything, and files can be anywhere, with the exception of a few folders that the game expects to be named appropriately.

Level Objects

Objects are created by creating a folder in level/objects. As each folder corresponds to an object that can be referenced from within Tiled, the name of the folder == the object name as used in Tiled.

From there, an Object must have a Lua file titled main.lua within it. This file is the first file that the engine executes when it loads the object. From here, you can use Lua’s require function to include other files in the same folder (or files in subfolders), and the engine will execute them appropriately, allowing you to split up the object into multiple files. This combination of files, whatever it may be, is the scope in which an object can access/set its own variables. This scope is local to each instance of an object; changes to variables are not shared with objects of the same type.

Each Object has certain variables that are always created by the engine, such as their position and their size. These are presented to scripts as fields within a global variable called Object. This same type is also relevent to an object’s collision callback (see below) as it’s how you get information on what it collides with. The Object global also has functions that can be used with your object (i.e. Object.get_collision_info for checking for an object’s collision with a tile), and the Object type received in the collision function has its own methods that can be executed (this is how objects can communicate in the collision function, they can get/set each other’s variables via Object:get and Object:set). However, these are not mutually exclusive; you can’t use the Object functions on an object you get in the collision function, and you can’t use the Object’s methods on the global Object.

Objects are also currently* responsible for loading textures with Renderer:load_texture. They can draw them manually via Renderer:draw_texture_opt, but the engine will automatically draw objects that don’t override the draw function (see below).

*The plan is to eventually have objects have configuration files which can set this too, allowing for objects that only have a tick function.

When in doubt, see the player object’s main.lua, which has everything wrapped up neatly and can thus serve as good example code.

Callbacks

To give an object functionality, there are several functions (“callbacks”) you can define anywhere within your object’s Lua scope that will get called by the engine appropriately:

These should idomatically be put in main.lua, but they can be placed in other files.

All of them are optional. Your main.lua file can be completely blank (though it does have to exist), resulting in an invisible object that does nothing.

init function

Called upon the object’s creation.

--- @param win Window
--- @param renderer Renderer
function init(win, renderer)
-- ...
end

tick function

Called every tick for every object in the level.

---@param win Window
-- tick function
function tick(win)
-- ...
end

draw function

Called for every object on screen. This is where you draw a texture at your object’s position.

--- @param renderer Renderer
--- @param cameraPos Vec2
function draw(renderer, cameraPos)
-- ...
end

Notice that camera position is passed in. If you want to draw something at your object’s current position, you need to do something like:

-- textureOptions being a TextureOptions in this case.
textureOptions.dest.x = (Object.position.x - cameraPos.x)
textureOptions.dest.y = (Object.position.y - cameraPos.y)

If this function is not present in your script, the above equations will be applied to Object.texture_options, and the object will be drawn automatically using them.

on_collide function

Called upon the object colliding with another object.

---@param obj Object
function on_collide(obj)
-- ...
end

Documented Level Objects

This documentation file is specifically for level objects. Generally this isn’t accessibly as normal Lua types, but instead via methods like Object:get. This also concerns how these objects are set up in the Tiled editor.


bigdoor

Big door that takes the player to another level when they press the up key.

Custom Properties:

  • level (string): The .tmx file to load the player into. Path must be relative to the assets directory, i.e. levels/prophets_castle/room_1.tmx.

player

Player Object


water

Water Object


music_queue

Planned to have this be an invisible object that starts a music track when you walk through it. Currently unimplemented.


Level Structure

Level Structure

Each level in the game is to be placed in its own folder within assets/levels. There are two files it is expected to have with a given name. Anything else can go in here with any name, and similarly the folder can be titled anything.

  • entry_point.tmx: The .tmx file that the game loads when the level is loaded. It can point to other .tmx files with their own names, but it must be named this. A level is not considered valid and will not show up in the level select without this.

  • name: The contents of this file contain the name of the level as shown on the Level Select screen. If this file is missing, the game will display the filename instead.

Global Scripts

assets contains a folder known as scripts which can be used for global Lua scripts loaded for every object.

As of writing this, there is a blank file in it called strict.lua, and the feature as a whole is unused. If this is ever used, this file is to be updated.

References

So the engine has Object right. And Lua has this thing called the garbage collector that does automatic memory management. The engine owns the fields within Object, and the lua context should never try to release them because otherwise that interferes with the engine’s ability to operate. So the engine needs to ensure that this garbage collector does not release any of those fields. The solution we go with is “references”: wrappers around certain types that are used identically to their regular types, but they are set up to never get dropped.

For the most part, this is only relevant at a low level, such to the point where this page would be useless. References carry the same methods/fields with their non-reference counterparts, and the same behavior, and thus there is no difference. However, because they are technically different types internally, there is going to be a known bug where certain engine functions will reject the reference types. Said engine functions are few and far between, especially since, again, this rule doesn’t apply to any methods of the classes. The one that comes to mind as of writing this, for example, is Renderer:draw_texture_opt; if you find yourself calling this function you must do it with a TextureOptions that You created, NOT Object.texture_options, otherwise you will get an error because that is a reference.

And this is why future projects I work on I will search for a different scripting language other then Lua if we need it :) or we’ll just write in native code haha idfk

Object

CollisionInfo

Collision info gotten by Object.get_collision_info


Properties

collides : boolean

semisolid : boolean

Object

Object placed within the map. Object is a little unique compared to other types because of the two ways to use it. Using its functions (i.e. Object.get_collision_info) will act upon the calling object. However, it can also be gotten via the collision callback, and from there, the methods can be used. These are not mutually exclusive; you cannot call i.e. get_collision_info on an Object obtained via the collision callback, and vice versa.


Properties

position : Vec2

Two component Vector

size : Vec2

Two component Vector

name : string


Functions

get_collision_info(x : number, y : number)

->CollisionInfo

Get information about the Object’s collision the current tile

get_prop_bool(key : string)

->boolean

Get a custom property from the Object (bool)

get_prop_color(key : string)

->Color

Get a custom property from the Object (Color)

get_prop_float(key : number)

->boolean

Get a custom property from the Object (float)

get_prop_int(key : integer)

->boolean

Get a custom property from the Object (int)

get_prop_string(key : string)

->string

Get a custom property from the Object (string)

set_map(path : string)

->string

Change the current map.

get(self, name : string)

->boolean | string | number | nil

Get a global variable defined within the Object’s scope.

Type Limitations

This works, obviously, because each Object has its own Lua state where its variables are stored. But a very unfortunate limitation of Lua is that complex types such as tables and userdatas (most important because this is the underlying type for every engine type, i.e. Vec2s and Textures) cannot be passed between two Lua states.

This limits this to only the following primitives: boolean, integer, number, string, or nil.

set(self, name : string, value : boolean | string | number | nil)

->boolean | string | number | nil

Set a global variable defined within the Object’s scope. If the variable is undefined, a new one will be set within that object.

Type Limitations

This works, obviously, because each Object has its own Lua state where its variables are stored. But a very unfortunate limitation of Lua is that complex types such as tables and userdatas (most important because this is the underlying type for every engine type, i.e. Vec2s and Textures) cannot be passed between two Lua states.

This limits this to only the following primitives: boolean, integer, number, string, or nil.

Renderer

Renderer

Interface for drawing to the window.


Functions

clear_background(self, color : Color)

Clear the background to the given color.

Note that this is for use within texture render mode! The actual screen is cleared on its own.

fps(self)

->number

Return the FPS the renderer is running at

set_target_fps(self, fps : number)

Set a cap for the renderer’s FPS.

load_texture(self, path : string)

->Texture

Load a texture from the given file name (relative to the assets directory, i.e. objects/bigdoor/bigdoor.png).

Note that the final output is converted to RGBA8

load_texture_from_image(self, image : Image)

->Texture

Load a texture from the given image.

Note that the final output is converted to RGBA8

load_texture_blank(self, width : number, height : number)

->Texture

Load a blank RGBA8 texture with the given width and height.

load_texture_blank_ex(self, width : number, height : number, length : number, format : TextureFormat)

->Texture

Load a blank texture with the given width and height, length of data, and format.

I kind of just absent-mindedly bound this to Lua, you probably don’t want this. ~ ioi

update_texture(self, texture : Texture, data : [number])

Upload data to the GPU, replacing the texture’s contents. MUST be a matching (width*height*pitch) array (pitch usually being 4 unless you loaded a texture with a custom format).

draw_texture(self, texture : Texture, source : Rectangle, dest : Rectangle, color : Color)

Draw a given texture to the screen.

draw_texture_opt(self, opts : TextureOptions)

Draw a given texture to the screen with the provided options.

begin_texture_render(self, texture : Texture)

Begins a “texture render mode”, in which any rendering operations after this to be drawn to texture. This lasts until you call end_texture_render.

The texture in question is pushed to a stack of textures and whichever one is at the forefront is what’s rendered to. This allows you to stack the call, and then appropriately pop the stack.

This function is also used internally to lock the game to a certain resolution, so if you forget to call end_texture_render, you will fuck up the game output.

end_texture_render(self)

End previous texture render switch and fall back to whatever was being rendered to before

draw_text(self, font : Font, text : string, pos_x : number, pos_y : number, font_size : number, color : Color)

Types

Global

Constants

ImageFormat

{
    PNG: integer = -1,
    JPEG: integer = -1,
    GIF: integer = -1,
    WEBP: integer = -1,
    PNM: integer = -1,
    TIFF: integer = -1,
    TGA: integer = -1,
    DDS: integer = -1,
    BMP: integer = -1,
    ICO: integer = -1,
    HDR: integer = -1,
    OPEN_EXR: integer = -1,
    FARBFELD: integer = -1,
    AVIF: integer = -1,
    QOI: integer = -1,
    PCX: integer = -1,
    UNSUPPORTED: integer = -1,
}

ImageFilterType

{
    NEAREST: integer = -1,
    TRIANGLE: integer = -1,
    CATMULL_ROM: integer = -1,
    GAUSSIAN: integer = -1,
    LANCZOS_3: integer = -1,
}

TextureFormat

{
    UNDEFINED: integer = -1,
    R8G8B8A8_SINT: integer = -1,
    R8G8B8A8_UINT: integer = -1,
    R8G8B8A8_UNORM: integer = -1,
    R8G8B8A8_SNORM: integer = -1,
    A8B8G8R8_UNORM_PACK32: integer = -1,
    A8B8G8R8_SNORM_PACK32: integer = -1,
    A8B8G8R8_SINT_PACK32: integer = -1,
    A8B8G8R8_UINT_PACK32: integer = -1,
    R16G16B16A16_SFLOAT: integer = -1,
    R16G16B16A16_SINT: integer = -1,
    R16G16B16A16_UINT: integer = -1,
    R32_SFLOAT: integer = -1,
    R32_SINT: integer = -1,
    R32_UINT: integer = -1,
    R32G32_SFLOAT: integer = -1,
    R32G32_SINT: integer = -1,
    R32G32_UINT: integer = -1,
    R32G32B32A32_SFLOAT: integer = -1,
    R32G32B32A32_SINT: integer = -1,
    R32G32B32A32_UINT: integer = -1,
    R8G8B8A8_SRGB: integer = -1,
}

TextureFilter

{
    POINT: integer = -1,
    NEAREST: integer = -1,
}

TextureWrap

{
    REPEAT: integer = -1,
    CLAMP: integer = -1,
    MIRROR_REPEAT: integer = -1,
    MIRROR_CLAMP: integer = -1,
}

KeyboardKey

{
    NULL: integer = -1,
    APOSTROPHE: integer = -1,
    COMMA: integer = -1,
    MINUS: integer = -1,
    PERIOD: integer = -1,
    SLASH: integer = -1,
    ZERO: integer = -1,
    ONE: integer = -1,
    TWO: integer = -1,
    THREE: integer = -1,
    FOUR: integer = -1,
    FIVE: integer = -1,
    SIX: integer = -1,
    SEVEN: integer = -1,
    EIGHT: integer = -1,
    NINE: integer = -1,
    SEMICOLON: integer = -1,
    EQUAL: integer = -1,
    A: integer = -1,
    B: integer = -1,
    C: integer = -1,
    D: integer = -1,
    E: integer = -1,
    F: integer = -1,
    G: integer = -1,
    H: integer = -1,
    I: integer = -1,
    J: integer = -1,
    K: integer = -1,
    L: integer = -1,
    M: integer = -1,
    N: integer = -1,
    O: integer = -1,
    P: integer = -1,
    Q: integer = -1,
    R: integer = -1,
    S: integer = -1,
    T: integer = -1,
    U: integer = -1,
    V: integer = -1,
    W: integer = -1,
    X: integer = -1,
    Y: integer = -1,
    Z: integer = -1,
    LEFT_BRACKET: integer = -1,
    BACKSLASH: integer = -1,
    RIGHT_BRACKET: integer = -1,
    GRAVE: integer = -1,
    SPACE: integer = -1,
    ESCAPE: integer = -1,
    ENTER: integer = -1,
    TAB: integer = -1,
    BACKSPACE: integer = -1,
    INSERT: integer = -1,
    DELETE: integer = -1,
    RIGHT: integer = -1,
    LEFT: integer = -1,
    DOWN: integer = -1,
    UP: integer = -1,
    PAGE_UP: integer = -1,
    PAGE_DOWN: integer = -1,
    HOME: integer = -1,
    END: integer = -1,
    CAPS_LOCK: integer = -1,
    SCROLL_LOCK: integer = -1,
    NUM_LOCK: integer = -1,
    PRINT_SCREEN: integer = -1,
    PAUSE: integer = -1,
    F1: integer = -1,
    F2: integer = -1,
    F3: integer = -1,
    F4: integer = -1,
    F5: integer = -1,
    F6: integer = -1,
    F7: integer = -1,
    F8: integer = -1,
    F9: integer = -1,
    F10: integer = -1,
    F11: integer = -1,
    F12: integer = -1,
    LEFT_SHIFT: integer = -1,
    LEFT_CONTROL: integer = -1,
    LEFT_ALT: integer = -1,
    LEFT_SUPER: integer = -1,
    RIGHT_SHIFT: integer = -1,
    RIGHT_CONTROL: integer = -1,
    RIGHT_ALT: integer = -1,
    RIGHT_SUPER: integer = -1,
    KB_MENU: integer = -1,
    KP_0: integer = -1,
    KP_1: integer = -1,
    KP_2: integer = -1,
    KP_3: integer = -1,
    KP_4: integer = -1,
    KP_5: integer = -1,
    KP_6: integer = -1,
    KP_7: integer = -1,
    KP_8: integer = -1,
    KP_9: integer = -1,
    KP_DECIMAL: integer = -1,
    KP_DIVIDE: integer = -1,
    KP_MULTIPLY: integer = -1,
    KP_SUBTRACT: integer = -1,
    KP_ADD: integer = -1,
    KP_ENTER: integer = -1,
    KP_EQUAL: integer = -1,
}

MouseButton

{
    LEFT: integer = -1,
    RIGHT: integer = -1,
    MIDDLE: integer = -1,
    SIDE: integer = -1,
    EXTRA: integer = -1,
    FORWARD: integer = -1,
    BACK: integer = -1,
}

MouseCursor

{
    DEFAULT: integer = -1,
    ARROW: integer = -1,
    IBEAM: integer = -1,
    CROSSHAIR: integer = -1,
    POINTING_HAND: integer = -1,
    RESIZE_EW: integer = -1,
    RESIZE_NS: integer = -1,
    RESIZE_NWSE: integer = -1,
    RESIZE_NESW: integer = -1,
    RESIZE_ALL: integer = -1,
    NOT_ALLOWED: integer = -1,
}

Color

R8G8B8A8 (32-bit RGBA) Color


Properties

r : number

g : number

b : number

a : number


Functions

from_rgb(r : number, g : number, b : number, a : number)

->Color

lightgray()

->Color

gray()

->Color

darkgray()

->Color

yellow()

->Color

gold()

->Color

orange()

->Color

pink()

->Color

red()

->Color

maroon()

->Color

green()

->Color

lime()

->Color

darkgreen()

->Color

skyblue()

->Color

blue()

->Color

darkblue()

->Color

purple()

->Color

beige()

->Color

brown()

->Color

darkbrown()

->Color

white()

->Color

black()

->Color

transparent()

->Color

magenta()

->Color

Font

Image

Rectangle


Properties

x : number

y : number

width : number

height : number


Functions

new(x : number, y : number, width : number, height : number)

->Rectangle

Texture


Functions

width(self)

->number

height(self)

->number

set_filter(self, filter : TextureFilter)

set_wrap(self, wrap : TextureWrap)

bounding_box(self)

->Rectangle

set_shader(self, renderer : Renderer, shader : string)

Set a texture’s shader to one of any preset shaders.

TextureOptions

Options used for drawing a texture


Properties

texture : Texture

source : Rectangle

dest : Rectangle

flippedX : boolean

flippedY : boolean

origin : Vec2

Two component Vector

rotation : number

tint : Color

R8G8B8A8 (32-bit RGBA) Color

Vec2

Two component Vector


Properties

x : number

y : number


Functions

new(x : number, y : number)

->Vec2

Vec3

Three component Vector


Properties

x : number

y : number

z : number


Functions

new(x : number, y : number, z : number)

->Vec3

Vec4

Four component Vector


Properties

x : number

y : number

z : number

w : number


Functions

new(x : number, y : number, z : number, w : number)

->Vec4

Window

Window

Application window


Functions

width(self)

->number

Return the window’s width.

height(self)

->number

Return the window’s height

is_fullscreen(self)

->boolean

Return whether the window is fullscreen.

is_hidden(self)

->boolean

Return whether the window is hidden.

is_minimized(self)

->boolean

Return whether the window is minimized.

is_maximized(self)

->boolean

Return whether the window is maximized.

is_focused(self)

->boolean

Return whether the window is focused.

toggle_fullscreen(self)

Togggle the window between fullscreen and not.

toggle_borderless_windowed(self)

Togggle the window having a border or not.

maximize(self)

Maximize the window.

minimize(self)

Minimize the window.

restore(self)

Undo a maximize for the window.

set_window_title(self, title : string)

Set the window’s title.

set_window_min_size(self, width : number, height : number)

Set the minimum size the window can be at.

set_window_max_size(self, width : number, height : number)

Set the maximum size the window can be at.

set_window_size(self, width : number, height : number)

Set the window’s size.

is_key_pressed(self, key : KeyboardKey)

Check whether key was pressed.

is_key_down(self, key : KeyboardKey)

Check whether key is being held down.

is_key_released(self, key : KeyboardKey)

Check whether key is being released.

is_mouse_button_pressed(self, button : MouseButton)

Check whether mouse button was pressed.

is_mouse_button_down(self, button : MouseButton)

Check whether mouse button was held down.

is_mouse_button_released(self, button : MouseButton)

Check whether mouse button was released.

get_mouse_position(self)

->Vec2

Get the mouse position relative to the window.

get_mouse_delta(self)

->Vec2

Get the delta of the mouse’s movement.

get_mouse_wheel_move(self)

->Vec2

Get the movement of the mouse wheel.

set_mouse_cursor(self, cursor : MouseCursor)

Set mouse cursor.

open_url(self, url : string)

Open the given URL in the user’s default browser.

Lua Builtin Types