Skip to content

Configuration ​

Every option lives in config.lua. After a change, run restart leno_spirit. To preview poses without editing the file, use /spirittest <id> <pose> (see Commands).

General ​

OptionDefaultDescription
Config.Notify'default''default' (GTA feed) | 'ox_lib' | 'okokNotify' | 'qb' | 'esx' | 'custom'. If the chosen resource isn't started, the GTA feed is used.
Config.Locale'en'Key in Config.Locales. en and pt are included.
Config.Permissions.admin'leno_spirit.admin'ACE permission for the admin commands
Config.Commandsspirit, spirittest, spiritadminCommand names
General.enabledtrueMaster switch. /spiritadmin on can still force the effect on.
General.activePeriod'always''always', or a list of months: { 10 } = October only, { 10, 11 } = October and November. Uses the server clock.
General.visibility'nearby''local' = only the player who died sees the spirit. 'nearby' = every player within nearbyRadius (needs OneSync).
General.nearbyRadius40.0Metres
General.triggerMode'native'When the effect fires. See Trigger modes.
General.cooldownSeconds3Minimum time between two spirits of the same player
General.startDelayMs1200Delay after death before the spirit appears, so the body falls first
General.maxSpirits4How many spirits one client draws at the same time

Trigger modes ​

ModeFires whenNotes
'native'GTA kills the player's pedWorks on every server. On qb-ambulancejob and wasabi_ambulance, this is when the player goes down (last stand).
'framework'The framework marks the player deadESX: esx:onPlayerDeath. QBCore: metadata.isdead changes to true.
'manual'Your script calls the exportNo automatic detection. Use it with exports.leno_spirit:Trigger(). See Exports and Events.

Spirit ​

OptionDefaultDescription
Spirit.pose'meditate'Key in Config.Poses
Spirit.riseHeight2.0Metres the spirit floats up
Spirit.durationMs6000Time from appearing to vanishing
Spirit.startAlpha153Starting transparency: 51, 102, 153, 204 or 255
Spirit.fadeFrom0.45When the fade-out starts, as a fraction of durationMs
Spirit.spin90.0Degrees the spirit turns over the whole rise. 0 = no spin.
Spirit.removePropsfalseRemove hats and glasses from the spirit

INFO

GTA only renders entity transparency in steps of 51, so the fade happens in a few visible steps. The finish particle burst covers the moment the spirit disappears.

Poses ​

Each pose is a base-game animation:

FieldDescription
dict / nameAnimation dictionary and clip
heightAboveGroundStart height above the ground under the body. Lying poses are drawn about 1 m below the ped's origin, so 1.0 makes them start inside the corpse.
PoseLook
meditateSeated, cross-legged
lyingOn the back, arms by the sides
sunbatheOn the back, relaxed
deadThe prologue "dead" pose
tposeArms open, legs apart

To add your own pose, add an entry with any base-game dict and name. If the animation can't be played, the spirit isn't shown, and only the particles and sound play.

Particles ​

start, trail and finish are one-shot base-game effects. Set any of them to false to skip it.

FieldDescription
asset / nameParticle dictionary and effect
scaleSize, 1.0 = normal
offsetZHeight in metres, positive = higher, negative = lower. start is measured from the body, trail from the spirit, and finish from where the spirit vanishes.
intervalMstrail only: time between two trail bursts
OptionDefaultDescription
Particles.enabledtrueTurn all particles on or off
Particles.finishHideDelayMs150The spirit stays visible this long after the finish burst, so the burst hides its disappearance

Sound ​

start plays when the spirit appears and finish when it vanishes. Set either to false to skip it.

OptionDefaultDescription
Sound.enabledtrueTurn all sounds on or off
Sound.range30Metres
start / finishShort_Transition_In / Short_Transition_Outname + set of a GTA sound

Any AudioName + AudioRef pair from soundNames.json works as name + set. Sounds from DLC sets may stay silent, because they need extra audio banks or a newer game build. An invalid sound stays silent and causes no error.

Notifications and languages ​

  • Config.Notify = 'custom' calls Config.CustomNotify(message, ntype), where ntype is 'inform', 'success' or 'error'.
  • To add a language, copy the en block in Config.Locales, translate it under a new key, and set Config.Locale to that key. Missing keys fall back to English.

Default config.lua ​

lua
--[[
    add_ace group.admin leno_spirit.admin allow
]]

Config = {}

-- 'default' | 'ox_lib' | 'okokNotify' | 'qb' | 'esx' | 'custom'
Config.Notify = 'default'

-- Locale key in Config.Locales
Config.Locale = 'en'

-- Add to server.cfg:
-- add_ace group.admin leno_spirit.admin allow
Config.Permissions = {
    admin = 'leno_spirit.admin',
}

Config.Commands = {
    toggle = 'spirit',      -- everyone: show / hide spirit effects for yourself
    test = 'spirittest',    -- admin: /spirittest [id] [pose]
    admin = 'spiritadmin',  -- admin: /spiritadmin on | off | auto
}

