# Configuration

### Config File

```lua
---@type configType
cfg = {}

--[[
  UI Interaction Type (How interaction prompts are displayed to the player) =

  'ui'     => Basic on-screen text prompt (e.g., "X to stand up").
  'gta'    => Uses GTA's native help notifications.
  'qb'     => Uses QBCore's text display system.
  'ox'     => Uses ox_lib's notification and text display system.
  'custom' => Use your own custom UI display method.
]]
cfg.uiInteraction = 'ui' -- Choose how prompts appear

--[[
  Interaction System (How the player interacts with objects in the world) =

  'auto'       => Automatically detects and uses the available interaction system.
  'qb-target'  => Uses the qb-target interaction system.
  'ox_target'  => Uses the ox_target interaction system.
  'qtarget'  => Uses the qtarget interaction system.
  'interact'   => Simple 3D text + key press interaction.
  'custom'     => Use your own custom interaction logic.
]]
cfg.interaction = 'auto' -- Choose the interaction method

--[[
  Framework (The core game framework being used) =

  'auto'      => Automatically detects and uses the available framework.
  'qbx_core'  => Uses QBX framework.
  'nd_core'  => Uses ND framework.
  'ox_core'  => Uses OX framework.
  'qb-core'   => Uses QBCore framework.
  'es_extended' => Uses ESX framework.
  'custom'     => Use your own custom interaction logic.
]]
cfg.framework = 'auto' -- Choose the framework

--[[
  Notification System (How notifications are displayed to the player) =

  'qb'            => Uses QBCore's built-in notification system.
  'esx'           => Uses ESX's built-in notification system.
  'ox'            => Uses ox_lib's notification system.
  'brutal'            => Uses brutal's notification system.
  'lation'            => Uses lation's notification system.
  'is_ui'            => Uses is_ui's notification system.
  'okok'            => Uses okok's notification system.
  'wasabi_uikit'            => Uses wasabi_uikit's notification system.
  'custom'        => Use your own custom notification method.
]]
cfg.notification = 'qb' -- Choose the notification method

--[[
  Debug Mode (Enable/disable debug features) =

  true  => Enables debug mode with extra logging and developer features.
  false => Disables debug mode for normal gameplay.
]]
cfg.debug = false

--[[
  Right-To-Left Text Support (For non-Latin languages) =

  true  => Forces RTL text direction (for Arabic, Persian, Hebrew, etc.).
  false => Forces LTR text direction (for English, Spanish, French, etc.).
  "auto" => Automatically detects based on the configured locale.
]]
cfg.enableRTL = "auto"

--[[
  Language/Locale (Game language) =

  "ar" => Arabic (العربية)
  "bg" => Bulgarian (Български)
  "bn" => Bengali (বাংলা)
  "ca" => Catalan (Català)
  "cs" => Czech (Čeština)
  "da" => Danish (Dansk)
  "de" => German (Deutsch)
  "el" => Greek (Ελληνικά)
  "en" => English (English)
  "es" => Spanish (Español)
  "fa" => Persian/Farsi (فارسی)
  "fi" => Finnish (Suomi)
  "fr" => French (Français)
  "he" => Hebrew (עברית)
  "hi" => Hindi (हिन्दी)
  "hu" => Hungarian (Magyar)
  "id" => Indonesian (Bahasa Indonesia)
  "it" => Italian (Italiano)
  "ja" => Japanese (日本語)
  "ko" => Korean (한국어)
  "ku" => Kurdish (کوردی)
  "mr" => Marathi (मराठी)
  "ms" => Malay (Bahasa Melayu)
  "nl" => Dutch (Nederlands)
  "no" => Norwegian (Norsk)
  "pa" => Punjabi (ਪੰਜਾਬੀ)
  "pl" => Polish (Polski)
  "ps" => Pashto (پښتو)
  "pt" => Portuguese (Português)
  "ro" => Romanian (Română)
  "ru" => Russian (Русский)
  "sv" => Swedish (Svenska)
  "sw" => Swahili (Kiswahili)
  "syr" => Syriac (ܠܫܢܐ ܣܘܪܝܝܐ)
  "th" => Thai (ไทย)
  "tr" => Turkish (Türkçe)
  "uk" => Ukrainian (Українська)
  "ur" => Urdu (اردو)
  "vi" => Vietnamese (Tiếng Việt)
  "zh-cn" => Chinese Simplified (简体中文)
  "zh-tw" => Chinese Traditional (繁體中文)
]]
cfg.locale = "en"

--[[
  Hide Player Names (Privacy setting) =

  true  => Player names are hidden in the UI.
  false => Player names are displayed in the UI.
]]
cfg.hideNames = false

--[[
  Bullet Configuration (Game mechanics for bullet loading) =

  deafultLoaded  => Number of live bullets when game starts.
  deafultEmpty   => Number of empty chambers when game starts.
  editableInGame => Whether players can modify bullet counts during gameplay.
  dieAfterDie    => Whether player dies in the game world after losing.
]]
cfg.bulletConfig = {
    deafultLoaded = 3,
    deafultEmpty = 3,
    editableInGame = {
        enabled = true,
        minLoaded = 1,
        maxLoaded = 5,
        minEmpty = 1,
        maxEmpty = 5,
        maxTotal = 6,
    },
    dieAfterDie = false,
}

--[[
  Spin Configuration (Weapon spinning mechanics) =

  onlyHostCanSpin => Whether only the host can spin the weapon.
  spinDuration    => How long the spin animation lasts (milliseconds).
  spinSound       => Sound effects during spinning.
  requestButton   => When to show the spin request button =
      "always"         => Button always appears first time players interact with weapon
      "afterHostDeath" => Button only appears after host dies
      "never"          => Button never appears (no spin requests)
]]
cfg.spinConfig = {
    onlyHostCanSpin = false,
    spinDuration = 4000,
    spinSound = {
        enabled = false,
        soundFile = "roulette_spin",
        volume = 0.5,
    },
    requestButton = "always" -- "always" | "afterHostDeath" | "never"
}

--[[
  Betting Configuration (Game betting mechanics) =

  enableEarlyEnd => Whether the game can end early if all players agree.
  betTime        => Time allowed for betting (seconds).
  minAmount      => Minimum bet amount.
  maxAmount      => Maximum bet amount.
  step           => Bet amount increment/decrement step.
  input = {               -- Input settings for manual bet entry
    enabled    => Whether manual input is enabled.
    useMinMax  => Whether to enforce minimum and maximum limits.
    minAmount  => Minimum allowed amount for input.
    maxAmount  => Maximum allowed amount for input.
  }
]]
cfg.betConfig = {
    enableEarlyEnd = false,
    betTime = 30,
    minAmount = 100,
    maxAmount = 1000,
    step = 1,
    input = {
        enabled = true,
        useMinMax = true,
        minAmount = 100,
        maxAmount = 1000,
    }
}

--[[
  Chair Key Mapping (Controls for chair interactions) =

  Set to a string like 'X' for dynamic key mapping per chair.
  Set to false to use static keys defined in each chair configuration.
  Dynamic mapping allows players to customize keys, static is more optimized.
]]
cfg.chairKeyMapping = 'X'

--[[
  Game Locations (Where Russian Roulette tables are placed in the world) =

  Each location contains =
  - coords = Position and heading of the table
  - models = 3D models for table and chairs
  - chairs = Chair positions and configurations
  - cameras = Camera angles for gameplay
  - items = Interactive objects (weapon, magazine, money)
  - requireApproval = Whether host must approve joining players
  - blip = Blip configuration for map display
]]
cfg.locations = {
    {
        requireApproval = true,
        coords = vector4(71.99, 3708.92, 39.75, 319.22),
        models = {
            table = 'falcon_rr_table',
            chair = 'falcon_rr_chair',
        },
        blip = {
            enabled = true,
            sprite = 630,
            color = 1,
            scale = 0.8,
            label = "Russian Roulette",
            display = 4,
            category = nil
        },
        chairs = {
            {
                offset = vector3(1.4, 0.0, 0.3),
                sitOffset = vector3(-0.3, 0.3, 0.3),
                heading = 230.0,
                enabled = true,
                key = { "X", 'KeyX' },
                animList = {
                    { type = 'emote', dict = 'timetable@ron@ig_3_couch', name = 'base' }
                }
            },
            {
                offset = vector3(-1.4, 0.0, 0.3),
                sitOffset = vector3(0.3, -0.3, 0.3),
                heading = 50.0,
                enabled = true,
                key = { "X", 'KeyX' },
                animList = {
                    { type = 'emote', dict = 'timetable@ron@ig_3_couch', name = 'base' }
                }
            },
            {
                offset = vector3(0.0, 1.4, 0.3),
                sitOffset = vector3(-0.3, -0.3, 0.3),
                heading = -40.0,
                enabled = true,
                key = { "X", 'KeyX' },
                animList = {
                    { type = 'emote', dict = 'timetable@ron@ig_3_couch', name = 'base' }
                }
            },
            {
                offset = vector3(0.0, -1.4, 0.3),
                sitOffset = vector3(0.3, 0.3, 0.3),
                heading = 140.0,
                enabled = true,
                key = { "X", 'KeyX' },
                animList = {
                    { type = 'emote', dict = 'timetable@ron@ig_3_couch', name = 'base' }
                }
            },
        },
        cameras = {
            { offset = vector3(0.0, 0.0, 1.5),   rot = vector3(-90.0, 0.0, 319.22), fov = 50.0 },
            { offset = vector3(1.5, 1.0, 1.0),   rot = vector3(-15.0, 0.0, 80),     fov = 60.0 },
            { offset = vector3(-1.5, -1.0, 1.0), rot = vector3(-15.0, 0.0, 260),    fov = 60.0 },
            { offset = vector3(-1.5, 1.0, 1.0),  rot = vector3(-15.0, 0.0, 200),    fov = 60.0 },
            { offset = vector3(1.5, -1.0, 1.0),  rot = vector3(-15.0, 0.0, 15),     fov = 60.0 },
        },
        items = {
            weapon = {
                model = 'w_pi_wep2_gun',
                offset = vector3(0.3, -0.3, 0.0),
                rot = vector3(90.0, 0.0, 0.0),
                outline = { draw = true, color = { r = 255, g = 215, b = 0, a = 255 } }
            },
            mag = {
                model = 'w_pi_wep2_gun_mag1',
                offset = vector3(0.2, -0.2, 0.05),
                rot = vector3(0.0, 90.0, 0.0),
                bone = 6,
                outline = { draw = true, color = { r = 30, g = 144, b = 255, a = 255 } }
            },
            money = {
                model = 'prop_anim_cash_pile_01',
                offset = vector3(-0.4, 0.0, 0.05),
                rot = vector3(0.0, 0.0, 0.5),
                outline = { draw = true, color = { r = 34, g = 139, b = 34, a = 255 } }
            },
        }
    },
}

--[[
  Animation Configuration (All game animations and timings) =

  weaponAnimation    => Animations for players during gameplay (taking aim, dying).
  hostWeaponAnimation => Special animations for the host (weapon handling).
  winnerAnimation    => Celebration animations for the winner.

  Each animation section contains =
  - Animation dictionaries and names
  - Facial expressions
  - Camera settings
  - Precise timing controls for smooth gameplay
]]
cfg.animations = {
    weaponAnimation = {
        takeWeapon = {
            dict = "anim@amb@business@weed@weed_inspecting_lo_med_hi@",
            name = "weed_spraybottle_crouch_base_inspector",
        },
        aimWeapon = {
            dict = 'falconded@custom_anim',
            name = 'falconded_clip',
            expression = 'mood_stressed_1'
        },
        dieAnimation = {
            dict = 'falconkilll@custom_anim',
            name = 'falconkilll_clip',
            expression = 'dead_1'
        },

        timings = {
            attachDelay = 150,
            weaponToHandDuration = 1500,
            afterAttachDelay = 200,
            aimDuration = 500,
            aimLerpDuration = 550,
            emptyReturnDelay = 850,
            returnLerpDuration = 550,
            emptySoundDelay = 650,
            surviveWait = 950,
            dieWait = 1230,
            surviveCleanup = 550,
            dieCleanup = 1050,
            returnToTableDuration = 1500
        }
    },

    hostWeaponAnimation = {
        takeWeapon = {
            dict = "anim@amb@business@weed@weed_inspecting_lo_med_hi@",
            name = "weed_spraybottle_crouch_base_inspector",
        },
        holsterWeapon = {
            dict = "anim@weapons@pistol@doubleaction_holster",
            name = "holster",
            duration = 2000
        },

        timings = {
            initialWait = 150,
            attachDuration = 1500,
            afterAttachWait = 150,
            afterAttachDelay = 200,
            holsterWait = 750,
            detachDelay = 500,
            returnToTableDuration = 1500
        }
    },

    winnerAnimation = {
        dict = 'missdocksshowoffcar@idle_a',
        name = 'idle_b_5',
        duration = 4500,
        expression = 'mood_happy_1',
        camDistance = 1.2,
        camHeight = 0.0,
        camFov = 40.0,
        transitionDuration = 1500
    }
}

--[[
  UI Style Configuration (Complete control over colors, icons, fonts and styling) =
  This section allows full customization of the game's visual appearance.
]]
cfg.styleConfig = {
    components = {
        panel = {
            layout = {
                main = {
                    display = 'flex',
                    position = 'absolute',
                    fontSize = '0.875rem',
                    lineHeight = '1.25rem',
                    gap = '0.75rem',
                    flexDirection = 'column',
                    top = '50%',
                    transform = 'translate(0, -50%) scaleX(1) scaleY(1)',
                    minWidth = '220px',
                    background = 'rgb(24 24 27 / 0.9)',
                    border = '1px solid rgb(63 63 70 / var(--tw-border-opacity, 1))',
                    borderRadius = '0.5rem',
                    padding = '1rem',
                    color = '#fafafa',
                },
                flexColumn = {
                    display = 'flex',
                    gap = '0.5rem',
                    flexDirection = 'column',
                },
            },
            bets = {
                row = {
                    display = 'flex',
                    fontSize = '0.75rem',
                    lineHeight = '1rem',
                    justifyContent = 'space-between',
                    alignItems = 'center',
                },
                label = {
                    display = 'flex',
                    alignItems = 'center',
                    gap = '0.5rem',
                    color = 'rgb(212 212 216 / var(--tw-text-opacity, 1))',
                },
                icon = {
                    fontSize = '11px'
                },
            },
            winner = {
                container = {
                    display = 'flex',
                    gap = '0.75rem',
                    flexDirection = 'column',
                },
                header = {
                    display = 'flex',
                    alignItems = 'center',
                    justifyContent = 'space-between'
                },
                card = {
                    background = 'rgb(39 39 42 / 0.4)',
                    borderRadius = '0.5rem',
                    padding = '1rem',
                    border = '1px solid rgb(63 63 70 / var(--tw-border-opacity, 1))'
                },
                name = {
                    flex = 1,
                    fontSize = '0.875rem',
                    lineHeight = '1.25rem',
                    overflow = 'hidden',
                    textOverflow = 'ellipsis',
                    whiteSpace = 'nowrap'
                },
                icon = {
                    color = 'rgb(161 161 170 / var(--tw-text-opacity, 1))',
                    fontSize = '12px',
                    marginLeft = '8px'
                },
                message = {
                    color = 'rgb(161 161 170 / var(--tw-text-opacity, 1))',
                    fontSize = '0.75rem',
                    lineHeight = '1rem',
                    textAlign = 'center'
                },
                title = {
                    color = 'rgb(161 161 170 / var(--tw-text-opacity, 1))',
                    fontSize = '0.75rem',
                    lineHeight = '1rem',
                    textTransform = 'uppercase',
                    letterSpacing = '0.05em'
                },
            },
            players = {
                item = {
                    display = 'flex',
                    alignItems = 'center',
                    justifyContent = 'space-between',
                    gap = '0.5rem',
                    borderRadius = '0.375rem',
                    padding = '0.5rem 1rem',
                    fontSize = '0.75rem',
                    lineHeight = '1rem'
                },
                name = {
                    display = 'flex',
                    alignItems = 'center',
                    gap = '0.375rem',
                    color = 'rgb(212 212 216 / var(--tw-text-opacity, 1))',
                    fontSize = '0.75rem',
                    lineHeight = '1rem'
                },
                bet = {
                    color = 'rgb(5 223 114)',
                    fontSize = '11px'
                },
                header = {
                    display = 'flex',
                    color = 'rgb(161 161 170 / var(--tw-text-opacity, 1))',
                    fontSize = '0.75rem',
                    lineHeight = '1rem',
                    textTransform = 'uppercase',
                    letterSpacing = '0.05em',
                    gap = '0.25rem'
                },
            },
            hints = {
                key = {
                    background = 'rgb(24 24 27 / 0.9)',
                    color = 'rgb(212 212 216 / var(--tw-text-opacity, 1))',
                    border = '1px solid rgb(82 82 91 / var(--tw-border-opacity, 1))',
                    borderRadius = '0.375rem',
                    paddingTop = '0.125rem',
                    paddingBottom = '0.125rem',
                    paddingLeft = '0.375rem',
                    paddingRight = '0.375rem',
                    fontSize = '0.75rem'
                },
                item = {
                    display = 'flex',
                    alignItems = 'center',
                    gap = '0.5rem',
                    padding = '0.5rem 1rem',
                    background = 'rgb(39 39 42 / 0.3)',
                    borderRadius = '0.375rem',
                    fontSize = '0.75rem',
                    lineHeight = '1rem'
                },
                divider = {
                    marginTop = '0.5rem',
                    marginBottom = '0.5rem'
                },
            },
            requests = {
                item = {
                    background = 'rgb(39 39 42 / 0.3)',
                    borderRadius = '0.375rem',
                    padding = '0.5rem 1rem',
                    border = '1px solid rgb(63 63 70 / var(--tw-border-opacity, 1))',
                    display = 'flex',
                    alignItems = 'center',
                    justifyContent = 'space-between',
                    gap = '0.5rem',
                    fontSize = '0.75rem',
                    lineHeight = '1rem'
                },
                name = {
                    overflow = 'hidden',
                    textOverflow = 'ellipsis',
                    whiteSpace = 'nowrap',
                    maxWidth = '120px',
                    color = 'rgb(212 212 216 / var(--tw-text-opacity, 1))'
                },
                buttons = {
                    display = 'flex',
                    gap = '0.75rem'
                },
                header = {
                    color = 'rgb(161 161 170 / var(--tw-text-opacity, 1))',
                    fontSize = '0.75rem',
                    lineHeight = '1rem',
                    textTransform = 'uppercase',
                    letterSpacing = '0.05em'
                },
            },
            prize = {
                section = {
                    display = 'flex',
                    alignItems = 'center',
                    justifyContent = 'space-between',
                    borderRadius = '0.375rem',
                    padding = '0.5rem 1rem',
                    background = 'rgb(63 63 70 / 0.3)',
                    fontSize = '0.75rem',
                    lineHeight = '1rem'
                },
                amount = {
                    fontSize = '0.875rem',
                    lineHeight = '1.25rem',
                    overflow = 'hidden',
                    textOverflow = 'ellipsis',
                    whiteSpace = 'nowrap',
                    maxWidth = '80px'
                },
            },
            badges = {
                host = {
                    fontSize = '9px',
                    padding = '0.125rem 0.25rem',
                    borderRadius = '0.25rem',
                    display = 'flex',
                    alignItems = 'center',
                    gap = '0.125rem',
                    background = 'rgb(251 191 36 / 0.1)',
                    color = 'rgb(252 211 77)'
                },
            },
            buttons = {
                kick = {
                    color = 'rgb(252 165 165 / var(--tw-text-opacity, 1))',
                    fontSize = '9px',
                    transition = 'color 0.2s',
                    background = 'none',
                    border = 'none',
                    cursor = 'pointer'
                },
                approve = {
                    color = '#22c55e',
                    fontSize = '11px',
                    transition = 'color 0.2s',
                    background = 'none',
                    border = 'none',
                    cursor = 'pointer'
                },
                reject = {
                    color = 'rgb(252 165 165)',
                    fontSize = '11px',
                    transition = 'color 0.2s',
                    background = 'none',
                    border = 'none',
                    cursor = 'pointer'
                },
                spin = {
                    background = 'rgba(39, 39, 42, 0.3)',
                    color = 'rgb(212 212 216)',
                    borderRadius = '0.375rem',
                    padding = '0.625rem 1rem',
                    fontSize = '0.75rem',
                    lineHeight = '1rem',
                    fontWeight = '500',
                    cursor = 'pointer',
                    transition = 'all 0.2s ease-in-out',
                    display = 'flex',
                    alignItems = 'center',
                    justifyContent = 'center',
                    gap = '0.5rem',
                    width = '100%',
                }
            },
            sections = {
                header = {
                    display = 'flex',
                    alignItems = 'center',
                    gap = '0.5rem',
                    marginBottom = '0.5rem',
                },
            },
            typography = {
                textXs = {
                    fontSize = '0.75rem',
                    lineHeight = '1rem'
                },
            },
            icons = {
                host = {
                    fontSize = '8px'
                },
                yourBet = 'fa-dollar-sign',
                totalPot = 'fa-piggy-bank',
                players = 'fa-users',
                risk = 'fa-skull',
                kick = 'fa-user-minus',
                winner = 'fa-user-crown',
                approve = 'fa-check',
                reject = 'fa-xmark'
            },
            colors = {
                text = {
                    secondary = 'rgb(212 212 216 / var(--tw-text-opacity, 1))',
                    tertiary = 'rgb(161 161 170 / var(--tw-text-opacity, 1))'
                },
                border = 'rgb(63 63 70 / var(--tw-border-opacity, 1))',
                bets = {
                    yourBet = 'rgb(5 223 114)',
                    totalPot = 'rgb(255 185 0)',
                    players = 'rgb(81 162 255)',
                    risk = 'rgb(255 100 103)'
                },
                backgrounds = {
                    primary = 'rgb(39 39 42 / 0.3)',
                    secondary = 'rgb(39 39 42 / 0.4)',
                    tertiary = 'rgb(63 63 70 / 0.3)'
                },
                winner = {
                    text = 'rgb(255 185 0)',
                    prize = 'rgb(255 185 0)'
                }
            },
        },
        money = {
            container = {
                position = 'fixed',
                width = '13rem',
                padding = '0.5rem',
                borderRadius = '0.5rem',
                borderWidth = '1px',
                borderStyle = 'solid',
                display = 'flex',
                flexDirection = 'column',
                alignItems = 'center',
                gap = '0.5rem',
                background = 'rgb(24 24 27 / 0.9)',
                borderColor = 'rgb(63 63 70 / var(--tw-border-opacity, 1))',
                transform = 'translateX(-50%)',
            },

            amountSection = {
                display = 'flex',
                alignItems = 'center',
                gap = '0.5rem',
                width = '100%',
                justifyContent = 'space-between',
            },

            amountButton = {
                width = '1.5rem',
                height = '1.5rem',
                borderRadius = '0.375rem',
                fontSize = '0.875rem',
                transition = 'background-color 0.2s',
                display = 'flex',
                alignItems = 'center',
                justifyContent = 'center',
                background = 'rgb(63 63 70 / var(--tw-bg-opacity, 1))',
                color = 'rgb(212 212 216 / var(--tw-text-opacity, 1))',
                border = 'none',
                cursor = 'pointer',
            },

            amountButtonHover = {
                background = '#52525b',
            },

            amountButtonDisabled = {
                opacity = '0.5',
                cursor = 'not-allowed',
            },

            amountText = {
                fontSize = '0.875rem',
                fontWeight = '600',
                width = '3.5rem',
                textAlign = 'center',
                color = '#fafafa',
            },

            amountInput = {
              fontSize = '0.875rem',
              fontWeight = '600',
              width = '3.5rem',
              textAlign = 'center',
              color = '#fafafa',
              background = 'transparent',
              
              [':focus'] = {
                outline = 'none',
                border = 'none',
                boxShadow = 'none'
              }
            },

            slider = {
                width = '100%',
                height = '0.5rem',
                borderRadius = '0.375rem',
                appearance = 'none',
                cursor = 'pointer',
                background = 'linear-gradient(to right, #22c55e 0%, #22c55e 50%, #3f3f46 50%, #3f3f46 100%)',
                track = '#3f3f46',
                filled = '#22c55e',
                thumb = '#22c55e'
            },

            sliderThumb = {
                width = '1rem',
                height = '1rem',
                borderRadius = '50%',
                background = '#22c55e',
                boxShadow = '0 2px 4px rgba(0, 0, 0, 0.2)',
                cursor = 'pointer',
            },

            error = {
                fontSize = '0.75rem',
                fontWeight = '500',
                textAlign = 'center',
                color = 'rgb(252 165 165 / var(--tw-text-opacity, 1))',
            },

            confirmButton = {
                width = '100%',
                fontWeight = '600',
                fontSize = '0.875rem',
                paddingTop = '0.25rem',
                paddingBottom = '0.25rem',
                borderRadius = '0.375rem',
                transition = 'background-color 0.2s',
                background = 'rgb(22 163 74 / var(--tw-bg-opacity, 1))',
                color = '#ffffff',
                border = 'none',
                cursor = 'pointer',
            },

            confirmButtonHover = {
                background = 'rgb(34 197 94 / var(--tw-bg-opacity, 1))',
            },

            primary = {
                background = 'rgb(24 24 27 / 0.9)',
                border = 'rgb(63 63 70 / var(--tw-border-opacity, 1))',
            },
            text = {
                primary = '#fafafa',
                secondary = 'rgb(212 212 216 / var(--tw-text-opacity, 1))',
            },
            button = {
                primary = {
                    normal = 'rgb(22 163 74 / var(--tw-bg-opacity, 1))',
                    hover = 'rgb(34 197 94 / var(--tw-bg-opacity, 1))',
                    text = '#ffffff',
                },
                secondary = {
                    background = 'rgb(63 63 70 / var(--tw-bg-opacity, 1))',
                    hover = '#52525b',
                }
            },
        },
        hints = {
            container = {
                position = 'absolute',
                bottom = '1.5rem',
                right = '50%',
                transform = 'translate(50%, 0) scaleX(1) scaleY(1)',
            },

            card = {
                borderRadius = '0.5rem',
                borderWidth = '1px',
                borderStyle = 'solid',
                paddingTop = '0.5rem',
                paddingBottom = '0.5rem',
                paddingRight = '3rem',
                paddingLeft = '3rem',
                background = 'rgb(24 24 27 / 0.9)',
                borderColor = 'rgb(39 39 42 / var(--tw-border-opacity, 1))',
                color = 'rgb(113 113 122 / var(--tw-text-opacity, 1))',
                textAlign = 'center',
                minWidth = '200px',
            },

            message = {
                textAlign = 'center',
                whiteSpace = 'pre-line',
            },

            content = {
                display = 'flex',
                alignItems = 'center',
                gap = '4px',
                justifyContent = 'center'
            },

            skipButton = {
                padding = '2px',
                border = '1px solid rgb(63 63 70 / 0.15)',
                borderRadius = '3px',
                cursor = 'pointer',
                fontSize = '11px',
                transition = 'all 0.2s',
                minWidth = '30px',
            },
        },
        bullet = {
            container = {
                position = 'fixed',
                width = '11rem',
                background = 'rgb(24 24 27 / 0.95)',
                border = '1px solid rgb(63 63 70 / var(--tw-border-opacity, 1))',
                borderRadius = '0.5rem',
                padding = '1rem',
                display = 'flex',
                flexDirection = 'column',
                alignItems = 'center',
                boxShadow = '0 25px 50px -12px rgb(0 0 0 / 0.5)',
                transform = 'translate(-50%, -50%)',
                transition = 'all 0.3s ease-in-out'
            },
            roulette = {
                position = 'relative',
                width = '7rem',
                height = '7rem',
                borderRadius = '50%',
                background = 'rgb(39 39 42)',
                border = '2px solid rgb(82 82 91)',
                boxShadow = '0 10px 15px -3px rgb(0 0 0 / 0.3)',
                marginBottom = '1rem',
                transition = 'all 0.3s ease-in-out'
            },
            centerDot = {
                position = 'absolute',
                top = '50%',
                left = '50%',
                width = '0.75rem',
                height = '0.75rem',
                background = 'rgb(82 82 91)',
                borderRadius = '50%',
                transform = 'translate(-50%, -50%)',
                boxShadow = 'inset 0 2px 4px 0 rgb(0 0 0 / 0.5)',
                transition = 'all 0.3s ease-in-out'
            },
            bullet = {
                position = 'absolute',
                width = '0.75rem',
                height = '0.75rem',
                borderRadius = '50%',
                top = '50%',
                left = '50%',
                transform = 'translate(-50%, -50%)',
                transition = 'transform 0.5s ease-in-out'
            },
            bulletLoaded = {
                background = 'rgb(239 68 68)',
                boxShadow = '0 0 10px 2px rgb(239 68 68 / 0.5)'
            },
            bulletEmpty = {
                background = 'rgb(113 113 122)',
                border = '1px solid rgb(82 82 91)'
            },
            controls = {
                width = '100%',
                display = 'flex',
                flexDirection = 'column',
                gap = '0.75rem',
                fontSize = '0.75rem',
                lineHeight = '1rem',
                marginBottom = '0.75rem'
            },
            controlRow = {
                display = 'flex',
                alignItems = 'center',
                justifyContent = 'space-between',
                width = '100%'
            },
            controlLabel = {
                display = 'flex',
                alignItems = 'center',
                gap = '0.25rem',
                minWidth = '60px'
            },
            iconLoaded = {
                color = 'rgb(239 68 68)',
                fontSize = '0.875rem',
                transition = 'all 0.3s ease-in-out'
            },
            iconEmpty = {
                color = 'rgb(113 113 122)',
                fontSize = '0.75rem',
                transition = 'all 0.3s ease-in-out'
            },
            labelText = {
                color = 'rgb(212 212 216)',
                transition = 'all 0.3s ease-in-out'
            },
            buttonGroup = {
                display = 'flex',
                alignItems = 'center',
                gap = '0.25rem'
            },
            controlButton = {
                width = '1.5rem',
                height = '1.5rem',
                background = 'rgb(63 63 70)',
                borderRadius = '0.25rem',
                color = 'rgb(212 212 216)',
                border = 'none',
                cursor = 'pointer',
                transition = 'all 0.2s ease-in-out',
                display = 'flex',
                alignItems = 'center',
                justifyContent = 'center',
                fontSize = '0.75rem'
            },
            controlButtonHover = {
                background = 'rgb(82 82 91)'
            },
            controlButtonDisabled = {
                opacity = 0.3,
                cursor = 'not-allowed'
            },
            countText = {
                color = 'rgb(244 244 245)',
                fontWeight = 'bold',
                width = '1.5rem',
                textAlign = 'center',
                fontSize = '0.875rem',
                transition = 'all 0.2s ease-in-out'
            }
        }
    },
    typography = {
        fontFamily = {
            primary = "'Tajawal', sans-serif",
            secondary = "'Open+Sans', sans-serif",
            mono = "'JetBrains Mono', monospace"
        },
    },
}

```

