Cutscene System

Keyframe-based camera cutscenes with a visual HTML editor, smooth interpolation, fade transitions, per-keyframe easing, and player protection. Create cutscenes in-game, edit with a timeline GUI, play via script or command.

Requires: Forge 1.20.1 or Fabric 1.21.1. MCEF for the editor GUI.


Creating Cutscenes

/cnpcext cutscene create <name>          — Create + open editor
/cnpcext cutscene edit <name>            — Open editor for existing
/cnpcext cutscene delete <name>          — Delete
/cnpcext cutscene list                   — List all saved

Playing Cutscenes (Commands)

/cnpcext cutscene play <name>            — Preview on yourself
/cnpcext cutscene play <name> <player>   — Play on another player
/cnpcext cutscene stop                   — Stop your cutscene
/cnpcext cutscene stop <player>          — Stop a player's cutscene

Script API

// Play a saved cutscene
cnpcext.startCutscene(player, "village_intro")

// With options
cnpcext.startCutscene(player, "village_intro", JSON.stringify({
    speed: 1.5,        // playback speed multiplier (default 1.0)
    hideHud: true,     // hide all HUD elements (default true)
    protect: true,     // freeze + invulnerable (default true)
    bars: true,        // show cinematic black bars (default false)
    keepPosition: true // teleport player to last keyframe on end (default true)
}))

// Control
cnpcext.stopCutscene(player)         // stop + restore player
cnpcext.pauseCutscene(player)        // freeze timeline
cnpcext.resumeCutscene(player)       // continue from pause
cnpcext.isInCutscene(player)         // returns boolean

// Ad-hoc camera (no saved cutscene needed)
cnpcext.moveCamera(player, JSON.stringify({
    keyframes: [
        {x: 100, y: 70, z: -200, pitch: -15, yaw: 90, travelTicks: 0, holdTicks: 10},
        {x: 120, y: 80, z: -180, pitch: -30, yaw: 45, travelTicks: 60, holdTicks: 0}
    ],
    protect: true
}))

Phase Events

The cutscene(e) handler fires at each keyframe arrival on the script that started the cutscene:

function cutscene(e) {
    var name = e.cutsceneName   // "village_intro"
    var phase = e.phase         // keyframe index (0-based), or -1 for end

    if (name === "village_intro") {
        if (phase === 0) {
            // First keyframe — start NPC animation
        }
        if (phase === 2) {
            // Third keyframe — trigger dialogue
            cnpcext.pauseCutscene(e.player)
            cnpcext.openHtmlGui(e, "dialogue.html", 0, 0, JSON.stringify({text: "Welcome!"}))
        }
        if (phase === -1) {
            // Cutscene ended
        }
    }
}

function htmlGuiEvent(e) {
    if (e.eventName === "dialogue_done") {
        cnpcext.resumeCutscene(e.player)
    }
}

Keyframe Properties

Property Type Description
x, y, z number Camera world position
pitch, yaw number Camera look direction (degrees)
travelTicks int Ticks to travel from previous keyframe (0 = instant)
holdTicks int Ticks to hold at this position before advancing
transition string "travel" (smooth interpolation) or "fade" (fade to black)
fadeTicks int Fade duration for "fade" transitions (default 20)
easing string Interpolation curve (default "smoothstep")

Available easings: linear, smoothstep, easeIn, easeOut, easeInOut, sine, expo, circ, bounce, elastic, back


Options

Option Type Default Description
speed float 1.0 Playback speed multiplier
hideHud boolean true Hide all vanilla HUD elements
protect boolean true Freeze player (speed 0) + invulnerable
bars boolean false Show cinematic letterbox bars
keepPosition boolean true Teleport player to last keyframe position on end

Features

Storage

Cutscenes are saved as JSON files at <world>/cnpcextended/cutscenes/<name>.json. Loaded automatically on server start.