Lua scripting cookbook
Here are some snippets to help you get familiar with Lua APIs and learn how to use them.
To test these examples, paste the code into the in-game Lua console by pressing the ` (backtick) key.
Items
Getting list of all wielded and worn items in your inventory
local you = gapi.get_avatar()
local items = you:all_items(false)
for _, item in pairs(items) do
local status = ""
if you:is_wielding(item) then
status = "wielded: "
elseif you:is_wearing(item) then
status = "worn: "
end
print(status .. item:tname(1, false, 0))
end
Example output
wielded: smartphone
worn: bra
worn: panties
worn: pair of socks
worn: jeans
worn: long-sleeved shirt
worn: pair of sneakers
worn: messenger bag
worn: wrist watch
pocket knife
matchbook
clean water (plastic bottle)
clean water
Monsters
Spawning a dog near the player
local avatar = gapi.get_avatar()
local coords = avatar:get_pos_ms()
local dog_mtype = MtypeId.new("mon_dog_bcollie")
local doggy = gapi.place_monster_around(dog_mtype, coords, 5)
if doggy == nil then
gdebug.log_info("Could not spawn doggy :(")
else
gdebug.log_info(string.format("Spawned Doggy at %s", doggy:get_pos_ms()))
end
Combat
Printing details about a combat technique when it is used
First, define the function.
on_creature_performed_technique = function(params)
local char = params.char
local technique = params.technique
local target = params.target
local damage_instance = params.damage_instance
local move_cost = params.move_cost
gdebug.log_info(
string.format(
"%s performed %s on %s (DI: %s , MC: %s)",
char:get_name(),
technique.name,
target:get_name(),
damage_instance:total_damage(),
move_cost
)
)
end
Then connect the hook to the function ONLY ONCE.
table.insert(game.hooks.on_creature_performed_technique, function(...) return on_creature_performed_technique(...) end)
Example output
Ramiro Waters performed Power Hit on zombie (DI: 27.96 , MC: 58)
Item Durability
Checking and modifying item damage
local you = gapi.get_avatar()
local wielding = you:all_items(false)[1]
print(wielding:get_damage())
print(wielding:get_damage_level(4))
wielding:mod_damage(2000)
print(wielding:get_damage_level(4))
Monsters
Adding items to a monster's inventory
local target_monster = -- [[ your monster reference ]]
local scraps = gapi.create_item(ItypeId.new("scrap"), 3)
target_monster:as_monster():add_item(scraps)
Weather Hooks
Reacting to weather changes
First, set up the hook in your preload.lua:
local mod = game.mod_runtime[game.current_mod]
table.insert(game.hooks.on_weather_changed, function(...) mod.weather_changed_alert(...) end)
table.insert(game.hooks.on_weather_updated, function(...) mod.weather_report(...) end)
Then define the handlers in your main.lua:
local mod = game.mod_runtime[game.current_mod]
-- Called when weather changes (e.g., clear -> rain)
mod.weather_changed_alert = function(params)
local msg = string.format(
"Weather changed from %s to %s!",
params.old_weather_id,
params.weather_id
)
gdebug.log_info(msg)
end
-- Called every 5 minutes with current weather data
mod.weather_report = function(params)
local msg = string.format(
"Current Weather: %s, Temperature: %.1f°C, Wind: %d, Humidity: %d%%",
params.weather_id,
params.temperature,
params.windspeed,
params.humidity
)
gdebug.log_info(msg)
end
Combat Ranged
Reacting to shots fired and thrown items
First, set up the hooks in your preload.lua:
local mod = game.mod_runtime[game.current_mod]
table.insert(game.hooks.on_shoot, function(...) return mod.on_shoot_fun(...) end)
table.insert(game.hooks.on_throw, function(...) return mod.on_throw_fun(...) end)
Then define the handlers in your main.lua:
local mod = game.mod_runtime[game.current_mod]
mod.on_shoot_fun = function(params)
---@type Item
local gun = params.gun
---@type Item
local ammo_item = params.ammo
local ammo = ItypeId.NULL_ID()
if not ammo_item then
ammo = gun:ammo_current()
else
ammo = ammo_item:get_type()
end
local shoot_noise = ammo:obj():slot_ammo().loudness
gdebug.log_info(string.format("Gun sound: %d.", shoot_noise))
end
mod.on_throw_fun = function(params)
---@type Character
local thrower = params.thrower
---@type Item
local thrown = params.thrown
if thrown:is_gun() then
gdebug.log_info("Hey! Guns are not for throwing!")
end
end
Overmap Queries
Finding and manipulating items on the overmap
-- Find all items on the overmap at a specific location
local om_pos = OmPos.new(0, 0, 0)
local items = gapi.overmap_find_items_around(om_pos, 0)
-- Get an item from the map and keep it in Lua even if the map unloads
local map_pos = MapPos.new(100, 100, 0)
local item = gapi.get_map():find_item_at(map_pos)
if item then
local detached = gapi.create_detached_item(item)
-- Later, you can reattach it to a location
local reattached = gapi.reattach_item(detached, map_pos)
end
-- Teleport items within the same map
local source_pos = MapPos.new(100, 100, 0)
local dest_pos = MapPos.new(110, 110, 0)
gapi.get_map():move_item_at(source_pos, dest_pos)
Death Hooks
Tracking when monsters die
-- In preload.lua
local mod = game.mod_runtime[game.current_mod]
table.insert(game.hooks.on_mon_death, function(...) return mod.on_mon_death(...) end)
-- In main.lua
local mod = game.mod_runtime[game.current_mod]
mod.on_mon_death = function(params)
---@type Creature
local monster = params.creature
---@type Character|nil
local killer = params.killer
local killer_name = killer and killer:get_name() or "Unknown"
gdebug.log_info(string.format("%s was killed by %s", monster:get_name(), killer_name))
end
Tracking character deaths
-- In preload.lua
local mod = game.mod_runtime[game.current_mod]
table.insert(game.hooks.on_char_death, function(...) return mod.on_char_death(...) end)
-- In main.lua
local mod = game.mod_runtime[game.current_mod]
mod.on_char_death = function(params)
---@type Character
local char = params.char
---@type Character|nil
local killer = params.killer
if char == gapi.get_avatar() then
gdebug.log_info("You have died!")
end
end
Character Combat Stats
Getting attack and stamina costs
local you = gapi.get_avatar()
local items = you:all_items(false)
for _, item in pairs(items) do
print(
item:tname(1, false, 0)
.. " { attack cost: " .. item:attack_cost()
.. ", stamina cost: " .. item:stamina_cost()
.. ", melee stamina cost: " .. you:get_melee_stamina_cost(item)
.. " }"
)
end
-- Check for special abilities
print("Uncanny dodge: " .. (you:uncanny_dodge() and "yes" or "no"))
Character Magics
Learn a new spell and forget it
learning a spell:
local u = gapi.get_avatar()
local km = u:get_magic()
local ex_sp = SpellTypeId.new("example_template")
km:learn_spell(ex_sp, u, true) -- learn forced
print( km:knows_spell(ex_sp) ) -- check
forgetting a spell:
local u = gapi.get_avatar()
local km = u:get_magic()
local ex_sp = SpellTypeId.new("example_template")
km:forget_spell(ex_sp) -- forget
print( km:knows_spell(ex_sp) ) -- check again
Dynamic Item Actions
Creating custom item use functions in Lua
-- Define an item's use behavior with tick and can_use functions
game.iuse_functions["my_custom_item"] = {
use = function(params)
local user = params.user
local item = params.item
gdebug.log_info("Using: " .. item:tname(1))
return 0 -- Return time cost in moves
end,
can_use = function(params)
local user = params.user
local item = params.item
-- Return true to allow use, false to prevent
return true
end,
tick = function(params)
local user = params.user
local item = params.item
-- Called periodically while item is active
if item:get_countdown() == 0 then
gdebug.log_info("Item countdown finished!")
end
end
}
-- Set a countdown on an item to trigger periodic ticks
local item = gapi.create_item(ItypeId.new("some_item"), 1)
item:set_countdown(100) -- Ticks for 100 turns
More Combat Hooks
Reacting to dodge, block, and technique events
-- In preload.lua
local mod = game.mod_runtime[game.current_mod]
table.insert(game.hooks.on_creature_dodged, function(...) return mod.on_creature_dodged(...) end)
table.insert(game.hooks.on_creature_blocked, function(...) return mod.on_creature_blocked(...) end)
table.insert(game.hooks.on_creature_performed_technique, function(...) return mod.on_creature_performed_technique(...) end)
table.insert(game.hooks.on_creature_melee_attacked, function(...) return mod.on_creature_melee_attacked(...) end)
-- In main.lua
local mod = game.mod_runtime[game.current_mod]
mod.on_creature_dodged = function(params)
---@type Character
local char = params.char
---@type Creature
local source = params.source
local difficulty = params.difficulty
gdebug.log_info(string.format("%s dodged %s (DC: %d)", char:get_name(), source:get_name(), difficulty))
end
mod.on_creature_blocked = function(params)
---@type Character
local char = params.char
---@type Creature
local source = params.source
local bodypart_id = params.bodypart_id
local damage_blocked = params.damage_blocked
gdebug.log_info(string.format(
"%s blocked %s on %s (blocked: %.1f damage)",
char:get_name(),
source:get_name(),
bodypart_id,
damage_blocked
))
end
mod.on_creature_melee_attacked = function(params)
---@type Character
local char = params.char
---@type Creature
local target = params.target
if params.success then
gdebug.log_info(string.format("%s hit %s", char:get_name(), target:get_name()))
else
gdebug.log_info(string.format("%s missed %s", char:get_name(), target:get_name()))
end
end
Item Type Information
Querying item type properties via ItypeId
local item_type = ItypeId.new("9mm")
-- Get the item type object (ItypeRaw)
local itype_raw = item_type:obj()
-- Access item type specific data (e.g., for ammo)
if itype_raw:slot_ammo() then
local ammo_data = itype_raw:slot_ammo()
print("Ammo damage: " .. ammo_data.damage)
print("Ammo range: " .. ammo_data.range)
end
-- For containers
if itype_raw:slot_container() then
local container_data = itype_raw:slot_container()
print("Capacity: " .. container_data.capacity)
end
-- For tools
if itype_raw:slot_tool() then
local tool_data = itype_raw:slot_tool()
print("Tool quality: " .. tool_data.quality)
end
Time and Space
Sun and moon, inside and outside
local u_pos = gapi.get_avatar():get_pos_ms()
local map = gapi.get_map()
local now = gapi.current_turn()
-- Found the key name from MoonPhase entries
local moon = ""
for name, num in pairs(MoonPhase) do
if num == now:moon_phase() then
moon = name
end
end
print( "Are you outside?: " .. tostring(map:is_outside(u_pos)) )
print( "Are you sheltered?: " .. tostring(map:is_sheltered(u_pos)) )
print( "Today moon phase is: " .. moon )
print( "Sunset time is: " .. now:sunset():to_string_time_of_day() )