***

### Utils Files

> Client-Side File

```lua
Utils = {}

-- ================================
-- EVENT MANAGEMENT UTILITIES
-- ================================

--- Registers a client event
--- If you have a secure event system, you can replace this with that
--- @param name string - the event name to register
--- @param cb function - the callback to execute when the event is triggered
--- @todo: Consider replacing with Safe-Events if available
--- @example RegisterNetEvent ⭢ falcon.safeEvent
function Utils.registerNetEvent(name, cb)
    RegisterNetEvent(name)
    AddEventHandler(name, cb)
end

--- triggers a server event with error handling
--- if you have a secure event system, you can replace this with that
--- @param eventName string - the server event name
--- @param ... any - event parameters
--- @todo: Consider replacing with Safe-Events if available
--- @example TriggerServerEvent ⭢ falcon.safeTriggerServerEvent
function Utils.triggerServerEvent(eventName, ...)
    TriggerServerEvent(eventName, ...)
end

-- ================================
-- ENTITY MANAGEMENT UTILITIES
-- ================================

--- Safely deletes an entity if it exists
--- @param entity number - entity handle
--- @return boolean - success status
function Utils.safeDeleteEntity(entity)
    if entity and DoesEntityExist(entity) then
        DeleteEntity(entity)
        return true
    end
    return false
end

--- Checks if entity exists and is valid
--- @param entity number - entity handle
--- @return boolean
function Utils.isEntityValid(entity)
    return entity and DoesEntityExist(entity)
end

--- Gets entity coordinates safely
--- @param entity number - entity handle
--- @return vector3 | nil
function Utils.getEntityCoords(entity)
    if Utils.isEntityValid(entity) then
        return GetEntityCoords(entity)
    end
    return nil
end

--- Gets entity forward vector safely
--- @param entity number - entity handle
--- @return vector3 | nil
function Utils.getEntityForwardVector(entity)
    if Utils.isEntityValid(entity) then
        return GetEntityForwardVector(entity)
    end
    return nil
end

-- ================================
-- PED MANAGEMENT UTILITIES
-- ================================

--- Sets or clears a ped's facial expression
--- @param toggle boolean - true to set the expression, false to clear it
--- @param ped number - the ped to modify
--- @param expression? string - the facial expression to set
function Utils.setPedFacialExpression(toggle, ped, expression)
    local currentSweat = GetPedSweat(ped) or 0

    if toggle and expression then
        SetFacialIdleAnimOverride(ped, expression, 0)
        SetPedSweat(ped, 100.0)
    else
        ClearFacialIdleAnimOverride(ped)
        SetPedSweat(ped, currentSweat)
    end
end

--- Safely gets player ped
--- @param playerId number - player ID
--- @return number | nil - ped handle or nil if invalid
function Utils.getPlayerPed(playerId)
    local ped = GetPlayerPed(playerId)
    if ped and DoesEntityExist(ped) then
        return ped
    end
    return nil
end

--- Gets ped bone position safely
--- @param ped number - ped handle
--- @param boneId number - bone ID
--- @return vector3 | nil
function Utils.getPedBoneCoords(ped, boneId)
    if Utils.isEntityValid(ped) then
        return GetPedBoneCoords(ped, boneId, 0.0, 0.0, 0.0)
    end
    return nil
end

-- ================================
-- ANIMATION & MODEL UTILITIES
-- ================================

--- Smoothly transitions entity attachment
--- @param entity number - entity handle
--- @param ped number - ped handle
--- @param bone number - bone ID
--- @param startAttach table - starting attachment offsets
--- @param endAttach table - ending attachment offsets
--- @param duration number - transition duration in milliseconds
--- @return void
function Utils.lerpAttachment(entity, ped, bone, startAttach, endAttach, duration)
    local startTime = GetGameTimer()

    while GetGameTimer() - startTime < duration do
        local progress = (GetGameTimer() - startTime) / duration
        local currentX = startAttach.x + (endAttach.x - startAttach.x) * progress
        local currentY = startAttach.y + (endAttach.y - startAttach.y) * progress
        local currentZ = startAttach.z + (endAttach.z - startAttach.z) * progress
        local currentRx = startAttach.rx + (endAttach.rx - startAttach.rx) * progress
        local currentRy = startAttach.ry + (endAttach.ry - startAttach.ry) * progress
        local currentRz = startAttach.rz + (endAttach.rz - startAttach.rz) * progress

        AttachEntityToEntity(entity, ped, GetPedBoneIndex(ped, bone),
            currentX, currentY, currentZ, currentRx, currentRy, currentRz,
            true, true, false, true, 1, true)
        Wait(0)
    end

    AttachEntityToEntity(entity, ped, GetPedBoneIndex(ped, bone),
        endAttach.x, endAttach.y, endAttach.z, endAttach.rx, endAttach.ry, endAttach.rz,
        true, true, false, true, 1, true)
end

--- Requests a model with timeout
--- @param model number - model hash
--- @param timeout number - timeout in milliseconds
--- @return boolean - success status
function Utils.requestModel(model, timeout)
    timeout = timeout or 15000
    local startTime = GetGameTimer()
    RequestModel(model)
    while not HasModelLoaded(model) do
        if GetGameTimer() - startTime > timeout then
            Utils.debug('requestModel timed out', model, GetGameTimer() - startTime)
            return false
        end
        Wait(0)
    end
    return true
end

--- Requests an animation dictionary with timeout
--- @param dict string - animation dictionary name
--- @return boolean - success status
function Utils.requestAnim(dict)
    if not dict or type(dict) ~= 'string' then return false end

    if not HasAnimDictLoaded(dict) then
        RequestAnimDict(dict)

        local timeout = 5000
        local start = GetGameTimer()

        while not HasAnimDictLoaded(dict) do
            if GetGameTimer() - start > timeout then
                Utils.debug('requestAnim: timeout loading dict', dict)
                return false
            end
            Wait(0)
        end
    end

    return true
end

--- Plays an animation on a ped safely
--- @param ped number - ped handle
--- @param ... any - animation parameters
--- @return boolean - success status
function Utils.playAnim(ped, ...)
    if not ped or not DoesEntityExist(ped) then
        Utils.debug('playAnim: invalid ped', ped)
        return false
    end

    local args = { ... }
    local dict = args[1]

    if type(dict) ~= 'string' then
        Utils.debug('playAnim: animDictionary invalid', dict)
        return false
    end

    if type(args[2]) ~= 'string' then
        Utils.debug('playAnim: animation name invalid', args[2])
        return false
    end

    local loaded = Utils.requestAnim(dict)
    if not loaded then
        Utils.debug('playAnim: failed to load dict', dict)
        return false
    end

    TaskPlayAnim(ped, table.unpack(args))
    RemoveAnimDict(dict)

    return true
end

---@param ped number
---@param dict string
---@param anim string
---@param pos vector3
---@param rot vector3
---@param blendInSpeed number
---@param blendOutSpeed number
---@param duration number
---@param flag number
---@param animTime number
function Utils.playAnimAdvanced(ped, dict, anim, pos, rot, blendInSpeed, blendOutSpeed, duration, flag, animTime)
    if not ped or not DoesEntityExist(ped) then
        Utils.debug('playAnimAdvanced: invalid ped', ped)
        return false
    end

    if type(dict) ~= 'string' or type(anim) ~= 'string' then
        Utils.debug('playAnimAdvanced: invalid dict or anim', dict, anim)
        return false
    end

    local loaded = Utils.requestAnim(dict)
    if not loaded then
        Utils.debug('playAnimAdvanced: failed to load dict', dict)
        return false
    end

    TaskPlayAnimAdvanced(
        ped,
        dict,
        anim,
        pos.x, pos.y, pos.z,
        rot.x, rot.y, rot.z,
        blendInSpeed or 8.0,
        blendOutSpeed or -8.0,
        duration or -1,
        flag or 1,
        animTime or 0.0,
        0, 0
    )

    RemoveAnimDict(dict)
    return true
end

-- ================================
-- PARTICLE EFFECTS UTILITIES
-- ================================

--- Creates particle effect at coordinates
--- @param asset string - particle asset name
--- @param effectName string - particle effect name
--- @param pos vector3 - position coordinates
--- @param scale number - effect scale
function Utils.createParticleEffect(asset, effectName, pos, scale)
    if not HasNamedPtfxAssetLoaded(asset) then
        RequestNamedPtfxAsset(asset)
        while not HasNamedPtfxAssetLoaded(asset) do
            Wait(10)
        end
    end

    UseParticleFxAssetNextCall(asset)
    StartParticleFxNonLoopedAtCoord(effectName, pos.x, pos.y, pos.z, 0.0, 0.0, 0.0, scale or 1.0, false, false, false)
end

--- Creates blood effects for ped
--- @param ped number - ped handle
function Utils.createBloodEffects(ped)
    local headPos = Utils.getPedBoneCoords(ped, 31086)
    if not headPos then return end

    Utils.createParticleEffect("core", "blood_headshot", headPos, 1.5)
    Utils.createParticleEffect("core", "blood_stab", headPos, 1.0)

    local headBone = GetPedBoneIndex(ped, 31086)
    ApplyPedBloodSpecific(ped, headBone, 0.0, 0.0, 0.0, "wound_sheet")
    ApplyPedDamagePack(ped, "BigHitByVehicle", 0.0, 1.0)
end

--- Creates muzzle flash effect
--- @param pos vector3 - muzzle position
function Utils.createMuzzleFlash(pos)
    Utils.createParticleEffect("core", "muzzle_pistol", pos, 2.5)
end

--- Creates blood pool effect
--- @param pos vector3 - ground position
function Utils.createBloodPool(pos)
    Utils.createParticleEffect("core", "blood_pool", pos, 1.0)
end

-- ================================
-- INPUT UTILITIES
-- ================================

local SpecialKeyCodes = {
    ['b_100'] = 'LMB', -- Left Mouse Button
    ['b_101'] = 'RMB', -- Right Mouse Button
    ['b_102'] = 'MMB', -- Middle Mouse Button
    ['b_103'] = 'Mouse.ExtraBtn1',
    ['b_104'] = 'Mouse.ExtraBtn2',
    ['b_105'] = 'Mouse.ExtraBtn3',
    ['b_106'] = 'Mouse.ExtraBtn4',
    ['b_107'] = 'Mouse.ExtraBtn5',
    ['b_108'] = 'Mouse.ExtraBtn6',
    ['b_109'] = 'Mouse.ExtraBtn7',
    ['b_110'] = 'Mouse.ExtraBtn8',
    ['b_115'] = 'MouseWheel.Up',
    ['b_116'] = 'MouseWheel.Down',
    ['b_130'] = 'NumSubstract',
    ['b_131'] = 'NumAdd',
    ['b_134'] = 'Num Multiplication',
    ['b_135'] = 'Num Enter',
    ['b_137'] = 'Num1',
    ['b_138'] = 'Num2',
    ['b_139'] = 'Num3',
    ['b_140'] = 'Num4',
    ['b_141'] = 'Num5',
    ['b_142'] = 'Num6',
    ['b_143'] = 'Num7',
    ['b_144'] = 'Num8',
    ['b_145'] = 'Num9',
    ['b_170'] = 'F1',
    ['b_171'] = 'F2',
    ['b_172'] = 'F3',
    ['b_173'] = 'F4',
    ['b_174'] = 'F5',
    ['b_175'] = 'F6',
    ['b_176'] = 'F7',
    ['b_177'] = 'F8',
    ['b_178'] = 'F9',
    ['b_179'] = 'F10',
    ['b_180'] = 'F11',
    ['b_181'] = 'F12',
    ['b_182'] = 'F13',
    ['b_183'] = 'F14',
    ['b_184'] = 'F15',
    ['b_185'] = 'F16',
    ['b_186'] = 'F17',
    ['b_187'] = 'F18',
    ['b_188'] = 'F19',
    ['b_189'] = 'F20',
    ['b_190'] = 'F21',
    ['b_191'] = 'F22',
    ['b_192'] = 'F23',
    ['b_193'] = 'F24',
    ['b_194'] = 'Arrow Up',
    ['b_195'] = 'Arrow Down',
    ['b_196'] = 'Arrow Left',
    ['b_197'] = 'Arrow Right',
    ['b_198'] = 'Delete',
    ['b_199'] = 'Escape',
    ['b_200'] = 'Insert',
    ['b_201'] = 'End',
    ['b_210'] = 'Delete',
    ['b_211'] = 'Insert',
    ['b_212'] = 'End',
    ['b_1000'] = 'Shift',
    ['b_1002'] = 'Tab',
    ['b_1003'] = 'Enter',
    ['b_1004'] = 'Backspace',
    ['b_1009'] = 'PageUp',
    ['b_1008'] = 'Home',
    ['b_1010'] = 'PageDown',
    ['b_1012'] = 'CapsLock',
    ['b_1013'] = 'Control',
    ['b_1014'] = 'Right Control',
    ['b_1015'] = 'Alt',
    ['b_1055'] = 'Home',
    ['b_1056'] = 'PageUp',
    ['b_2000'] = 'Space'
}

--- Gets the human-readable label for a control/command hash
--- @param commandHash number - the control command hash
--- @return string - the readable key label
function Utils.getKeyLabel(commandHash)
    local key = GetControlInstructionalButton(0, commandHash | 0x80000000, true)
    if string.find(key, "t_") then
        local label, _ = string.gsub(key, "t_", "")
        return label
    else
        return SpecialKeyCodes[key] or "unknown"
    end
end

-- ================================
-- TABLE & GAME UTILITIES
-- ================================

--- Checks if value exists in table
--- @param tbl table - table to search
--- @param value any - value to find
--- @return boolean
function Utils.tableContains(tbl, value)
    for _, v in pairs(tbl) do
        if v == value then
            return true
        end
    end
    return false
end

--- Formats time for display
--- @param seconds number - time in seconds
--- @return string - formatted time
function Utils.formatTime(seconds)
    local h = math.floor(seconds / 3600)
    local m = math.floor((seconds % 3600) / 60)
    local s = seconds % 60

    local parts = {}
    local units = _L('timeUnits')
    if not units then return 'N/A' end

    if h > 0 then table.insert(parts, h .. units.hour) end
    if m > 0 then table.insert(parts, m .. units.min) end
    if s > 0 then table.insert(parts, s .. units.sec) end

    return table.concat(parts, " ")
end

-- ================================
-- SOUND UTILITIES
-- ================================

--- Plays sound through NUI
--- @param soundData table - sound parameters
function Utils.playSound(soundData)
    SendReactMessage('playSound', soundData)
end

--- Stops sound through NUI
--- @param soundData table - sound parameters
function Utils.stopSound(soundData)
    SendReactMessage('stopSound', soundData)
end

-- ================================
-- DEBUG UTILITIES
-- ================================

--- Safe debug logging
--- @param ... any - message to log
function Utils.debug(...)
    if falcon and falcon.debug then
        falcon.debug(...)
    else
        print("[DEBUG] ", ...)
    end
end

--- Safe error logging
--- @param ... any - error message
function Utils.error(...)
    if falcon and falcon.error then
        falcon.error(...)
    else
        print("[ERROR] ", ...)
    end
end

--- Safe warning logging
--- @param ... any - warning message
function Utils.warn(...)
    if falcon and falcon.warn then
        falcon.warn(...)
    else
        print("[WARN] ", ...)
    end
end
```

