-
-
Notifications
You must be signed in to change notification settings - Fork 3
Cut Scenes
The engine can queue commands on an actor and play them out in order — an NPC walks somewhere and then does something, with no Lua in between. Sacred ships two such scenes; this page says exactly how much of that machinery an SDK mod can use, because part of it stays out of reach.
Verified on Sacred Gold Steam, build 2.0.2.28 (2006-10-13), measured over four live runs against the actor's own command queue.
A scene is one section: cinema mode on, the actor commands, cinema mode off. While cinema mode is on a command is not executed — it is queued on the actor it names, and the actors play their queues out in order. That is what turns a pile of records into a sequence.
local Sc = require "scene"
Sc.play(
Sc.walk_to("res:GUIDE", { sacred.hero_pos() }), -- coordinates, not "HERO"
Sc.anim("res:GUIDE", 89))| Step | Record | Works from an SDK section? |
|---|---|---|
Sc.walk_to(who, {x, y}) |
NPC_Goto | yes — queues and walks. |
Sc.walk_to(who, "HERO") |
NPC_Goto | no (see below). |
Sc.anim(who, id) |
PlayAnim | yes — queues and plays in turn. |
Sc.teleport(who, pos) |
Teleport | same queue kind as the two above. |
Sc.attack(who, target) |
Attack | same queue kind. |
Sc.fx(who, argb, fx) |
Partikel | same queue kind. |
Sc.focus(who) |
SetFocus | the camera; the addon's intro opens with it. |
Sc.wait_for(who, target) / Sc.wait(who, n)
|
Wait | no. |
Sc.talk(who, node) |
NPC_TalkTo |
no — use Sc.say instead. |
Other helpers: Sc.open() / Sc.close() run cinema mode on and off as separate
sections (the addon does exactly that — one section opens it, a timer section
closes it), Sc.queue(...) sends steps between them, Sc.abort() closes cinema
mode by hand, Sc.is_on() reads the engine's flag, and Sc.queued(handle)
returns how many commands an actor still has pending.
Measured, every 250 ms, against the actor's queue:
- Walking to coordinates and playing an animation queue and run in order. The queue climbed to 2 and drained while the actor walked tile by tile and then gestured on arrival. That is a real cut scene.
- Walking to a creature does nothing — no queue entry, no step — even though both shipped scenes use that form. Pass coordinates.
- Wait and NPC_TalkTo never reach the queue, in any arrangement: one section with everything, or one record per section, cinema mode opened inline or separately. The Wait handler wants more than the record — two resolved ids and a register the walker carries in from the record before it, which a section of ours does not reproduce.
So sequence the rest from Lua: a tick counter, or simply Sc.play for the two
steps that do queue.
There are two records that open the queue, and they are not the same:
| Record | Tag | What it sets |
|---|---|---|
| SetCinemaMode | 0x86 |
quest-manager flag bits 0 and 1, kernel event 0x13 {3,1}
|
| SetAnimMode | 0x58 |
bit 0 only, the same kernel event |
Reading the record walker suggests a trap in 0x86: when the hero's action queue
is empty it calls into the creature layer and returns from the walker, which
would abandon every record after it — exactly the shape of "the scene did
nothing". Measured 2026-09-12, it does not happen to us: both records queued the
walk and the animation (queue 0 → 2) and the section ran to its very last
record. The early exit reads the queue of the section's own creature, and an
SDK section runs with no creature at all, so the branch is never taken.
Vb.anim_mode() (0x58) is in the SDK anyway: it is the smaller hammer — the
queue without the camera — when all you want is an NPC to perform.
Sc.say(node) opens a dialog node's window through the Popup record. It needs
no NPC, works from anywhere, and shows the same window NPC_TalkTo would have
opened — so an NPC that walks up and then "speaks" is:
Sc.play(Sc.walk_to(GUIDE, { sacred.hero_pos() }), Sc.anim(GUIDE, 89))
-- a few seconds later, from your own tick:
Sc.say("MY_GUIDE_NODE") -- the node name you gave Npc:bind_questPopup binds the node to the hero for the length of the window, the way a signpost does. Live: an NPC answered a paid bribe this way, right after the button.
When nobody speaks at all — the hero's own thoughts, a note found on the ground —
Sc.note declares a node of its own the first time (the vanilla DlgNPC record,
Vb.declare_node) and opens it the same way. Live.
require("text").named("AXE_NOTE", "The axe is covered in blood, and the tracks lead east.")
Sc.note("my_axe_note", "AXE_NOTE", function() ... end) -- on_ok is optionalAn open cinema mode swallows actor commands, so:
-
Sc.playalways closes it in the same section; -
Sc.abort()closes it by hand if a scene was ever interrupted; -
Sc.is_on()tells you (it reads the engine's own flag), so a watchdog on your tick can close it after a timeout.
Worth reading before writing your own:
-
Daemonin_Tutorialandbtn_Deamonstart(base script of the Seraphim class) — the minimal shape: cinema on, walk, wait, talk, cinema off. -
Los_kampf1_anim(addon, Seraphim) — the full treatment: the actor's dialog unbound, the camera put on the hero, particles, an animation, thunder played on the actor, barriers and triggers built around the arena so the player cannot walk out, and cinema mode closed later from a timer section.
Getting started
Authoring
- Quests and Dialog Authoring
- Native Quests
- Runtime NPCs
- Hero Classes
- Dialog Text
- Dialog Nodes (catalogue)
The engine's own verbs
Reference