Script-owned cinematics

An entity or scene script can temporarily take control of simulation from its update function:

local player = game.this_player
if game.begin_cinematic(player.pack_name, "Police cinematic REV00", player.object_index, 1800) then
    return false
end

The script name has no .lua extension. Use the runtime pack_name of an entity from the target pack; this also works with Editor [draft] pack identities. Acquisition requires an existing player, enemy, or item owner, the InGame scene state, a loadable script, a positive deadline, and no active cinematic.

Starting the event stops the remaining normal actor/world update pipeline. Its script begins on the next host frame. While active, the normal scene hook, input processing, Twitch spawns, slow motion, actors, camera, animations, stage effects, and game clock remain suspended. There is no update whitelist or blacklist. The cinematic script's update function runs once per host frame and chooses every update.

Select existing enemies using is_enabled/is_alive and the intended entity classification, not is_active. The latter is an item/projectile flag; ordinary enemy spawning leaves it clear. Damage and death handling belong to the selected enemy controller: after queuing a hit, advance that controller and its animation while the event owns simulation. Clearing is_blocking alone does not change which native move controller runs. For an impact that must interrupt the current move, queue its hit fields and use check_being_hit() to consume the shared reaction before selectively advancing the resulting controller. Test blocking, airborne and boss reactions as well as standing enemies.

Include enemies with extra health bars in the verification; current health alone does not represent their remaining life.

API Behavior
game.is_cinematic Whether an event owns simulation.
game.cinematic_tick Zero-based host tick, saved in rollback snapshots.
game.cinematic_owner_id Owner's object ID; -1 outside a cinematic.
game.get_cinematic_value(index) / game.set_cinematic_value(index, value) 32 event-local integers, indexed 0..31. The optional presentation API reserves 29..31 when used. Values reset per event and do not overwrite scene custom values.
game.update_entity_controller(id) Advance one player, enemy, or item through its full controller, including its usual physics/position resolution and pack scripts. Does not advance animation.
game.update_entity_animation(id) Advance only that object's normal animation/hit-pause logic. Also supports render-only subitems.
game.is_cinematic_actor(id) Whether the event owns this helper slot. Select event helpers by ownership rather than matching their type to the owner.
game.schedule_cinematic_quake(amplitude_pixels, duration_frames) Schedule an exact alternating background-plane quake for this event. Amplitude is 1..8 pixels and duration is 1..600 display frames; invalid values and calls outside a cinematic are ignored. The event passes through the rollback-aware visual effect queue.
game.get_entity(id) Resolve a player, enemy, item, or subitem with its actual Lua wrapper; invalid IDs return nil.
game.first_item_id, game.item_capacity Item slot range.
game.first_sub_item_id, game.sub_item_capacity Render-only subitem slot range.
helper.initialize(pack_name, entity_name, animation_id, mode) Initialize a free item/subitem returned by a free-slot lookup; default mode is 2. Retains the slot's object ID and resets previous data.
game.end_cinematic(keep_camera) Release control, restore the camera state from acquisition (unless keep_camera is true: the camera stays where the script moved it, for a walk into the next room), and remove helpers initialized during this event.

For gravity without AI, adjust the actor's height/impulse fields directly, then resolve any required world collision through update_position_and_check_world_collisions. Do not also call the full controller unless a second physics step is intended. Render-only subitems have no built-in controller; a cinematic can execute its own Lua actor routine and selectively call the animation update.

Nested actor scripts temporarily change game.this_player, game.this_enemy, or game.this_item. The selective controller API restores the cinematic's context afterward. Cinematic scripts themselves have no implicit current entity; resolve the owner by ID. Re-entering an actor's controller recursively returns false.

The deadline, script failure, owner removal/identity change, and scene/level change also release control. No normal simulation update runs on that release frame. The cleanup list records created helper ownership only; it never determines which actors update. Existing actors and scene custom values remain pack-owned.

Keep all mutable state in cinematic values or actor model fields. Lua VM globals, module caches, and retained mutable upvalues are not rollback state. The update function's return value does not end the event; call end_cinematic explicitly. Cinematic state is included in GameModel copying, raw session serialization, and the session checksum. During a cinematic the checksum also includes helper and actor custom state used by selective simulation.

Stage presentation without gameplay transitions

A cinematic can temporarily display other stage art without changing the live fight's level, wave, loadcues, music or actor state. Do not use change_scene for this: a gameplay scene change ends the cinematic and reinitializes the scene.

