Modding: Lua Scripting

Modding Pages

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.