RE:CORD
REC_BetterMask

Stracture

Installation, configuration, and usage for REC_BetterMask.
Added
Updated

The following configuration files and manifest are taken from the current source.

config/sh_config.lua

---@class REC_BetterMask.Shared.Config
local config = {}

---[[
---    Debug Mode
---]]
---@type boolean
config.debugMode = true

---[[
---     Gas mask settings
---]]
config.gasMask = {

    ---[[
    ---     ox_inventory item names (keep them in sync with _install/ox_inventory_items.lua)
    ---]]
    ---@type string
    item = "rec_bettermask_gasmask",

    ---[[
    ---     Clothing used while worn (componentId 1 = mask)
    ---]]
    ---@type table<REC_BetterMask.Shared.Enum.Gender, REC_BetterMask.Shared.Config.Clothing>
    clothing = {
        male = {
            componentId = 1,
            drawableId  = 129,
            texture     = 1,
        },
        female = {
            componentId = 1,
            drawableId  = 130,
            texture     = 0,
        },
    },

    ---[[
    ---     Put on / take off animation
    ---     applyAt: how many ms after the animation starts the clothing is swapped
    ---]]
    ---@type table<"putOn" | "takeOff", REC_BetterMask.Shared.Config.Animation>
    anims = {
        putOn = {
            dict     = "mp_masks@on_foot",
            name     = "put_on_mask",
            flag     = 48,
            duration = 1400,
            applyAt  = 900,
        },
        takeOff = {
            dict     = "missheist_agency2ahelmet",
            name     = "take_off_helmet_stand",
            flag     = 48,
            duration = 1400,
            applyAt  = 800,
        },
    },

    ---[[
    ---     Filter capacity (stored in the item metadata as charge, 0 to 100 %, mirrored to the durability bar)
    ---     durationInGas: how many seconds it lasts inside the gas
    ---     durationIdle:  how many seconds it lasts while worn outside the gas (0 never drains)
    ---     lowThreshold:  warn once it drops below this %
    ---]]
    filter = {
        ---@type integer
        durationInGas = 5 * 60, -- second
        ---@type integer
        durationIdle = 0, -- second
        ---@type integer
        lowThreshold = 20, -- %
    },

    ---[[
    ---     Ways to take the mask off other than using the item
    ---     (ox_inventory refuses to use a mask whose filter has run out)
    ---     both take off whichever mask is worn, the oxygen mask included
    ---]]
    command = {
        ---@type string
        takeOff = "removegasmask",
    },
    keybind = {
        ---@type boolean
        enabled = true,
        ---@type string
        name = "rec_bettermask_takeoff",
        ---@type string
        defaultKey = "",
    },
}

---[[
---     Oxygen mask settings
---     while worn with oxygen left the player breathes underwater and the game's oxygen does not drain,
---     the mask's oxygen (the ox_inventory durability) drains instead
---     it does not protect from the gas, and only one mask can be worn at a time
---]]
config.oxygenMask = {

    ---[[
    ---     ox_inventory item names (keep them in sync with _install/ox_inventory_items.lua)
    ---]]
    ---@type string
    item = "rec_bettermask_oxygenmask",

    ---[[
    ---     Clothing used while worn (componentId 1 = mask)
    ---]]
    ---@type table<REC_BetterMask.Shared.Enum.Gender, REC_BetterMask.Shared.Config.Clothing>
    clothing = {
        male = {
            componentId = 1,
            drawableId  = 166,
            texture     = 0,
        },
        female = {
            componentId = 1,
            drawableId  = 166,
            texture     = 0,
        },
    },

    ---[[
    ---     Put on / take off animation
    ---     applyAt: how many ms after the animation starts the clothing is swapped
    ---]]
    ---@type table<"putOn" | "takeOff", REC_BetterMask.Shared.Config.Animation>
    anims = {
        putOn = {
            dict     = "mp_masks@on_foot",
            name     = "put_on_mask",
            flag     = 48,
            duration = 1400,
            applyAt  = 900,
        },
        takeOff = {
            dict     = "missheist_agency2ahelmet",
            name     = "take_off_helmet_stand",
            flag     = 48,
            duration = 1400,
            applyAt  = 800,
        },
    },

    ---[[
    ---     Oxygen capacity (stored in the item metadata as charge, 0 to 100 %, mirrored to the durability bar)
    ---     durationUnderwater: how many seconds it lasts underwater
    ---     durationIdle:       how many seconds it lasts while worn out of the water (0 never drains)
    ---     lowThreshold:       warn once it drops below this %
    ---]]
    oxygen = {
        ---@type integer
        durationUnderwater = 5 * 60, -- second
        ---@type integer
        durationIdle = 0, -- second
        ---@type integer
        lowThreshold = 20, -- %
    },

    ---[[
    ---     Ped underwater limit (seconds) handed to SetPedMaxTimeUnderwater
    ---     supplied: applied while the mask has oxygen left (large enough to never run out)
    ---     restore:  applied when the mask comes off or runs out, match it to the server's default breath
    ---]]
    maxTimeUnderwater = {
        ---@type number
        supplied = 2000.0,
        ---@type number
        restore = 10.0,
    },

    ---[[
    ---     Engine scuba gear (the tank on componentId 8 through SetPedScubaGearVariation)
    ---     the game plays the breathing sound with it, the tank comes off once the oxygen runs out
    ---]]
    ---@type boolean
    useScubaGear = true,

    ---[[
    ---     Interval of the underwater check reported to the server
    ---]]
    ---@type integer
    checkInterval = 500, -- ms

    ---[[
    ---     Take off command (config.gasMask.keybind works for this mask as well)
    ---]]
    command = {
        ---@type string
        takeOff = "removeoxygenmask",
    },
}

