Skip to content

Lua API Reference

Borys Stelmakh edited this page Sep 17, 2026 · 7 revisions

Lua API Reference

The complete sacred.* Lua API, the ctx:* methods passed to trigger handlers, and the npcobj NPC wrapper. Functions are grouped by topic, each with a one-line description grounded in the SDK source.

Verified on Sacred Gold Steam, build 2.0.2.28 (2006-10-13).

The sacred table is available everywhere — in bake-time mod files and in runtime callbacks. Most read/write functions need the world loaded (call them from sacred.on_tick, sacred.on_world_load, or a trigger handler); when the hero pointer isn't resolved yet (menu / loading) they return nil/false.

Contents


Bake / modding

File I/O and logging available during the bake (and at runtime). Paths are relative to the game install root.

Function Description
sacred.log(msg) Write a line to sdk_loaded.log and the overlay ring.
sacred.read_file(rel) Read any file under <game>/ and return its bytes.
sacred.write_file(rel, bytes) Write bytes under custom/ (used by text.lua); returns byte count.
sacred.disasm(bytes) Decompile a whole FunkCode/QuestCode/StartCode blob into the record tables the baker consumes; returns records, stats where stats is {records, mnemonic, hex, bytes}.

sacred.disasm is the exact inverse of the baker's encoder — same opcode table — and it verifies every record by re-encoding it, so what comes back bakes to the same bytes unless you change something. A record the mnemonic vocabulary cannot spell comes back as {"_HEX", "..."}. This is what lets vanilla.load read a shipped script with nothing prepared in advance:

local recs, stats = sacred.disasm(sacred.read_file("bin/TYPE_NPC_SERAPHIM/FunkCode.bin"))
-- 125,236 records out of 3.97 MB in 721 ms, measured

package.path is pre-configured so require finds custom/lua/lib/?.lua first (yours), then sdk/custom/lua/lib/?.lua (the framework's), then the rest of both mod trees. package.cpath is deliberately empty (the SDK won't load arbitrary DLLs from the script tree).

The bake-time builder libraries (quest, dialog, state, text, vanilla, reward, questfsm, raw, funkcode, unsafe) are documented on Writing Your First Mod and Quests and Dialog Authoring.


Runtime triggers & ctx

Attach Lua callbacks that fire at game-time. See Writing Your First Mod.

Function Description
sacred.on_trigger(name, fn) Register fn(ctx) to fire when Sacred dispatches trigger name. Handlers stack and run in registration order.
sacred.clear_triggers() Drop all registered trigger handlers (for clean re-bake).
sacred.on_tick(fn) fn() is pcalled ~every 250 ms while in-world — ideal for polling hero position / NPC state.
sacred.on_world_load(fn) fn() fires once per process, the moment the FunkCode walker captures cQuestManager (a save is loaded). The place to call questbook_register.
sacred.notify(text) Top-of-screen gold toast banner via the SDK overlay, so it is not seen while the overlay is hidden (F12). Returns true if queued, false if throttled/deduped/empty.

sacred.notify is anti-overflow: 256-byte cap, 750 ms minimum gap, back-to-back duplicate dedup, queue capped at 8 (oldest evicted), each toast lives ~4.5 s.

The ctx table

Every on_trigger handler receives ctx as its first argument.

Member Returns Notes
ctx.trigger_name string The name that fired.
ctx:gold() int / nil Hero gold; nil if hero pointer not built yet.
ctx:give_gold(N) bool Direct write to hero+0x3EE plus the coin/"+N" event; N<0 charges; clamped to 0..INT32_MAX.
ctx:give_gold_silent(N) bool Same money math, no UI/sound side effects.
ctx:charge_gold(N) bool Alias for give_gold(-N).
ctx:has_item(res) bool / nil Scans the 18 equip slots (backpack not yet covered).
ctx:get_var(name) int / nil First value of a named quest variable.
ctx:set_var(name, v0[,v1,v2,v3]) bool Overwrite a named quest variable (in-place).
ctx:notify(text) bool Forwards to sacred.notify.
ctx:set_qbit(n,v) bool Stub — use q.set_hero_qbit at bake time.
ctx:get_qbit(n) bool / nil Stub.

