Koja Scripts

Configuration

Every option in data/koja/core.lua, interact.lua, admin.lua, rarity.lua and staff.lua, plus the upstream data files Koja extends.

Configuration

Koja Inventory adds five config files in data/koja/. They return plain Lua tables and are merged into one config on start.

FileWhat is in it
data/koja/core.luaSlot layout, default player settings, clothing, weight, throwing, plates, fast actions, ped preview
data/koja/interact.luaGive, trade, search animation, ox_target options, discovery, shops, weapon attachments
data/koja/admin.luaAdmin command, roles, addon toggles, history and security defaults
data/koja/rarity.luaThe default rarities
data/koja/staff.luaStaff identifiers (server only, never sent to clients)

The upstream files in data/ (items.lua, weapons.lua, shops.lua, stashes.lua, crafting.lua...) keep their ox_inventory format. Koja only adds optional fields, listed at the end of this page and in Items.

Restart the resource after changing a file.

Slot layout

data/koja/core.lua
baseSlots = 25,
pockets = 5,
OptionDefaultWhat it does
pockets5Slots 1..pockets are the Pockets row (hotbar)
baseSlots25Slots up to this number are always unlocked. Slots above it, up to inventory:slots, need a backpack

Default player settings

Every player can change these in the Settings tab (see Player settings). Their choice is saved on their PC (client KVP koja_inv_settings); these are the values for new players and for the Reset button.

data/koja/core.lua
defaults = {
    accent = '#F5A623',
    secondary = 'rgba(25,26,26,0.98)',
    rows = 6,
    scale = 100,
    clothingPanel = 'center',
    theme = 'v2',
    font = 'Poppins',
    clothingInventory = true,
    pedCamera = true,
    notifyStyle = 'compact',
    sharp = false,
    streamer = false,
    sounds = true,
},
OptionValuesWhat it does
accent, secondaryCSS colourMain colour and panel background
rows4–8Visible rows of the inventory grid
scale70–130UI scale in percent
clothingPanelleft, center, rightWhere the clothes panel and ped preview sit
themev2, ferrofluid, minimal, dotted, antigravityInventory style
fontPoppins, Inter, Montserrat, Rajdhani, BarlowUI font
clothingInventorybooleanShows Your clothes next to the inventory
pedCamerabooleanfalse keeps the gameplay camera; the ped is shown inside the UI instead
notifyStylecompact, minimalItem added / removed popups while the inventory is closed
sharpbooleanSquare corners
streamerbooleanStreamer mode
soundsbooleanUI sounds

settingsLimits holds the allowed ranges and values used to clean saved settings (rows = { 4, 8 }, scale = { 70, 130 }, the panel positions and themes). Change it only if you add a theme to the UI yourself.

Clothing

data/koja/core.lua
clothing = {
    enabled = true,
    imageResource = 'illenium-appearance',
    saveAppearance = true,
    adoptAppearance = true,
    externalAppearance = true,
    hiddenSlots = { 'underwear_top', 'underwear_bottom' },
    slots = { ... },
    nakedMale = { ... },
    nakedFemale = { ... },
    genericItem = 'clothing',
    hangerItem = 'clothing_hanger',
    undressAnim = { dict = 'clothingtie', clip = 'try_tie_negative_a', duration = 1200 },
},
OptionWhat it does
enabledClothing as items. Also switchable live as the Clothing as items addon
imageResourceResource that exports getClothingImage(gender, category, drawable, texture) — illenium-appearance with Koja Appearance
saveAppearanceSaves the ped appearance through illenium-appearance / fivem-appearance after every change
adoptAppearanceClothes the ped already wears become clothing items when the matching slot is empty
externalAppearanceAn appearance saved by a skin menu or outfit wins over the worn items: changed pieces are rewritten, removed or named pieces go back to the inventory. Bag and vest stay items
hiddenSlotsSlots tracked as worn but not shown in the UI
slotsThe clothing slots, in UI order. See below
nakedMale, nakedFemale[componentId] = { drawable, texture } applied when a slot is empty (freemode peds). Props are cleared
genericItemItem given when you undress a piece that has no item of its own
hangerItemThe hanger item used by the Put on hanger fast action
undressAnimAnimation played while dressing / undressing

Clothing slots

Each entry of slots:

data/koja/core.lua
{ key = 'torso', label = 'Torso', icon = 'layers', column = 'left', kind = 'component', id = 11, category = 'jackets' },
FieldWhat it is
keySlot key used in the code and in the locale (clothing_slot_<key>)
label, iconFallback label and Lucide icon name
columnleft or right of the ped
kind, idcomponent + component id, or prop + prop id
categoryCategory key used for the image lookup (hats, masks, jackets...)

