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.
levels- See Level Structurescripts- See Global Scriptsobjects- See Adding new Level Objects
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
.tmxfiles 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)
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)
Texture
Functions
width(self)
->number
height(self)
->number
set_filter(self, filter : TextureFilter)
set_wrap(self, wrap : TextureWrap)
bounding_box(self)
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.