NPCs (runtime)

Spawn living creatures, set behavior, equip, teleport, and bind to dialog at game-time. The high-level way is the npcobj wrapper — these are the raw bindings it sits on. See Runtime NPCs for the full guide and the coordinate system. Handles are integers; positions are KompassPos (kx, ky).

Spawning

Function Description
sacred.spawn_npc(type, kx, ky) Spawn a creature via the engine create path at KompassPos. Returns a handle or nil.
sacred.spawn_here(type) Spawn at the hero's own valid position (no sector math).
sacred.spawn_at(type, kx, ky) Spawn at KompassPos using the hero's sector — reliable near the player.
sacred.spawn_item(type, kx, ky) Spawn a pickup-able ground item at KompassPos. Returns a handle.
sacred.createnpc_engine(payload_bytes, want_type) Run a dev-authored CreateNPC record through the engine's own handler (type-correct HP/AI/faction).

Behavior & stats

Function Description
sacred.npc_info(handle) Returns { type, kx, ky, faction } or nil if the creature is gone.
sacred.npc_set_faction(handle, value) Write the faction/side word (cCreature+0x1F4).
sacred.npc_wake(handle) Activate the creature's AI.
sacred.npc_set_stance(handle[, mode[, value]]) Set behavior stance (cCreature+0x1F0); mode 0 = class-default.
sacred.npc_set_invulnerable(handle[, on=true]) Toggle essential/invulnerable.
sacred.npc_set_stationary(handle[, on=true]) Hold post (no patrol) / free to roam.
sacred.npc_set_level(handle, level) Set creature level/rank.
sacred.npc_set_hp(handle, hp) Raise current + max HP (runtime spawns are weak by default).
sacred.npc_make_combatant(handle[, level=20[, ai_class=3]]) Replay CreateNPC's combat init; ai_class 7 = active ally, 2 = monster, 13 = immune.
sacred.npc_set_disposition(handle, matrix_class) Change hostility class mid-game and re-aggro.
sacred.npc_equip(handle, item_type[, slot=0xC]) Give a visible item (experimental; guarded — returns false if the ABI guess misses).
sacred.npc_teleport(handle, kx, ky) Engine teleport to KompassPos.

Lifecycle, dialog & roster

Function Description
sacred.npc_set_name(handle, "Captain Miles") Custom display name (no-op for a pure runtime spawn that lacks a DlgNPC entry).
sacred.npc_quest_icon(handle[, on=true]) Floating "?!" quest-giver glyph over the head (visual only).
sacred.npc_bind_quest(handle, "Name"[, marker_on=true]) Create the engine DlgNPC entry so name + overhead marker show; returns the index.
sacred.dialog_arm(handle, "DlgName", "TEXT_KEY"[, "voice"]) Arm an NPC to speak baked text in the talk window.
sacred.dialog_clear(handle) Close the armed dialog and clear the "?!" marker.
sacred.npc_make_companion(handle) Make the NPC follow the hero, fight for him, treated as the hero for friend/foe.
sacred.npc_dismiss(handle) Stop following; restore independent neutral AI.
sacred.npc_despawn(handle) Clean engine removal (the DelNPC path).
sacred.npc_roster_add(handle, quest_id) List the companion in the on-screen roster panel (crash-safe in-place add).
sacred.npc_roster_remove(handle) Remove from the roster panel.
sacred.npc_in_dialog(handle) Is the player talking to this NPC right now? (cCreature+0x200 bit 0x400).

For native dialog text override in the talk window (dialog_override, dialog_redirect, dialog_learn), see Dialog Text.

Function Description
sacred.dialog_override(vanilla_name, our_name) Native by-name swap: when the engine resolves vanilla dialog node vanilla_name, render baked our_name instead.
sacred.dialog_redirect(hash) Arm/disarm the native dialog-text redirect for the talk window; hash = sacred.hash(key) to arm, 0/nil to disarm.
sacred.dialog_learn(hash) On talk-close, map the just-fetched vanilla key to hash and persist it for all future fetches.