The default slots: hat, mask, glasses, ears, chain, tshirt, torso, arms, vest, bag, pants, shoes, watch, bracelet, decals, plus the hidden underwear_top and underwear_bottom.

Weight and overload

data/koja/core.lua
weight = {
    enabled = true,
    overloadPct = 0.85,
    clipset = 'move_m@hobo@a',
    moveRate = 0.85,
    disableSprintPct = 0.97,
    staminaDrain = 2.5,
    sweat = true,
    sweatAmount = 80.0,
},
OptionWhat it does
overloadPctEffects start at this fraction of the max weight
clipsetMovement clipset while overloaded. false = none
moveRateMove rate while overloaded
disableSprintPctNo sprint or jump from this fraction of the max weight
staminaDrainStamina points lost per second while running overloaded
sweat, sweatAmountSweat on the ped while overloaded

Live switch: the Weight effects addon.

Throwing

data/koja/core.lua
throw = {
    enabled = true,
    anyItem = true,
    maxDistance = 30.0,
    minForce = 6.0,
    maxForce = 22.0,
    forcePerKg = 1.2,
    defaultProp = 'prop_cs_cardbox_01',
    landTimeout = 7000,
    aimTimeout = 15000,
    keys = { throw = 24, cancel = 25 },
    anim = nil,
    weapons = { ... },
},
OptionWhat it does
anyItemEvery item can be thrown unless its definition says throwable = false. With false, only items with throwable = true
maxDistanceThe server rejects landings further than this from the thrower
minForce, maxForce, forcePerKgThrow force; heavier stacks fly shorter
defaultPropProp thrown when the item has no prop
landTimeoutMilliseconds before the prop position is taken as the landing point
aimTimeoutMilliseconds of aiming before the throw is cancelled
keysControl ids while aiming: throw (24, left mouse) and cancel (25, right mouse)
anim{ dict, clip, release } to force one throw animation. nil uses the built-in list

Throwing the weapon in hand

data/koja/core.lua
weapons = {
    enabled = true,
    mapper = 'mouse_button',
    key = 'MOUSE_MIDDLE',
    minHold = 250,
    chargeTime = 1200,
    minCharge = 0.45,
    maxDistance = nil,
    denyProjectiles = true,
    allow = nil,
    deny = { 'WEAPON_UNARMED', 'WEAPON_PETROLCAN', 'WEAPON_HAZARDCAN', 'WEAPON_FERTILIZERCAN', 'WEAPON_FIREEXTINGUISHER', 'WEAPON_STUNGUN_MP' },
},
OptionWhat it does
mapper, keyDefault key of the koja_throw_weapon keybind. Players can rebind it in the FiveM key bindings
minHoldReleasing earlier cancels (no accidental throws)
chargeTime, minChargeHolding charges the force from minCharge to 100% over chargeTime ms
maxDistancenil = throw.maxDistance
denyProjectilesGrenades, molotovs and other thrown weapons cannot be thrown this way
allownil = every weapon, or a list of weapon names
denyWeapons that can never be thrown

Armour plates

data/koja/core.lua
plates = {
    enabled = true,
    maxPerVest = 3,
    vests = { armor_vest = true },
    types = { plate_light = 25, plate_medium = 50, plate_heavy = 100 },
},
OptionWhat it does
maxPerVestPlates one vest can hold
vestsItems that accept plates, besides any item whose clothing.id is 9
typesArmour per plate item, used when the item has no plate = { armor = n }

Ped armour is the sum of the plates, capped at 100.

Fast actions

data/koja/core.lua
fastActions = {
    { id = 'handsup', label = 'Hands up',      icon = 'hand' },
    { id = 'undress', label = 'Undress',       icon = 'shirt' },
    { id = 'hanger',  label = 'Put on hanger', icon = 'archive' },
    { id = 'trade',   label = 'Trade',         icon = 'repeat' },
    { id = 'give',    label = 'Give',          icon = 'hand-helping' },
},

The buttons of the Fast actions row. Remove an entry to hide it. Labels can be translated with the locale key fast_action_<id>.

Item text limits

data/koja/core.lua
item = { renameMax = 32, describeMax = 160 },

Maximum length of a renamed item label and of a custom description.

Ped preview