***

> Server-Side File

```lua
Utils = {}

-- ================================
-- EVENT MANAGEMENT UTILITIES
-- ================================

--- Registers a server event
--- If you have a secure event system, you can replace this with that
--- @param name string - the event name to register
--- @param cb function - the callback to execute when the event is triggered
--- @todo: Consider replacing with Safe-Events if available
--- @example RegisterNetEvent ⭢ falcon.safeServerEvent
function Utils.registerNetEvent(name, cb)
    RegisterNetEvent(name)
    AddEventHandler(name, cb)
end

--- triggers a client event with error handling
--- if you have a secure event system, you can replace this with that
--- @param eventName string - the client event name
--- @param ... any - event parameters
--- @todo: Consider replacing with Safe-Events if available
--- @example TriggerClientEvent ⭢ falcon.safeTriggerClientEvent
function Utils.triggerClientEvent(eventName, player, ...)
    TriggerClientEvent(eventName, player, ...)
end

-- ================================
-- DEBUG UTILITIES
-- ================================

--- Safe debug logging
--- @param ... any - message to log
function Utils.debug(...)
    if falcon and falcon.debug then
        falcon.debug(...)
    else
        print("[DEBUG] ", ...)
    end
end

--- Safe error logging
--- @param ... any - error message
function Utils.error(...)
    if falcon and falcon.error then
        falcon.error(...)
    else
        print("[ERROR] ", ...)
    end
end

--- Safe warning logging
--- @param ... any - warning message
function Utils.warn(...)
    if falcon and falcon.warn then
        falcon.warn(...)
    else
        print("[WARN] ", ...)
    end
end
```

### Bridge Files

{% file src="/files/1M3e98u1EZwhQJuvfNbk" %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://falcon-development.gitbook.io/product-docs/new-scripts/russian-roulette/configuration.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
