Skip to content

Animations

Core Cinematics is standalone and works on any framework, but to record + replay animated peds (emotes, walking styles, prop interactions) it needs an animation provider to be installed alongside it.

The recorder ships with an open contract — the Cinematic Anim Provider API — that any animation script can implement. We publish a reference implementation: a fork of rpemotes-reborn that drops in as a replacement for vanilla rpemotes.

Why This Matters

The in-editor recording system captures the full motion of every ped within RecordRadius — position, rotation, weapon state, aim — and replays them as puppets on your timeline. By default it does not capture which animation a ped is playing, because most animation scripts run purely client-side and the cinematics recorder has no hook to read them.

A registered Anim Provider adds that hook. With one running, every emote, dance, walking style, expression, and prop animation that a ped is performing during the recording window is saved alongside their motion data — and re-applied during playback, on the puppet ped, perfectly in sync with the camera.

WARNING

Without an Anim Provider, recorded peds will replay their movement, but their hands will hang at their sides — no emotes, no dances, no walking styles, no props.

Download (Reference Provider)

rpemotes-reborn
rpemotes fork with a built-in Cinematic Anim Provider implementation. Required for animation capture and playback if you don't already have a custom one.

Repository: github.com/deivismac/rpemotes-reborn

Setup (Customers)

1
Replace Your rpemotes

Remove your existing rpemotes resource from [c8re] (or wherever it lives) and replace it with the fork. The fork is a drop-in replacement — all of your existing emotes, walking styles, and config carry over.

resources/
  [c8re]/
    rpemotes-reborn/   # replaces vanilla rpemotes
    core_cinematics/

TIP

If you renamed the folder, update any ensure rpemotes line in your server.cfg to match. Keeping the resource name as rpemotes is fine — the fork is API-compatible.

2
Ensure Load Order

rpemotes-reborn and core_cinematics have no hard load-order requirement, but both should be ensured before any resource that triggers cinematic recordings programmatically.

cfg
ensure rpemotes-reborn
ensure core_cinematics
3
Verify in a Recording
  1. Have a player run an emote (e.g. /e dance1) within RecordRadius of the director
  2. Start a recording with /cinematics record
  3. Stop after a few seconds, open the editor, and play back
  4. The puppet ped should perform the same emote in sync with the recorded movement

If the puppet stands still during playback while the recorded ped was emoting, the provider isn't being detected — double-check the resource name and load order.

INFO

You don't need to change anything in core_cinematics config. The integration is automatic — the recorder detects any registered Anim Provider on the server and starts capturing animations the moment one becomes available.

For Animation Script Developers

If you ship your own animation/emote/walking-style script and want it to work with Core Cinematics out of the box, implement the Cinematic Anim Provider contract below. There is no registration step — the recorder discovers any resource that exposes GetAnimData automatically and registers it on the fly. Multiple providers can coexist.

What you need to implement

A provider has two halves: a state bag broadcast on the local player whenever an anim or walk is active, and a small set of exports the recorder can call to apply the same anim to a replay puppet.

1. State bag — cinematics:animProvider

Set on the local player whenever an anim or walking style is active. Clear (set to nil) when neither is active. Always include providerId set to your resource name — the recorder uses it to route the playback call back to the right script.

lua
LocalPlayer.state:set('cinematics:animProvider', {
    providerId = GetCurrentResourceName(),

    -- nil when no emote/anim is currently playing
    anim = {
        name       = 'wave',           -- machine-readable identifier
        dict       = 'anim@dict@id',   -- engine dict (used for phase seek)
        anim       = 'anim_name',      -- engine anim
        flag       = 1,                -- TaskPlayAnim flag (1 = loop)
        textureVar = 0,                -- prop texture variation, optional
        library    = 'Greetings',      -- optional menu category
    },

    -- nil when no walking style is set
    walk = {
        name    = 'drunk',
        clipset = 'move_m@drunk@a',
    },
}, true)

If only one of anim / walk changes, preserve the other field — read the current bag, mutate one field, write back. (See CinematicsBridge.lua in the rpemotes-reborn fork for a clean helper.)

2. Required exports

lua
-- Returns engine info for `name`, or nil if unknown.
-- Used by the recorder to seek a recorded loop's phase on the replay puppet.
exports('GetAnimData', function(name)
    return {
        dict       = 'anim@dict@id',
        anim       = 'anim_name',
        flag       = 1,
        label      = 'Wave',         -- human-readable
        library    = 'Greetings',    -- optional category
        textureVar = 0,              -- prop tex variation, optional
    }
end)

-- Plays `name` on ANY ped (not just the local player). Must handle prop
-- attach if your system has props for this anim.
-- opts may include: { textureVariation = N, flag = N }
exports('PlayAnimOnPed', function(ped, name, opts)
    -- ... your apply logic ...
    return true   -- or false on failure
end)

-- Stops the anim on `ped`, removes any attached props, clears tasks.
exports('CancelAnimOnPed', function(ped)
    -- ... your stop logic ...
end)

3. Optional exports — walking styles

lua
exports('GetWalkData', function(name)
    return { clipset = 'move_m@drunk@a', label = 'Drunk' }
end)

exports('SetWalkOnPed', function(ped, name)
    -- Apply the clipset to `ped`. Replay puppets are local entities so
    -- you don't need to network anything.
end)

exports('ClearWalkOnPed', function(ped)
    ResetPedMovementClipset(ped, 0.0)
end)

4. Optional exports — menus & filtering

lua
-- Flat list for the editor's emote / walk pickers. Skip categories that
-- don't make sense as "play this on a ped" (e.g. EXPRESSIONS, EMOJI).
exports('GetAllAnims', function()
    return {
        { name = 'wave',   label = 'Wave',  library = 'Greetings' },
        { name = 'dance1', label = 'Dance', library = 'Dances'    },
    }
end)

exports('GetAllWalks', function()
    return { { name = 'drunk', label = 'Drunk' } }
end)

-- If your provider spawns a preview puppet for its emote menu, return its
-- handle so Core Cinematics filters it out of the recording. Return 0
-- when no preview is active.
exports('GetMenuPed', function()
    return MyMenuPreviewPed or 0
end)

Reference implementation

The full source for the rpemotes-reborn fork's bridge — including the atomic state-bag helper, every export, and walk-on-puppet handling — lives at:

rpemotes-reborn/client/CinematicsBridge.lua

It's ~150 lines and reads top-to-bottom. Copy it as a template, swap the EmoteData / WalkData lookups for your own data structures, and you're done.

Frame format & backwards compatibility

The recorder writes per-ped frames as { emote, emoteVar, emoteType, emotePhase, emoteProvider, walkClipset, walkName, walkProvider, ... }. Old recordings made before the API existed (no emoteProvider field) still play back — the recorder falls back to scanning every registered provider for the anim name and routing through the first match. Your script doesn't need to do anything special to support old projects.

Discovery + multi-provider support

  • Discovery is pull-based: at startup the recorder probes every started resource for the GetAnimData export. Anything that responds is registered.
  • onResourceStart / onResourceStop keep the registry live, so installing or stopping a provider takes effect without a server restart.
  • Multiple providers can run simultaneously. Each anim/walk in the state bag carries its providerId; the recorder routes playback back to whichever provider published the recorded frame.
  • If two providers both expose an anim with the same name, the one whose state bag was active at recording time wins. For UI-initiated plays (e.g. the Scene Editor), the first matching provider is chosen.

INFO

There is no opt-in flag, no event to fire, no "register me" call. Just expose the exports and broadcast the state bag — the recorder picks you up automatically.