Project Mammut is a HTML5 game engine. Scene scripts are written in TypeScript or JavaScript, and new scenes start in TypeScript. The examples on this page are TypeScript.
Every scene in a project has its own script, made of lifecycle hooks and any custom methods you add. This page is a reference for what's available to write against.
Scene lifecycle
onEnter() {
// Scene is about to be shown.
},
onStart() {
// Called after onEnter, once attached scenes are ready. this.dom is populated here.
},
onExit() {
// Scene is being removed. Clean up timers/listeners here.
},
// Add your own methods too. To run one from a chapter's [ActionName(...)] tag, register it with Revealer.Register (see below).
Inside any of these, and any handler registered with this as its owner, this is the scene instance. this.dom, this.root, this.vars, and any custom methods or properties you've added are all available.
this, the scene instance
this.root/this.dom.root: the scene's own root element, which gets the same helpers as any other node (see below).this.dom.<nodeId>: every node marked "referenceable" in the Scene Editor, by id.this.vars: the scene's variables, from its Scene Variables list in the inspector.this.attachedScenes: child scenes attached to this one, populated automatically for "scene" nodes.
DOM node helpers
Attached automatically to every node in this.dom (see _setupDomHelpers in sceneBase.js):
hide(),show(),isHidden(),toggleVisibility().hidden(boolean): toggle a shared hidden class, independent of the node's own Visible flag and of each other's internal state.disableEvents(),enableEvents(): block or restore mouse and pointer interaction.setState(name, repeat = true): swap in a named CSS state class. Returns aPromisethat resolves when that state's CSS animation finishes, or immediately if it has none:await this.dom.card.setState("flip");- Animator nodes:
play(clip),pause(),resume(),stop(),restart(clip). - Particle nodes:
start(),stop(),pause(),resume(),burst(n).
Revealer: text reveal and markup events
Revealer.Init(this.dom.text_box); // or { clickTarget: this.dom.next_button } to click only that
const ctrl = await Revealer.StartRevealChapter("my_chapter");
Revealer.Register("SetCharacter", (event) => {
const key = event.args[0];
const variant = event.kwargs?.variant;
});
// A scene method, with the scene as owner
Revealer.Register("SectionStart", this.onSectionStart, this);
Register(eventName, handler): callshandlerevery time the event fires.Unregister(eventName, handler)removes one handler, andunregisterAll(eventName)removes every handler for that event.- Owner: every manager's
Register(Revealer, Store, InputManager, AudioManager and Logger) takes an optional owner as its last argument, usuallythis. The handler is then called with your scene asthis, and it's removed automatically when the scene exits, so you don't need anUnregisterinonExit.Unregister(eventName, this.onSectionStart)still removes it early, andUnregisterScene(this)removes everything the scene registered with that manager. - Every handler receives one event object:
event.name,event.args(positional),event.kwargs(namedkey=value), plus event-specific fields likeevent.optionsorevent.char. - Built-in lifecycle events:
NodeStart,NodeEnd,SectionStart,SectionEnd,WaitingForClick,OptionsReady,OnTextCharPlace,SetCharacter, plus any custom[ActionName(...)]tag you use in dialogue. Pause()/Unpause()/IsPaused(): freeze or resume the reveal. Returning aPromisefrom a handler does this automatically until it resolves.RevealNode(nodeId): jump straight to a node.- Full reference:
/revealer-reference.html, shipped with the app.
Assets: reading project data
Assets.GetCharacter("alice"); // { key, name, image, variants, ... }
Assets.GetChapter("chapter_01");
Assets.GetImage("bg_forest");
Assets.GetAudio("theme_music");
Assets.GetVariant("alice", "smiling"); // a specific variant object
Assets.Get(key, kind); // generic lookup
Assets.GetAll(kind); // every asset of a kind
Assets.Register(name, kind, data); // register something at runtime
Store: persistent variables
Store.Get("playerName", "Alex"); // reads, seeding the default if unset
Store.Set("playerName", "Sam");
Store.Register("playerName", (newVal, oldVal) => { /* react to changes */ });
Store.Unregister("playerName", handler);
Store.Register("playerName", this.onNameChanged, this); // scene as `this`, removed on scene exit
Store.GetCharacterTarget(); // { key, variant } that $char and $variant use in chapters
Store.SetCharacterTarget("alice", "smiling"); // same as {character(alice, variant=smiling)}
{$varName} and {set($var, expr)} in dialogue text read and write through this same store. Character variables that chapters change are kept on Assets.GetCharacter(key).variables and saved with the game (see Logic).
SceneManager
SceneManager.SwitchScene("main_menu");
SceneManager.GetScene("settings_panel")?.togglePanel(); // reach another loaded scene directly
SceneManager.GetCurrentScene();
await SceneManager.LoadScene("settings_panel"); // ensure it's loaded before calling into it
SceneManager.AttachScene(name) / DetachScene(name);
Every scene registers itself globally by filename once loaded, so GetScene can reach any currently-loaded scene, sibling or not.
AudioManager
AudioManager.PlayMusic("theme"); // crossfades
AudioManager.StopMusic(fadeSeconds);
AudioManager.PlayAmbient("rain");
AudioManager.StopAmbient(fadeSeconds);
AudioManager.PlaySFX("click");
AudioManager.PlayVoice("line_042");
AudioManager.SetMasterVolume(0.8); // also Music/Sfx/Ambient/Voice
AudioManager.GetMasterVolume();
AudioManager.ApplySettings(settings);
AudioManager.SaveSettings() / LoadSettings();
AudioManager.Register("SFXPlayed", handler); // and other audio events
InputManager
InputManager.Register("KeyDown", (e) => { if (e.code === "Space") jump(); });
InputManager.Register("KeyDown", "Space", (e) => jump()); // pre-filtered by the manager
InputManager.Register("GamepadButtonDown", InputManager.GamepadButton.A, (e) => jump());
InputManager.IsKeyDown("ArrowLeft");
InputManager.IsGamepadButtonDown(gamepadIndex, button);
code is the physical key, layout-independent (e.g. "Escape", "Space"); key is the layout-aware character. GamepadButton has named constants (A, B, X, Y, LeftBumper, DPadUp, and so on) for the standard controller mapping.
Application
Application.GetPlatform(); // "tauri" or "web"
Application.Quit();
Application.Restart();
Application.EnterFullscreen() / ExitFullscreen() / ToggleFullscreen() / IsFullscreen();
Application.OpenURL(url);
Fullscreen uses the browser's Fullscreen API on web and the native OS window under Tauri. That means Escape always exiting fullscreen is a web-only browser restriction, not something the desktop build has to work around.
SaveManager
SaveManager.Save(slot);
SaveManager.Load(slot);
SaveManager.Exists(slot);
SaveManager.GetSlotInfo(slot); // { date, scene }
SaveManager.GetGameSettings() / SetGameSettings(settings) / SaveGameSettings();
Logging
log("hello");
logWarning("careful");
logError("something broke");
Logger.Register((level, args) => { /* pipe to your own UI, etc. */ });
Logger.Unregister(handler);
Debugging
You can learn how to debug your projects here.