Quest book

Read and write Sacred's quest-display registry at runtime. Register from sacred.on_world_load; the registry is rebuilt from the save each load. See Quests and Dialog Authoring.

Function Description
sacred.questbook_register(quest_id) Append a brand-new display entry (calls the engine's vector::resize); returns the slot index.
sacred.questbook_count() Number of entries in the display registry.
sacred.questbook_get_id(idx) The quest_id at registry slot idx.
sacred.questbook_set_log(quest_id, page, name0[, name1, ...]) Set the journal lines for an entry (page 0 = first tab); names are global.res resource names.
sacred.questbook_add_log(quest_id, name) Append one journal line to the next free slot (0..10); for multi-step progression.
sacred.questbook_set_marker(quest_id, worldX, worldY) Set the primary (story) minimap + world-map marker.
sacred.questbook_set_kompass(quest_id, v0[, v1..v4]) Write the entry's kompass/position block (so the journal location preview renders).
sacred.questbook_set_step_done(quest_id, done_bool) Fill (true) or hollow (false) the objective bullet.
sacred.questbook_mark_solved(quest_id) Set the vanilla "solved" state (+0x04=100, kompass cleared).
sacred.questbook_complete(quest_id) Remove the entry from the journal entirely.
sacred.questbook_dump() Diagnostic: log every registry entry's fields.
sacred.hide_other_quests() Hide all non-SDK quests from the journal/map this tick (idempotent; call from on_tick). Returns hidden count.
sacred.hide_vanilla_quests([on=true]) Persistently suppress every non-SDK quest at journal-build time (race-free); call once.
sacred.questbook_track(quest_id [, handle]) Put this quest in its compass column (story column for ids ≤ 99, side column above), optionally following a creature.
sacred.questbook_clear_marker() Clear the forced primary arrow (the engine's slot-3 marker).

The engine's quest registry

The registry the game itself drives — see Native Quests. These are what nativequest.lua uses; a quest id in the registry also makes the SetUpQuest / TriggerQuest / ExitQuest / LoseQuest records work on it.

Function Description
sacred.quest_register(id, name) Put id in the engine's quest registry (id must be positive). The entry is re-created after every world load and savegame load.
sacred.quest_state(id) The live entry: { index, name, flags, setup, entered, done, trigger, on_enter, on_setup, on_exit, on_lose, sdk }, or nil.
sacred.quest_flags(id [, set, clear]) Read, or edit, the registry flags of one of your quests (bit 0 set up, 1 done, 2 entered).

Sections, objects and names

The SDK's own script sections, and the engine objects a record can address. See Vanilla Verbs and World Objects.

Function Description
sacred.section_define(name, bytes [, owner_quest]) Define (or replace) an SDK section from record bytes. It enters the engine's section table on the next tick and is found by name like a shipped one. owner_quest reinstates the engine's gate: the section stays dead until that quest is set up.
sacred.section_run(name [, handle]) Run a section — yours or a shipped one — on the next heartbeat. With a handle, the object behind it becomes the section's context (what a record means by "this object").
sacred.name_register(name) Give a new object name an id through the engine's own registrar (89 dynamic slots). Without it a CreateObj name is dropped and nothing can address the object.
sacred.object_by_name(name) The handle of a named world object.
sacred.object_at(type, kx, ky [, radius=2]) The nearest object of that type within radius tiles — no name needed.
sacred.kill_counters() / sacred.collect_counters() The engine's live objective counters ({type, left, total, section}).

State / vars

Sacred's named-state store (hq_uw, dq_belohnung, custom vars from q.var()), backed by the cQuestManager array. Values persist inside the save game. See examples/09_state_vars.lua.

Function Description
sacred.state_get(name) Returns the full {v1, v2, v3, v4} value array, or nil.
sacred.state_set(name, v0[, v1, v2, v3]) Overwrite a named state value in-place; returns bool.
sacred.state_dump() Log every named-state entry; returns the count.

The ctx:get_var / ctx:set_var methods are the convenience form (first value as an integer) — see the ctx table.

The vars wrapper

local V = require "vars" — the engine's script variables, the ones vanilla quests test and every savegame carries. Names are 1 to 31 characters and case-insensitive; give yours a prefix so they never meet vanilla ones.

Call Description
V.get(name [, default]) The value, or default when the variable does not exist.
V.set(name, value) Set it, creating it when missing. Needs a loaded world.
V.inc(name [, n]) / V.dec(name [, n]) Add or subtract n (default 1), as vanilla IncVar / DecVar do.
V.set_bit(name, b) / V.clear_bit(name, b) / V.bit(name, b) Bits 0..31. The name "HeroQBit" addresses the hero's own quest bits, which travel with the hero's save (SDK bits 150-159).
V.all(prefix) / V.dump(prefix) The variables with a prefix / write them to the log.
V.on_ready(fn) fn(loaded) once per world: when its scripts are loaded and the hero has been in it for about 2 s. loaded is true after a savegame load. The moment to spawn, arm zones and read saved state; anything created earlier does not appear.
V.is_ready() True while the world the last on_ready ran for is still the current one. Check it in on_tick.
V.world() The world serial (sacred.world_serial()) the last on_ready ran for.

Hero / player

Function Description
sacred.hero_pos() Hero world coords x, y (KompassPos space), or nil if not resolved.
sacred.hero_type() The hero's creature type, or nil with no hero: 1 Seraphim, 2 Gladiator, 3 Battle Mage, 4 Dark Elf, 5 Wood Elf, 6 and 7 Vampiress (day, vampire form), 8 Dwarf, 9 Daemon.
sacred.set_new_game_spawn(kx, ky [, hero_types]) Every new game starts the hero at (kx, ky) instead of the campaign start; savegame loads are never moved. hero_types = { 6, 7 } limits it to those heroes, and any other hero starts where the campaign puts him (the log says hijack dropped). No arguments: off.
sacred.give_gold(N) Global form of ctx:give_gold — usable from on_tick/on_world_load; N>0 grants, N<0 charges.
sacred.set_spawn(kx, ky) Teleport the active hero to KompassPos now; returns bool (false until the hero chain resolves).
sacred.arm_spawn_teleport(kx, ky) Arm a one-shot rewrite of the engine's own campaign start placement to (kx, ky). Call from on_world_load.
sacred.read_save(path) Read a hero .pax save; returns { class_id, class_name, level, gold, xp, underworld } or nil.
sacred.hero_weapon_dump() Diagnostic: log the hero's equip slots + item types.

The hero's items

The engine's own inventory: the 19 equipment slots, the backpack grid and the quest-item list. reward.lua wraps these for the common cases.

Function Description
sacred.hero_items() Every item the hero carries: { where = "equip"/"bag"/"quest", slot, ref, type }.
sacred.hero_item_count(type) How many of that item type the hero has, anywhere.
sacred.hero_has_item(type) True if the hero has at least one.
sacred.hero_put_item(type) Create one and put it in the backpack, through the engine's own putItem — a normal item the player can use, sell or drop.
sacred.hero_take_item(type [, n=1]) Take items back, the way the engine's own DelOBJ does (held, worn or in the bag). Returns how many went.

reward.give_quest_item(type) uses a different route (the CreateObj quest-item operand): it lands on the quest-item list, not in the backpack.

Hero quest bits

The per-hero, per-difficulty bit array in cStatsManager — it travels with the hero rather than with the world, which makes it the right place for "this hero has already done this".

Function Description
sacred.hero_qbit(n) Read bit n on the current difficulty.
sacred.hero_qbit_set(n, on) Write it.
sacred.difficulty() The current difficulty index.

Vanilla uses bits 0..11 and 101..106; the SDK keeps to 150..159. The engine's own SetVarBit record with the name "HeroQBit" writes the same array (Vb.set_var_bit("HeroQBit", n)).


Data tables

Read-only lookups against the SDK's compiled-in port of Sacred's data tables. No game state required — usable any time, including at bake.

Function Description
sacred.creature_name(id) Creature type name; returns name, note, band (band = hero/monster/humanoid_npc/animal/townsfolk).
sacred.combat_art(packed_id) Combat-art name; returns name, class_name, note.
sacred.companions(class_id) Companion info for hero class 1..9 (only 3/4/5/6 have one): { class_name, model_res, name_res={...} }.
sacred.class_skills(class_id) Per-class skill layout for class 1..9: list of { slot, id, name } (empty slots skipped).
sacred.skill_name(id) English skill name for id 0..33.
sacred.xp_for_level(level) Cumulative XP to reach 1-based level (1..206).
sacred.survival_bonus(deaths) Survival bonus percentage (0..99).
sacred.bonus_name(packed_id) Item-bonus name (German).
sacred.hash(name) Sacred's resource-name hash of name, masked to 31 bits.

Diagnostics / engine

Read-only instrumentation for SDK contributors and live RE. Most log to sdk_loaded.log; the *_dump family logs and returns nothing useful to a mod.

Function Description
sacred.scan_creatures([filter_type=-1[, cap=800]]) Log live creatures in the world, optionally filtered by type.
sacred.npc_dump(handle[, tag]) Log render-suspect fields of one creature.
sacred.npc_peek(handle) Read-only SEH-guarded field reads; returns v14, v245, vC, v204 (diagnostic).
sacred.roster_dump([tag]) Log the companion-roster array (qm+0x31c) (diagnostic).
sacred.trigger_table_dump([want_name]) Walk and log the engine's trigger-name table; returns the entry count (diagnostic).
sacred.dump_vanilla_of(type, ai_class[, skip_lo[, skip_hi]]) Log a vanilla CreateNPC record template for the given type/ai_class (diagnostic).
sacred.arm_hwbp(handle[, offset=0xc]) Arm a hardware data breakpoint on a creature field via DR0/VEH — must be called from on_tick (deep RE; read-only).
sacred.resolve_engine() Resolve/verify engine VAs now; returns { inflate, inflate_init, debug_log, resolved } (diagnostic).
sacred.globalres(handle) Resolve a global.res string handle to its UTF-8 text via the engine's own resolver; nil on miss.

The npcobj wrapper

require "npcobj" returns an OOP wrapper around a spawned creature handle — the high-level, recommended way to work with runtime NPCs. spawn returns an object you can stash in a global or quest table; the handle stays valid for the creature's lifetime, and :alive() tells you if it's still there.

local N   = require "npcobj"
local NPC = require "npc"        -- 474 creature-type constants

local guard = N.spawn(NPC.SKELETON, 2793, 2273)
guard:make_guard(30)             -- ally soldier: awake + class-default + AI
guard:save("townguard")          -- persist under a name; N.get("townguard") later

Constructors

Method Description
N.spawn(type, kx, ky) Spawn at KompassPos via the hero-sector path; returns an Npc or nil, reason.
N.spawn_here(type) Spawn at the hero (no coords).
N.spawn_template(arch, opts) THE recommended spawn: build a dev-authored CreateNPC record from npc_templates.lua and let the engine create+init it (type-correct stats). Archetypes include quest_npc, friendly_town_guard, named_enemy, merchant / smith / trainer, named_guard (a named fighter; opts.weapon), dormant_group (peaceful until SetGroupState; opts.group, opts.hook) and mount (a horse). See Runtime NPCs.
N.spawn_item(item_type, kx, ky) Spawn a ground item; returns the raw handle (items aren't cCreatures).
N.wrap(handle) Wrap an existing numeric handle.
N.get(name) / N.forget(name) Look up / drop a saved NPC in the named registry.

Identity & state

Method Description
o:handle() The raw integer handle.
o:info() { type, kx, ky, faction } or nil.
o:alive() Handle still resolves to a live creature?
o:type() Creature type id.
o:pos() KompassPos kx, ky.
o:faction() / o:set_faction(v) Get / set the side word.
o:save(name) Persist in the module registry; returns self for chaining.

Behavior archetypes

Method Description
o:wake() Turn on the AI.
o:stance(mode, val) Set behavior stance (mode 0 = class-default).
o:activate() Class-default stance + AI on (the one-call "behave like your kind").
o:set_level(n) / o:set_hp(n) Level / current+max HP.
o:make_aggressive(level) Hunts the hero on sight (explicit stance 2 + awake bit + AI).
o:make_hostile(fac, level) Class-natural: only fights back when hit.
o:make_friendly(fac) Friendly side + class-default behavior.
o:make_combatant(level, ai_class) Replay the engine combat init (ai_class 7 = active ally, 2 = monster, 13 = immune).
o:make_soldier(level, hp) A tough ally DEFENDER that fights monsters, never the hero.
o:make_guard(level) The vanilla ally-guard combo (awake + class-default; patrols and defends).
o:make_immortal_passive(level) Passive, immortal, holds post (captain type).
o:set_invulnerable(on) Essential / invulnerable.
o:set_stationary(on) Hold post vs. roam.

Storyline & dialog

Method Description
o:set_name(name) / o:name() Custom display name.
o:quest_icon(on) Floating "?!" glyph.
o:teleport(kx, ky) / o:walk_to(kx, ky) Engine teleport to a point (walk_to is the same teleport). The NPC's home stays behind — see below.
o:bind_quest(name, icon_on) Create the engine DlgNPC entry (name + overhead marker); returns the index. Calling it again re-binds the same entry, which makes a muted NPC talk again.
o:dialog{ text, buttons } The NPC's own dialog node: our text and up to 4 buttons { label, on, records }. records run inside the engine on the click, on(o) on the heartbeat after. See Runtime NPCs.
o:make_quest_giver(name, level) One call: immortal + holds post + bind (a walk-up-and-talk NPC).
o:say(text_key, vanilla_node, voice) Speak baked text; with vanilla_node does a native by-name swap (see Dialog Text). The override is dropped when the NPC dies, vanishes or its handle is reused.
o:dialog_off(next_key) Close the armed dialog, clear the marker, mute the NPC; next_key = its line from then on.
o:set_talkable(on) Ignore clicks (keeps the nameplate) / restore.
o:icon(kind) / o:node(name) Vanilla SetIcon ("offer", "handin", "none") / move to another dialog node (window and glyph).
o:equip(item_type, slot) Give a visible item (slot default 0x0D = main hand).

Walking and home

Method Description
o:go(kx, ky [, on_arrive [, opts]]) Walk there with NPC_Goto, watched: re-sent when stuck ~4 s, put on the spot after opts.tries (3). Also moves the NPC's home there. on_arrive(o, placed) once. Paused while the NPC talks.
o:walking() / o:arrive() Still on the way? / end the walk now: put it there and fire on_arrive.
o:place(kx, ky) Teleport and make that spot its home, so it stays around.
o:home(kx, ky) Set the home only (SetNPCState 4d). The idle AI walks a creature that strays ~3.7 tiles from its home back there.
o:state(...) / o:state_record(...) Run / build a SetNPCState record with Vb.ST sub-ops; a bound, unmuted NPC gets its dialog node re-bound in the same record. Needs the spawn name.

Companions

Method Description
o:make_companion(quest_id [, opts]) The engine's follow command: follows the hero, on his side, keeps its level, portrait in the party panel. opts.combat = true gives the hireling AI (attacks what the hero attacks); default: follows and defends itself. quest_id is ignored.
o:dismiss() Stop following, restore neutral AI, leave the panel.
o:despawn() Clean engine removal (the Npc is dead after this).
o:set_disposition(d) 'hostile' / 'ally' / 'neutral'; re-aggros immediately.

Talk detection

Method Description
o:in_dialog() Is the player talking to this NPC right now?
o:on_talk(fn) Rising-edge: fn(self) once each time the player starts talking. Re-arms across death/respawn.
o:on_talk_end(fn) Falling-edge: fn(self) once each time the talk window closes (use for effects that should land after the player reads the line).
o:on_answer(fn) fn(self, answer_index) once per answer on this NPC's window (the level cCreature+0x150 == 7 dropping).

The persona wrapper

local P = require "persona" — a named NPC that is spawned once and adopted again after every savegame load, instead of being spawned twice. Full description: Runtime NPCs.

Call Description
P.define(id, spec) spec.type (required), name, template, sub_id, home = {x, y}, immortal, hp, companion, when, setup(o, adopted). id: at most 10 characters.
P.watch() Bring every persona into each ready world; keep positions, immortality and walks on the heartbeat.
P.get(id) / P.ensure_all() Look one up / bring all in now.
p:npc() The Npc wrapper, or nil.
p:move_to(x, y [, walk]) / p:companion(on) / p:immortal(on) Walk there (or place with walk = false), join or leave the party, switch immortality.

Hero classes

local CM = require "classmod" — a playable class slot with another creature's body. Full page: Hero Classes.

Call Description
CM.replace(class, spec) class: C.SERAPHIM ... C.DAEMON from require "classes", or the key string. spec.name, spec.info, spec.body (a creature type, or { [6] = day, [7] = night } for the Vampiress), spec.parts = false, spec.portrait = false, spec.armor = true, spec.preview_items = true.

A mod for some classes

require "classes" also tells a mod which class the hero is, and scopes a mod to some classes. Every mod runs in every game, so a storyline written for one class registers its runtime code through a scope; heroes of other classes play the vanilla campaign.

local C    = require "classes"
local ONLY = C.only(C.VAMPIRESS)

ONLY.on_ready(function(loaded) ... end)      -- V.on_ready, a Vampiress world only
ONLY.on_tick(function() ... end)             -- sacred.on_tick, a Vampiress world only
sacred.set_new_game_spawn(2796, 2261, ONLY.types)   -- { 6, 7 }
P.define("roch", { type = NPC.ROCHEFORD, when = ONLY.active, ... })
NV.keep_givers_talking(ONLY.active)
Call Description
C.hero() The hero's class entry (C.DWARF, ...), or nil in a menu.
C.hero_is(...) Is the hero of one of these classes? Entries, keys ("DWARF"), dirs or abbreviations.
C.by_type(t) The class of a hero creature type. Each entry's types lists its own.
C.only(...) A scope for those classes: on_ready(fn), on_tick(fn), active() and types. Each world logs once whether the scope runs ([classes] hero type 8 (Dwarf): the mod for Vampiress is skipped).

active is the when argument that persona (spec.when), novanilla.keep_markers_hidden, novanilla.keep_givers_talking and openworld.keep_open accept. Section triggers need no scope: their sections run only from the mod's own records. The SDK's Vampiress storyline (bin/TYPE_NPC_VAMPIRELADY/FunkCode.lua) is written this way.


The zones wrapper

local Z = require "zones" — the engine's area triggers and trigger objects, with the behaviour an author expects. Full page: Zones and Barriers.

Call Description
Z.define(name, spec) Define a zone. spec.rect = {x1,y1,x2,y2}, spec.records = vanilla records the engine runs at the step, on_enter(z, who) / on_leave(z) / on_inside(z, who), once, raw, repeat_delay, grace.
z:arm() / z:disarm() Register the rectangle with the engine, or take it down. Arm on every world load.
z:contains(x, y) / z:hero_inside() Geometry, without the engine.
Z.get(name) / Z.all() Look one up, or all of them.
Z.barrier(name, cells [, opts]) An invisible blocker over map cells; starts closed.
b:close() / b:open() / b:lock() / b:unlock() Shut the road, or open it.
Z.line(x1, y1, x2, y2 [, thick]) Build a straight line of cells for a barrier.

By default a zone fires once per entry (the handler deletes its own rectangle and the module puts it back when the hero leaves) because the raw engine trigger runs the handler every heartbeat, once per creature inside.

Clone this wiki locally