Config.General = {
    enabled = true,
    -- 'always' | { 10 } | { 10, 11 }  (months 1-12, server clock)
    activePeriod = 'always',
    -- 'local' (only the dead player) | 'nearby' (everyone in radius, needs OneSync)
    visibility = 'nearby',
    nearbyRadius = 40.0,
    -- 'native' (GTA death, = going down on qb/wasabi) | 'framework' (esx:onPlayerDeath / qb metadata.isdead)
    -- | 'manual' (only exports.leno_spirit:Trigger() from your own death script)
    triggerMode = 'native',
    cooldownSeconds = 3,
    startDelayMs = 1200,
    maxSpirits = 4, -- per client, at the same time
}

Config.Spirit = {
    pose = 'meditate',  -- key in Config.Poses
    riseHeight = 2.0,   -- metres
    durationMs = 6000,
    startAlpha = 153,   -- 51 | 102 | 153 | 204 | 255 (GTA renders alpha in steps of 51)
    fadeFrom = 0.45,    -- 0.0 - 1.0 of durationMs
    spin = 90.0,        -- degrees over the whole rise, 0 = none
    removeProps = false,
}

-- Base-game animations. Preview in game: /spirittest <id> <pose>
-- heightAboveGround: start height above the ground under the body (lying poses: 1.0 = inside the corpse).
Config.Poses = {
    lying = { dict = 'mini@cpr@char_b@cpr_def', name = 'cpr_pumpchest_idle', heightAboveGround = 1.0 },
    sunbathe = { dict = 'amb@world_human_sunbathe@male@back@base', name = 'base', heightAboveGround = 1.0 },
    dead = { dict = 'missprologueig_6', name = 'lying_dead_player0', heightAboveGround = 1.0 },
    meditate = { dict = 'rcmcollect_paperleadinout@', name = 'meditiate_idle', heightAboveGround = 1.0 },
    tpose = { dict = 'missfam5_yoga', name = 'a2_pose', heightAboveGround = 1.0 },
}

-- One-shot base-game effects. Set start / trail / finish to false to skip one.
-- offsetZ: height in metres (+ up / - down). start: from the body, trail: from the spirit, finish: where it vanishes.
Config.Particles = {
    enabled = true,
    finishHideDelayMs = 150, -- spirit stays visible this long after the finish burst
    start = { asset = 'scr_rcbarry1', name = 'scr_alien_teleport', scale = 1.0, offsetZ = 0.0 },
    trail = { asset = 'scr_rcbarry1', name = 'scr_alien_teleport', scale = 0.55, offsetZ = -0.7, intervalMs = 700 },
    finish = { asset = 'scr_rcbarry1', name = 'scr_alien_disintegrate', scale = 0.6, offsetZ = -0.7 },
}

-- Base-game sounds (AudioName + AudioRef from DurtyFree soundNames.json). Set start / finish to false to skip one.
Config.Sound = {
    enabled = true,
    range = 30,
    start = { name = 'Short_Transition_In', set = 'PLAYER_SWITCH_CUSTOM_SOUNDSET' },
    finish = { name = 'Short_Transition_Out', set = 'PLAYER_SWITCH_CUSTOM_SOUNDSET' },
}

-- Used when Config.Notify = 'custom'. ntype: 'inform' | 'success' | 'error'
Config.CustomNotify = function(message, ntype)
    TriggerEvent('chat:addMessage', { args = { 'Spirit', message } })
end

Config.Locales = {
    en = {
        spirits_hidden = 'Spirit effects hidden.',
        spirits_shown = 'Spirit effects visible.',
        no_permission = 'You are not allowed to use this command.',
        admin_on = 'Spirit effect forced ON for everyone.',
        admin_off = 'Spirit effect forced OFF for everyone.',
        admin_auto = 'Spirit effect follows the config again.',
        admin_usage = 'Usage: /%s on | off | auto',
        test_played = 'Spirit effect played on player %s.',
        player_not_found = 'Player not found.',
        help_toggle = 'Show or hide spirit effects',
        help_test = 'Play the spirit effect (admin)',
        help_admin = 'Turn the spirit effect on, off or back to the config (admin)',
        help_id = 'Player id (empty = yourself)',
        help_pose = 'Pose: lying, sunbathe, dead, meditate, tpose (empty = config)',
        help_mode = 'on | off | auto',
    },
    pt = {
        spirits_hidden = 'Efeitos de espírito escondidos.',
        spirits_shown = 'Efeitos de espírito visíveis.',
        no_permission = 'Não tens permissão para usar este comando.',
        admin_on = 'Efeito de espírito LIGADO para todos.',
        admin_off = 'Efeito de espírito DESLIGADO para todos.',
        admin_auto = 'O efeito de espírito voltou a seguir a config.',
        admin_usage = 'Uso: /%s on | off | auto',
        test_played = 'Efeito de espírito mostrado no jogador %s.',
        player_not_found = 'Jogador não encontrado.',
        help_toggle = 'Mostrar ou esconder os efeitos de espírito',
        help_test = 'Mostrar o efeito de espírito (admin)',
        help_admin = 'Ligar, desligar ou voltar à config (admin)',
        help_id = 'ID do jogador (vazio = tu)',
        help_pose = 'Pose: lying, sunbathe, dead, meditate, tpose (vazio = config)',
        help_mode = 'on | off | auto',
    },
}