Getting started • Basics • Lua Scripting • Data.wak • Useful Tools |
Audio • Enemies • Environments • Perks • Spells • Spritesheets • Materials • Image Emitters |
Lua API • Enums • Special Tags • List of all tags • Utility Scripts • Sound Events • Enemy Information Table • Spell and Perk IDs |
Basic info and examples for writing Lua scripts for Noita mods. Note that scripting is only a part of modding, be sure to read about the Entity Component System as well.
Places to hook your scripts[]
init.lua
- The best place to get started.
- Doesn’t need any overwriting, this is specially unique to your mod.
- See Modding Basics
data/scripts/gun/gun.lua
- Called on every wand shot, highly intertwined with the engine code.
- Note compatibility, as explained below
data/scripts/biomes/<biome_name>.lua
- Called once when entering the biome
- Note compatibility, as explained below
<LuaComponent>
- Probably the best and most-used place to hook your random code
- Most everything else that you can find under
data/scripts/
is actually defined in some entity via this component. - See the official
lua_api_documentation.txt
for a list of how to hook into different actions with this component.
Hooking in a compatible way[]
When dealing with common scripts used heavily by the game itself and other mods, it might be a good idea to append to it, instead of simply overwriting it via the data/
path.
When every modder is doing this, their code will get appended in the player's mod loading order, and nothing is forcefully overwritten.
-- Any file can be appended by any file, the gun.lua is just an example.
-- *But* this can only be done inside init.lua.
ModLuaFileAppend("data/scripts/gun/gun.lua", "mods/mymod/files/gun.lua")
Lua Notes and Tips[]
Restricted Libraries[]
Some of the default Lua libraries are restricted and will not run in the mod files. A incomplete list of restricted libraries and functions:
- IO
- OS
- require
Some of the functionality of these libraries has been reimplemented in the Lua API.
-- You can't do this
local file = require "/mods/MODNAME/files/somefile.lua"
-- But you can do this
local file = dofile_once("/mods/MODNAME/files/somefile.lua")
-- You can't do this
local time = os.time()
-- But you can do this
local year,month,day,hour,minute,second = GameGetDateAndTimeLocal()
local time = year .. month .. day .. hour .. minute .. second
Without the IO library, it is far more difficult to save persistent data between runs. The only two ways to store persistent data are by using flags or the mod_settings
file.
Script examples[]
Get the current entity[]
local entity = GetUpdatedEntityID()
"Current" means the entity in which your script is running via a <LuaComponent>
(or other such trigger). Not all scripts are run this way, for example init.lua.
In the above snippet the entity could be player, wand, projectile, anything; depending on where the script is run from. This is the easiest way to fetch the currently running entity, so it deserves a mention.
Bonus: in data/scripts/gun/gun.lua
this will give you the player entity.
Get player entity[]
The basic utility script returns a list of player entities, so we have to jump through a small hoop to get just one.
For brevity, the fetching of player entity is left out in subsequent examples. Many hooks expose the player entity already, but this can be used as a backup if they don't.
dofile_once("data/scripts/lib/utilities.lua")
function get_player()
local players = get_players()
if players then
return players[1]
end
-- Player is dead
return nil
end
local player = get_player()
Get currently held wand[]
dofile_once("data/scripts/gun/procedural/gun_action_utils.lua")
-- Probably works on any entities with the Inventory2Component, eg. some enemies
local wand = find_the_wand_held(player)
Get currently held item (including wands)[]
function get_held_item(animal)
local inv_comp = EntityGetFirstComponentIncludingDisabled(
animal, "Inventory2Component"
)
-- Although the component should always be present, something unexpected
-- might've still happened (eg. another mod messing around).
if not inv_comp then
return nil
end
return ComponentGetValue2(inv_comp, "mActiveItem")
end
-- Should return a numeric ID
local wand = get_held_item(player)
Easily add items to inventory[]
-- Note, player is not the only thing with an inventory
function add_items_to_inventory(player, items)
for _, path in ipairs(items) do
local item = EntityLoad(path)
if item then
GamePickUpInventoryItem(player, item)
else
GamePrint("Error: Couldn't load the item ["..path.."]!")
end
end
end
function OnPlayerSpawned(player)
local items = {
"data/entities/items/pickup/thunderstone.xml",
}
add_items_to_inventory(player, items)
end
Spawn and enable perk[]
-- Perk system is a bit convoluted, so use the existing function
dofile_once("data/scripts/perks/perk.lua")
local perk = perk_spawn(x, y, "PROTECTION_RADIOACTIVITY")
-- Leave this part out, if you only want to spawn the perk in world
if perk then
perk_pickup(perk, player, EntityGetName(perk), false, false)
end
Spawn most other things[]
-- Enemy
EntityLoad("data/entities/animals/duck.xml", x, y)
-- Gold
EntityLoad("data/entities/items/pickup/goldnugget_200.xml", x, y)
Run init code only once[]
ie. only upon starting a new game, not when you load a savegame
local LOAD_KEY = "MYMOD_FIRST_LOAD_DONE"
function OnPlayerSpawned(player)
if GlobalsGetValue(LOAD_KEY, "0") == "1" then
return
end
GlobalsSetValue(LOAD_KEY, "1")
-- Continue with your code normally here
end
Shoot a projectile through Lua[]
dofile_once("data/scripts/lib/utilities.lua")
-- Define your starting position here, according to whatever rules you want,
-- and same with the velocities.
shoot_projectile(player, "mods/mymod/files/projectiles/supernuke.xml", from_x, from_y, vel_x, vel_y)
Note that there is also the direct API function GameShootProjectile()
, but it requires a few lines of setup, which the included utility function already does for you.