---[[
---     Gas settings
---]]
config.gas = {

    ---[[
    ---     Damage taken inside the gas without a mask (or with an empty filter)
    ---     Removes amount every interval ms, ignoreArmour = true skips the armour
    ---]]
    damage = {
        ---@type integer
        interval = 1000, -- ms
        ---@type integer
        amount = 5,
        ---@type boolean
        ignoreArmour = true,
    },

    ---[[
    ---     Screen effect while exposed (nil disables it)
    ---]]
    screenEffect = {
        ---@type string|nil
        timecycle = "DRUG_gas_huffin",
        ---@type number
        timecycleStrength = 1.0,
        ---@type string|nil
        camShake = "DRUNK_SHAKE",
        ---@type number
        camShakeAmplitude = 0.15,
    },

    ---[[
    ---     Coughing animation while exposed
    ---]]
    cough = {
        ---@type boolean
        enabled = true,
        ---@type string
        dict = "timetable@gardener@smoking_joint",
        ---@type string
        name = "idle_cough",
        ---@type integer
        interval = 6000, -- ms
    },

    ---[[
    ---     Gas particle (created through REC_EffectManager)
    ---     Anything a zone does not set falls back to these values
    ---     renderDistance: the particle is drawn once the player gets this close
    ---]]
    particle = {
        ---@type string
        asset = "core",
        ---@type string
        name = "ent_amb_smoke_foundry",
        ---@type number
        scale = 3.0,
        ---@type REC_Library.Shared.RGBA
        colour = { r = 120, g = 255, b = 90, a = 255, },
        ---@type vector3
        offset = vector3(0.0, 0.0, 0.0),
        ---@type number
        renderDistance = 80.0,
    },

    ---[[
    ---     Draw the hit range for debugging
    ---]]
    ---@type boolean
    drawDebugZone = false,

    ---[[
    ---     Permanent gas zones
    ---     Keys must be unique. Use exports.REC_BetterMask:registerGasZone() for dynamic zones
    ---]]
    ---@type table<string, REC_BetterMask.Shared.Config.GasZone>
    zones = {
        -- ["humane_labs_1"] = {
        --     coords = vector3(3536.29, 3669.12, 28.12),
        --     radius = 10.0,
        --     damage = 5,
        --     particle = {
        --         name = "ent_amb_smoke_foundry",
        --         scale = 3.0,
        --         colour = { r = 120, g = 255, b = 90, a = 255, },
        --     },
        -- },
    },
}

---[[
---     NUI settings (the filter charge / oxygen gauge)
---]]
config.ui = {

    ---@type boolean
    enabled = true,

    ---[[
    ---     Position
    ---     x / y are the distance from the screen edge (CSS units)
    ---]]
    position = {
        ---@type "top-left" | "top-right" | "bottom-left" | "bottom-right"
        anchor = "top-right",
        ---@type string
        x = "2vw",
        ---@type string
        y = "14vh",
    },

    ---[[
    ---     Tint the screen edges green while exposed
    ---]]
    ---@type boolean
    showVignette = true,
}

---[[
---     Night vision settings
---]]
config.nightVision = {

    ---@type boolean
    enabled = true,

    ---@type REC_BetterMask.Shared.Config.NightVision.Clothing
    clothing = {
        male = {
            {
                up = {
                    componentId = 0,
                    drawableId  = 119,
                    texture     = 0
                },
                down = {
                    componentId = 0,
                    drawableId  = 118,
                    texture     = 0
                },
            },
        },
        female = {
            {
                up = {
                    componentId = 0,
                    drawableId  = 117,
                    texture     = 0
                },
                down = {
                    componentId = 0,
                    drawableId  = 118,
                    texture     = 0
                },
            },
        },
    },
}

return config

---@class REC_BetterMask.Shared.Config.Mask
---@field item string
---@field clothing table<REC_BetterMask.Shared.Enum.Gender, REC_BetterMask.Shared.Config.Clothing>
---@field anims table<"putOn" | "takeOff", REC_BetterMask.Shared.Config.Animation>
---@

---@class REC_BetterMask.Shared.Config.Clothing
---@field componentId integer
---@field drawableId integer
---@field texture integer
---@

---@class REC_BetterMask.Shared.Config.Animation
---@field dict string
---@field name string
---@field flag integer
---@field duration integer
---@field applyAt integer
---@

---@class REC_BetterMask.Shared.Config.GasZone
---@field coords vector3
---@field radius number
---@field damage? integer
---@field showParticle? boolean
---@field particle? REC_BetterMask.Shared.Config.GasZone.Particle
---@

---@class REC_BetterMask.Shared.Config.GasZone.Particle
---@field asset? string
---@field name? string
---@field scale? number
---@field colour? REC_Library.Shared.RGBA
---@field offset? vector3
---@field renderDistance? number
---@

---@class REC_BetterMask.Shared.Config.NightVision.Clothing
---@field male REC_BetterMask.Shared.Config.NightVision.Clothing.Male[]
---@field female REC_BetterMask.Shared.Config.NightVision.Clothing.Female[]
---@

---@class REC_BetterMask.Shared.Config.NightVision.Clothing.Male
---@field up REC_BetterMask.Shared.Config.NightVision.Clothing.UpDown
---@field down REC_BetterMask.Shared.Config.NightVision.Clothing.UpDown

---@class REC_BetterMask.Shared.Config.NightVision.Clothing.Female
---@field up REC_BetterMask.Shared.Config.NightVision.Clothing.UpDown
---@field down REC_BetterMask.Shared.Config.NightVision.Clothing.UpDown

---@class REC_BetterMask.Shared.Config.NightVision.Clothing.UpDown
---@field componentId integer
---@field drawableId integer
---@field texture integer
©2026 RE:CORD Development Group. All rights reserved.