data/koja/core.lua
pedMode = 'clone',
ped = {
    distance = 2.6,
    height = 0.15,
    fov = 38.0,
    offset = { left = 0.95, center = 0.0, right = -0.95 },
},
status = { interval = 500 },
OptionWhat it does
pedModeclone = a camera frames your ped where the clothes panel is. pause = the pause-menu ped on a dark background. Also switchable on the admin Addon Management page
ped.distance, height, fovCamera of clone mode
ped.offsetSideways camera offset per clothes panel position
status.intervalMilliseconds between health / armour / hunger / thirst updates in the clothes panel

Give

data/koja/interact.lua
give = {
    distance = 3.0,
    marker = { type = 25, scale = 1.1, color = { 196, 255, 64, 180 } },
},

distance is the max distance between giver and receiver. marker is the ground circle drawn under the targeted player.

Trade

data/koja/interact.lua
trade = {
    distance = 3.0,
    timeout = 30,
    sessionTimeout = 300,
    maxOffers = 12,
    keys = { accept = 'Y', decline = 'N' },
},
OptionWhat it does
distanceMax distance to send and keep a trade
timeoutSeconds a trade request stays valid
sessionTimeoutSeconds of inactivity before a running trade is cancelled
maxOffersMax different slots one player can offer
keysDefault keys of the koja_trade_accept / koja_trade_decline keybinds (used while the inventory is closed)

World containers

data/koja/interact.lua
search = {
    enabled = true,
    dumpster = { dict = 'amb@prop_human_bum_bin@base', clip = 'base' },
    trunk = { dict = 'mini@repair', clip = 'fixing_a_ped' },
},

target = {
    enabled = true,
},
OptionWhat it does
search.enabledLooping search animation while a dumpster or trunk inventory is open. Not played in a vehicle
search.<type>Animation per inventory type
target.enabledExtra ox_target options (search dumpster, open trunk, stash and evidence zones) while the server runs without inventory:target. Needs ox_target started. Ignored when inventory:target is on

Both switches can be changed live on the admin World containers page, together with the loot tables.

Discovery

data/koja/interact.lua
discovery = {
    enabled = true,
    types = { 'player', 'drop', 'stash', 'trunk', 'glovebox' },
    skipOwned = true,
    memory = 600,
},
OptionWhat it does
typesInventory types where items have to be discovered
skipOwnedStashes owned by the player are never "discovered"
memorySeconds the server remembers what a player already searched

The search time per item comes from its rarity (discoveryTime in data/koja/rarity.lua).

Shops

data/koja/interact.lua
shop = {
    defaultPayment = { 'cash', 'card' },
    cashItem = 'money',
    interaction = {
        enabled = true,
        distance = 2.0,
        drawDistance = 8.0,
        key = 38,
        keyLabel = 'E',
    },
    dui = {
        enabled = true,
        size = 0.3,
        maxHeight = 0.11,
    },
},
OptionWhat it does
defaultPaymentPayment methods of shops that do not set paymentMethods. cash = the cashItem item, card = the framework bank account
cashItemItem used for cash payments
interaction.enabledThe built-in floating "press E" prompt for shops that do not set it themselves
interaction.distance, drawDistanceDistance to press the key / to see the prompt
interaction.key, keyLabelControl id and label
dui.enabledDraws the prompt as a 3D card in the player's inventory style. Falls back to a text prompt when disabled or when the card fails to load
dui.sizeCard height in metres
dui.maxHeightOn-screen height cap (fraction of the screen) when standing next to it

Weapon attachments

data/koja/interact.lua
attachments = {
    interval = 30,
    fov = 32.0,
    distance = 1.1,
    rotateSpeed = 0.35,
    pitchSpeed = 0.25,
    maxPitch = 45.0,
    minFov = 14.0,
    maxFov = 50.0,
    zoomStep = 2.0,
    smoothing = 0.07,
    bones = { ... },
    fallback = { ... },
    types = { 'muzzle', 'flashlight', 'scope', 'magazine', 'grip', 'barrel', 'skin', 'tint' },
    labels = { ... },
},
OptionWhat it does
intervalMilliseconds between marker position updates
fov, distanceCamera FOV and distance (scaled by the weapon length)
rotateSpeed, pitchSpeed, maxPitchMouse rotation of the weapon
minFov, maxFov, zoomStepMouse wheel zoom
smoothingEasing of rotation and zoom in seconds (0 = instant)
bonesWeapon bones searched, in order, for each marker type
fallbackMarker position (fraction of the weapon bounding box) when no bone exists
types, labelsMarker types in UI order and their labels

Admin

