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 otherwise self-explanatory (see below for the expected functions). It can have other Lua files which are then referenced as modules via Lua’s require function.

If a object is especially big and/or contains lots of variables that are referenced across fiels (such as the player object) you may want to put important global variables in a “vars.lua” file that gets referenced across multiple files.

Each object has its own confined Lua state, with its own scope. All of them have an Object global which is used to access things that the engine expects them to have (or to interact with the engine in general), i.e. Object.position and Object.size. It can communicate (somewhat limitingly, see the relevant docs) with objects it collides with via Object:get and Object:set.

Objects are also responsible for loading textures with Renderer:load_texture and then drawing them via Renderer:draw_texture_opt (Or draw_texture, but as of writing this, that one is broken).

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

Expected Functions

There are three functions expected to be within an object’s Lua scope. These should idomatically be put in main.lua, but they can be placed in other files (however I’ve only ever done it in main.lua and I don’t know if placing them in other modules messes with the game’s ability to see them; it shouldn’t but if your functions aren’t being loaded check this).

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)

Having this be done manually is useful so that there can be HUD objects or objects that mess with the entire screen for any reason.

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 name of the level as shown on the Level Select screen. This one isn’t strictly required as the game can fall back to showing the filename.

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.

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

any

A type for a dynamic argument, it can be anything at run-time.

boolean

A built-in type representing a boolean (true or false) value, see details

function

A built-in type representing functions, see details

integer

A helper type that represents whole numbers, a subset of number

lightuserdata

A built-in type representing a pointer, see details

nil

A built-in type representing a non-existant value, see details. When you see ? at the end of types, it means they can be nil.

number

A built-in type representing floating point numbers, see details

self

A type that represents an instance that you call a function on. When you see a function signature starting with this type, you should use : to call the function on the instance, this way you can omit this first argument.

local object = SomeClass()
object:do_something(123)

string

A built-in type representing a string of characters, see details

table

A built-in type representing associative arrays, see details

unknown

A dummy type for something that cannot be inferred before run-time.

userdata

A built-in type representing array values, see details.