Tetris-grid inventory with weight, slots, drops, shops, and weapon support.
Part of the COREX Framework.
Drop the corex-inventory folder into:
server-data/resources/[corex]/corex-inventory/
The Inventory manifest requires corex-core. The native inventory uses the framework's database setup; follow the full Recipe installation for the database and shared services.
ox_target is optional for the inventory itself, but required for its portable-vehicle look-at pickup interaction. The consumer calls ox_target directly; replacing the target library does not automatically adapt this interaction. Check that your selected target resource supports the actual exports and configuration you use. ox_lib is not a hard dependency of this inventory; other resources, including Admin, may require it.
Make sure the load order is:
ensure corex-core
ensure ox_target # Only when the portable-vehicle pickup interaction is enabled
ensure corex-inventoryBack up the database, configuration and recovery data first. Select the reviewed compatible revision, merge configuration changes, and update with players disconnected. Do not overwrite local configuration blindly or delete inventory-recovery.json; it can contain unacknowledged player saves and must never be published.
CoreX inventory data is stored in corex_inventories, leaving the generic
inventories table available to replacements such as qb-inventory. On the
first start after upgrading, an older CoreX-shaped inventories table is copied
to the namespaced table and retained as corex_inventories_legacy. A table that
does not contain CoreX's inventory_type, inventory_id, and hotbar columns
is left untouched.
π https://corex-zombies.gitbook.io/corex-docs/reference/native-inventory
Players can turn a deployed, owned vehicle from a COREX vehicle catalog into a single inventory item so it survives server restarts and world wipes. This is no longer bicycle-only: cars, bikes, scooters, helicopters, and future vehicle catalogs can use the same flow. Requires ox_target (ensure ox_target before this resource).
- Player buys a vehicle from any
type = "vehicle"shop backed by a COREX vehicle catalog. - After spawn, the client registers the networked vehicle with the server; the server sets portable vehicle state bags on the entity.
- Player aims at their vehicle with ox_target β Pick up vehicle.
- The vehicle is removed from the world and a
portable_vehicleitem is added with metadata:owner,catalogId,plate,model,label,itemName. - From inventory, Use closes the bag, spawns the vehicle in front of the player, and auto-enters the driver seat by default.
Pickup does not use the global player βbusyβ lock (TrySetBusy), so it still works if another action left isBusy stuck; a short cooldown prevents double-submit spam.
| Where | What is stored |
|---|---|
| Inventory row | metadata.owner = COREX player.identifier; metadata.catalogId, metadata.model, metadata.plate |
| World entity (while parked) | State bags set after successful registration (corexPortableVehicleOwner, etc.) |
If the item stays in the backpack, ownership survives reconnect and restart because it is saved with the inventory payload.
| Area | File |
|---|---|
| Pickup / spawn-from-item / ox_target | client/rental_bicycle.lua (compat filename; portable vehicle implementation) |
Shop spawn hook + exports (SetActiveRentalVehicle, β¦) |
client/main.lua |
| Purchase plate, finalize, pickup, deploy events | server/main.lua |
| Item definition | shared/items.lua β portable_vehicle |
corex-inventory:server:finalizePortableVehicleβ attaches ownership to a freshly spawned vehicle (catalog + plate + model match).corex-inventory:server:pickupPortableVehicleβ validates owner, addsportable_vehicle, deletes entity for all clients.corex-inventory:server:deployPortableVehicleFromItemβ validates metadata, removes item, spawns client-side deploy + finalize.
Server-side validation uses allowed model hashes from the metadata catalogId, not client-only vehicle-class checks. rental_bicycle events/items are still accepted as compatibility aliases.
You do not need to copy rental_bicycle.lua or create new pickup/deploy events for every vehicle type. Add a vehicle catalog in corex-core, point a vehicle shop at that catalog, and choose which inventory item should represent the vehicle.
File: server-file/resources/[corex]/corex-core/shared/vehicles.lua
Corex.SharedVehicles["car_rental"] = {
id = "car_rental",
label = "Car Rental",
subtitle = "BASIC CARS",
currency = "cash",
purchaseLabel = "Deploy Car",
-- true = vehicles in this catalog can be picked up with ox_target.
portable = true,
-- Optional. If omitted, vehicles use Config.PortableVehicles.ItemName
-- from corex-inventory/config.lua, which defaults to "portable_vehicle".
inventoryItem = "portable_vehicle",
vehicles = {
{
model = "blista",
label = "Blista",
category = "compact",
price = 1200,
description = "Small reliable city car.",
-- Optional per-vehicle override.
-- portable = false disables pickup for this vehicle only.
portable = true
},
{
model = "sanchez",
label = "Sanchez",
category = "bike",
price = 900
}
}
}File: server-file/resources/[corex]/corex-inventory/shared/shops.lua
Shops["Car Rental"] = {
label = "Car Rental",
type = "vehicle",
catalogId = "car_rental",
npc = {
model = "s_m_m_autoshop_02",
coords = vector4(1518.0, 1708.0, 109.0, 90.0),
scenario = "WORLD_HUMAN_CLIPBOARD",
icon = "fa-car",
interactLabel = "[E] Car Rental",
interactDistance = 2.5
},
spawnPoint = vector4(1514.0, 1705.0, 109.0, 90.0),
items = {}
}Most systems can use the default portable_vehicle item. If you want a custom item like rental_bicycle, add it to shared/items.lua and set inventoryItem on the catalog or vehicle.
['rental_helicopter'] = {
label = 'Helicopter',
weight = 20.0,
size = {w = 3, h = 3},
stackable = false,
usable = true,
image = 'default.png',
iconMetadataKey = 'model',
rarity = 'epic'
}Then in the catalog:
inventoryItem = "rental_helicopter"You can also set it per vehicle:
{
model = "buzzard",
label = "Buzzard",
price = 25000,
inventoryItem = "rental_helicopter"
}portable = trueon a catalog makes the catalog portable.portable = falseon a vehicle blocks only that vehicle.inventoryItemon a vehicle overridesinventoryItemon the catalog.- If no
inventoryItemis set, the system usesConfig.PortableVehicles.ItemName, defaultportable_vehicle. rental_bicycleis just a custom item forbike_rental; the pickup/deploy logic is still the same portable vehicle system.
Icons are chosen in html/script.js via getItemImageSrc(item, itemDef). Item definitions from shared/items.lua are sent to the UI as itemsData.
['some_item'] = {
label = 'Some item',
image = 'default.png', -- fallback if dynamic icon cannot be resolved
iconMetadataKey = 'model', -- read this key from item.metadata
-- iconFilenamePattern is OPTIONAL:
-- if omitted, pattern defaults to "%s.png" β images/<value>.png
iconFilenamePattern = 'pack_%s.png', -- optional; must contain one "%s"
-- ...
}Examples
- Same style as bike models: only set
iconMetadataKey = 'model'β fileshtml/images/bmx.png,cruiser.png, etc. (sanitized lowercase). - Custom prefix:
iconFilenamePattern = 'loot_%s.png'βhtml/images/loot_gold.pngwhenmetadata.tierwould neediconMetadataKey = 'tier'β adjust key/pattern to match your metadata.
Invalid or missing metadata falls back to image, then images/<item_name>.png, then default.png (via onerror).
corex-inventory/client/weapons.lua only applies a combat-skill effect when
the FiveM/GTA V boundary supports it without inventing native behavior.
| Skill effect | Current inventory-side status |
|---|---|
| Recoil / Steady Aim | Uses COREX's own additive recoil plus the documented SHAKE_GAMEPLAY_CAM path. The skill multiplier scales COREX's configured shake intensity; no undocumented raw native hash is required. |
Quick Reload (reloadSpeed = 0.80) |
server/reload_runtime.lua owns an opaque timed reload. The client plays the normal reload animation and cannot spend/increase reserve ammo itself. Death, weapon switch, reconnect/session change and expiry cancel the pending reload. |
Iron Sights (aimSpeed = 0.70) |
Uses an aim-capable scripted camera transition. The default 220 ms transition becomes 154 ms for the skill, then the scripted camera is destroyed and normal GTA camera control resumes. |
| Akimbo | Pistols/SMGs render an off-hand copy of the equipped weapon. Each detected primary shot can request exactly one additional round; the server validates skill/category/weapon/ammo progression/rate before authorizing the second shot. weaponDamage = 0.85 is applied while Akimbo is active. |
| Weapon Modder | Craft mod items at a workbench, equip the weapon, then use `/weaponmod <grip |
| Headshot Pro / Executioner | Locally owned victims use entityDamaged, GET_PED_LAST_DAMAGE_BONE, live health and a control fence. For remote COREX zombies, Zombies matches the attacker's engine hit to the native owner's damage observation and routes the authorized bonus to that owner. Inventory does not submit attacker-selected remote bonus damage. |
Primary Cfx references: citizenfx/fivem GameEventFunctions.cpp, and
citizenfx/natives entries for SET_CAM_AFFECTS_AIMING,
DOES_WEAPON_TAKE_WEAPON_COMPONENT, GIVE_WEAPON_COMPONENT_TO_PED,
SET_PLAYER_WEAPON_DAMAGE_MODIFIER, SHOOT_SINGLE_BULLET_BETWEEN_COORDS,
GET_PED_LAST_DAMAGE_BONE, IS_ENTITY_DEAD, and SHAKE_GAMEPLAY_CAM.
tests/test_weapon_skill_engine_contracts.py checks source structure only.
test_weapon_actions_integration.py, test_weapon_metadata_boundary.py and
test_client_reload.py execute actual server/client Lua with controlled external
boundaries. They cover transactions, stale replies, session changes and cancellation,
but do not replace native gameplay acceptance for camera easing, off-hand
rendering, or cross-owner Headshot/Executioner damage. Update server and client
together: reload messages carry request correlation and completion tokens. Confirmed
reloads also advance metadata.ammoRevision; pre-reload ammo/Akimbo reports are
rejected, and newer inventory snapshots restore the authoritative ammo generation.
Legacy items without a revision use zero. Preserve this field during item transfers.
π¬ https://discord.gg/G95rtnb9sg
Released under the MIT License.