data/koja/admin.lua
admin = {
    command = 'invadmin',
    key = nil,
    superAce = 'group.admin',
    rateLimit = { calls = 30, window = 3000 },
    roles = { ... },
},
OptionWhat it does
commandChat command that opens the admin panel. false disables it
keyOptional keybind, e.g. 'F10' (registered as koja_invadmin)
superAceAce that grants every admin page
rateLimitMax admin calls per window (ms) per player
rolesDefault roles, created in the database on first start: owner, admin, moderator, support. After that, edit them on the Permissions page

Role perms map a page key to true (['*'] = true = every page). Page keys: home, addons, items, rarity, players, history, security, clothing, shops, stashes, permissions, locales.

Addon toggles

data/koja/admin.lua
features = {
    clothing = { default = true, label = 'Clothing as items', description = '...' },
    -- ...
},

default is used until an admin changes the toggle on Addon Management; after that the database value wins. Disabled features fall back to stock ox_inventory behaviour.

KeyFeature
clothingClothing as items
backpackBackpacks
ministashMini stashes
throwThrowing
weightEffectsWeight effects
platesPlate armour
discoveryItem discovery
tradeTrading
giveHover & give
shops3d3D shop interaction
attachmentsWeapon 3D attachments
multiselectMulti-select (SHIFT + click)
renameRename & describe
historyInventory history

History

data/koja/admin.lua
history = {
    enabled = true,
    retentionDays = 30,
    flushInterval = 5000,
    maxBuffer = 500,
    ignore = {},
},
OptionWhat it does
retentionDaysRows older than this are removed on start and every 6 hours. 0 keeps them forever
flushIntervalMilliseconds between buffered database inserts
maxBufferForces a flush when the buffer gets this big
ignoreActions never logged, e.g. { open = true, close = true }

Security

data/koja/admin.lua
security = {
    autoKick = false,
    logHookRejections = true,
    trimPlates = true,
    emergencyLock = false,
    distanceCheck = true,
    maxDistance = 10.0,
    logLevel = 1,
    evidenceGrade = 2,
    retention = 100,
    imageHosts = { 'r2.fivemanage.com', 'i.fmfile.com' },
    severe = { malformed = true, locked_slot = true, exploit = true },
},
OptionWhat it does
autoKickDrops players on severe reports
logHookRejections, trimPlates, logLevel, evidenceGradeLive mirrors of inventory:loghookrejection, inventory:trimplate, inventory:loglevel, inventory:evidencegrade
emergencyLockBlocks every inventory open. Never saved, so it is off after a restart
distanceCheck, maxDistanceReports drops and opens from further than maxDistance metres
retentionRecent reports kept in memory for the Security page
imageHostsHosts allowed in metadata image URLs (inventory:validhosts)
severeReport kinds that count as severe (and trigger autoKick)

Rarities

data/koja/rarity.lua
rarities = {
    { name = 'common',    label = 'Common',    color = '#9AA0A6', discoveryTime = 0,  revealTimer = false },
    { name = 'uncommon',  label = 'Uncommon',  color = '#2ECC71', discoveryTime = 2,  revealTimer = true },
    { name = 'rare',      label = 'Rare',      color = '#3498DB', discoveryTime = 4,  revealTimer = true },
    { name = 'epic',      label = 'Epic',      color = '#9B59B6', discoveryTime = 6,  revealTimer = true },
    { name = 'legendary', label = 'Legendary', color = '#F39C12', discoveryTime = 8,  revealTimer = true },
    { name = 'mythic',    label = 'Mythic',    color = '#E74C3C', discoveryTime = 10, revealTimer = true },
},
FieldWhat it does
nameKey used by items (rarity = 'rare')
label, colorShown in tooltips; the colour tints slot borders and labels
discoveryTimeSeconds an item of this rarity stays "Discovering..." in other inventories
revealTimerShows a countdown on the slot while it is discovered

Items without a rarity are common. Edits on the Edit Rarity page are stored in koja_inv_rarities and override this file.

Staff

data/koja/staff.lua
return {
    ['license:0123456789abcdef'] = 'owner',
}

Identifier → role name. Accepted identifiers: license:, license2:, discord:, fivem:, steam:. This file is not in the manifest files, so clients never download it. Staff added on the Permissions page is stored in the database and merged on top.

Upstream data files

FileWhat Koja adds
data/items.luaOptional item fields (rarity, throwable, prop, unique, isStash, hanger, backpack, clothing, plate, tint) — see Items
data/weapons.luaComponent type decides the marker of the 3D attachments view (sight is shown as scope)
data/shops.luapaymentMethods, interaction and icon per shop — see Features
data/crafting.luaduration per recipe is shown in the crafting panel; count may be { min, max } — see Features
data/stashes.luaUnchanged. Stashes can also be created on the Stashes admin page