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
| Option | Default | Description |
|---|---|---|
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.Commands | spirit, spirittest, spiritadmin | Command names |
General.enabled | true | Master 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.nearbyRadius | 40.0 | Metres |
General.triggerMode | 'native' | When the effect fires. See Trigger modes. |
General.cooldownSeconds | 3 | Minimum time between two spirits of the same player |
General.startDelayMs | 1200 | Delay after death before the spirit appears, so the body falls first |
General.maxSpirits | 4 | How many spirits one client draws at the same time |
Trigger modes
| Mode | Fires when | Notes |
|---|---|---|
'native' | GTA kills the player's ped | Works on every server. On qb-ambulancejob and wasabi_ambulance, this is when the player goes down (last stand). |
'framework' | The framework marks the player dead | ESX: esx:onPlayerDeath. QBCore: metadata.isdead changes to true. |
'manual' | Your script calls the export | No automatic detection. Use it with exports.leno_spirit:Trigger(). See Exports and Events. |
Spirit
| Option | Default | Description |
|---|---|---|
Spirit.pose | 'meditate' | Key in Config.Poses |
Spirit.riseHeight | 2.0 | Metres the spirit floats up |
Spirit.durationMs | 6000 | Time from appearing to vanishing |
Spirit.startAlpha | 153 | Starting transparency: 51, 102, 153, 204 or 255 |
Spirit.fadeFrom | 0.45 | When the fade-out starts, as a fraction of durationMs |
Spirit.spin | 90.0 | Degrees the spirit turns over the whole rise. 0 = no spin. |
Spirit.removeProps | false | Remove 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:
| Field | Description |
|---|---|
dict / name | Animation dictionary and clip |
heightAboveGround | Start 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. |
| Pose | Look |
|---|---|
meditate | Seated, cross-legged |
lying | On the back, arms by the sides |
sunbathe | On the back, relaxed |
dead | The prologue "dead" pose |
tpose | Arms 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.
| Field | Description |
|---|---|
asset / name | Particle dictionary and effect |
scale | Size, 1.0 = normal |
offsetZ | Height in metres, positive = higher, negative = lower. start is measured from the body, trail from the spirit, and finish from where the spirit vanishes. |
intervalMs | trail only: time between two trail bursts |
| Option | Default | Description |
|---|---|---|
Particles.enabled | true | Turn all particles on or off |
Particles.finishHideDelayMs | 150 | The 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.
| Option | Default | Description |
|---|---|---|
Sound.enabled | true | Turn all sounds on or off |
Sound.range | 30 | Metres |
start / finish | Short_Transition_In / Short_Transition_Out | name + 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'callsConfig.CustomNotify(message, ntype), wherentypeis'inform','success'or'error'.- To add a language, copy the
enblock inConfig.Locales, translate it under a new key, and setConfig.Localeto 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',
},
}