API Behavior
game.get_scene_camera_path(level_id, scene_id) Read-only stage-wave geometry, or nil for invalid IDs. Coordinates are viewport pixels, positive right/down.
game.get_previous_presentation(level_id, scene_id) Previous wave's geometry; at level start, previous playable level's final valid wave; nil if absent.
game.get_previous_known_presentation(level_id, scene_id) Most recent earlier wave with known directional scroll geometry, scanning earlier scenes/levels and skipping unknown routes; nil if none.
game.set_cinematic_presentation(level_id, scene_id) Select resolved replacement/inherited art for valid loaded stage IDs. Returns false outside a cinematic or for invalid IDs. It does not change gameplay scene IDs.
game.set_cinematic_dark_presentation() Hide stage art and use a black backdrop. Clear the global cinematic fade to show owned helpers against that backdrop. Existing entities are masked separately.
game.restore_cinematic_presentation() Select the presentation captured at acquisition.
game.cinematic_fade Read/write integer 0..1000, clear to black. Snapshot-driven; the script owns fade timing.
game.cinematic_hide_existing_entities Read/write view mask for existing actors, bodies, shadows and effects. Cinematic-owned helpers remain visible. Original actor rendering flags are preserved.

Camera-path records expose level_id, scene_id, start_x, start_y, end_x, end_y, min_x, max_x, min_y, max_y, has_diagonal, diagonal_x, diagonal_y, previous_level_id, previous_scene_id, contiguous_previous, scripted, scroll_direction (+1 right, -1 left, 0 unknown), is_known_directional, has_authored_route, route_actor_y (the route's actorY, 0 when unset), playable_width, floor_far_y and floor_near_y. The floor values describe the entry viewport; its near edge at another camera Y is camera_y + 216. path.floor_far_at(world_x) also accounts for the native sloped wall segment and post-segment horizon at that world X. Vertical blockers, custom polygons and pits are separate collision geometry: path.is_ground_obstructed(x, y, margin) is true when world point (x, y), in pixels, is inside or within margin pixels of one of the wave's Wall, Pit or Custom2 custom wall shapes (the same test that keeps native jump-in respawns off holes). Use it to stage helpers on solid ground, for example by sampling a car's path across the view and moving its depth until clear. When the shown scene's route sets actorY, use camera_y + path.route_actor_y instead: the Streets of Rage 1 police car keeps the original's fixed line there (round 7), whatever the caller's depth. diagonal_x retains the native signed threshold, not a positive viewport coordinate. path.sample_y(x) returns unobstructed one-pixel native forward geometry. Player positioning can delay actual camera Y, and scripted stages can follow arbitrary paths; anchor a retreat to the actual acquired camera and do not infer a scripted route from the native fields. Optional scene cinematicCameraRoute declares absolute start/end viewport pixels for a known scripted direction; sample_y then samples its bounded linear Y instead of the native diagonal gate. Use playable_width for placement in the displayed level, whose rendering scale can differ from that of the live fight.

A fixed-camera wave (an elevator) can declare a cinematicCameraRoute too, over art drawn beside its room, so a retreat has somewhere to go; a round-clear scene that keeps its last wave's art should repeat that wave's route. When a cinematic has no room in the current wave and must borrow other art, walk back with get_previous_presentation while each previous wave shares the current art (contiguous_previous) and has no known route, and take the first wave with other art (the previous background or level, even a fixed-camera one, whose end_x is where its camera stopped). The Streets of Rage 1 police call does this. Because an authored route gives absolute camera positions, a camera that is off it (an elevator high above the street it starts from: its route lies at the street's camera Y) should first move onto it vertically and come back the same way: the Streets of Rage 1 police call scrolls down, then along the route, and back along it, then up. A route's actorY also places lanes that depend on the depth below the caller's camera (the police fire). Staging actors on walls: when no line across the shown view is clear of walls, rails and pits (a wave walled in, like an elevator), place the actor already inside, on ground clear under its whole length (and a band in front of it), instead of driving it in; and let effects that land on the ground (the police fire and rain) land only on walkable ground, falling on through a pit otherwise. An unhooked stationary final combat wave can retain the direction of its contiguous native scene while keeping its actual zero-span bounds. Exit barriers, script hooks and fixed-room/elevator cameras stop that inheritance.

The presentation API reuses cinematic values 29..31 rather than adding game or character state fields. Once used, keep script-owned values in 0..28. Helpers still use ordinary actor custom fields. IDs, fade and visibility therefore copy, serialize and hash through the existing cinematic state. Cleanup also restores presentation when the owner disappears, the deadline expires or Lua fails. Encoded zero IDs in values 29 and 30 select the dark backdrop; use the named API rather than writing this encoding yourself. It remains distinct from a full-screen fade, which also covers cinematic helpers.

Switch distant scenes while fully dark. Their render camera snaps under black, then follows the ordinary scripted-camera interpolation. Resource preparation does not unload music or change the currently displayed palette. Readiness stays local to the view: it holds black until requested art is loaded and never becomes a Lua simulation branch. Preserved native fade callbacks resume after cinematic control is released.