Every item in EndCore, from a bandage to a rifle, is defined in one file: en-inventory/data/items.lua. This guide adds two items: a Geiger counter that players can check (and keep), and a scrap bundle that unpacks into scrap metal when used.
1. Define the item
Open resources/[encore]/en-inventory/data/items.lua. It returns a table keyed by item name. Add your entries anywhere inside it:
geiger_counter = {
label = 'Geiger Counter',
description = 'Clicks faster when you should leave.',
category = 'equipment',
size = { 1, 2 },
weight = 0.6,
usable = true,
consume = 0, -- using it never uses it up
},
scrap_bundle = {
label = 'Scrap Bundle',
description = 'Wire-tied panels. Cut it open for scrap metal.',
category = 'material',
size = { 2, 2 },
weight = 2.0,
stack = 3,
usable = true, -- consume defaults to 1
},The item name (geiger_counter) is the id used everywhere else: in commands, loot tables, recipes, trader stock and scripts. Use lowercase letters, numbers and underscores. Weapons are the exception and use the weapon name, such as WEAPON_PISTOL.
Item fields
| Field | Default | What it does |
|---|---|---|
label | required | Display name |
description | none | Tooltip text |
category | none | One of food, drink, medical, weapon, ammo, tool, material, clothing, equipment, misc, key, currency. Traders buy by category, and death bags can use per-category drop chances. |
size | required | Footprint { width, height } in grid cells |
weight | required | Kilograms per unit |
stack | 1 | Most units in one stack |
usable | false | Can be used from the inventory or hotbar |
consume | 1 | Units removed when a use succeeds. 0 keeps the item. |
durability | none | Tracks metadata.durability from 100 down to 0 |
weapon | none | { ammo = 'ammo_pistol', magazine = 12 }, or {} for melee |
ammo | none | Marks an ammo item |
container | none | { width, height, maxWeight } for bags |
clothing | none | Wearable definition, see en-clothing |
deathDrop | none | Chance (0 to 1) this item drops into a death bag, overriding the category and default chance |
2. Choose the footprint
EndCore's inventory is a spatial grid. A player carries an 8 by 6 grid, and every item takes up size cells. Players can rotate items to fit, and a stack takes the same footprint as a single unit.
Footprint is as important as weight for balance. A player who has to choose between a 2 by 3 rifle and six 1 by 1 bandages is making a real survival decision.
| Footprint | Good for | Shipped examples |
|---|---|---|
| 1 by 1 | Small things | bandage, lockpick, cash |
| 1 by 2 | Tall, thin things | radio |
| 2 by 1 | Long, flat things | scrapmetal |
| 2 by 2 and up | Bulky gear, clothing, bags | jackets, backpacks |
Keep in mind that bags add their own grid underneath the player's, and a bag can never go inside another bag.
3. Add an image
Item images live in en-inventory/html/images/ and are named after the item: geiger_counter.png, scrap_bundle.png. The shipped images are 128 by 128 PNGs with transparent backgrounds; match that and they will sit neatly in the grid.
Images are on by default (Config.images = true in en-inventory/config/shared.lua). Set it to false to show a category glyph for every item instead.
4. Make it usable
usable = true tells en-inventory the item can be used, but not what it does. That comes from a usable handler registered by any resource through the library.
The handler receives the player's server id, the item name, and a snapshot of the used stack { id, name, count, metadata }. The item is only consumed if your handler returns true.
Put this in a server script of your own resource (see Writing a resource):
-- server/items.lua
-- Geiger counter: read out radiation. consume = 0, so it is kept either way.
encore.inventory.registerUsable('geiger_counter', function(source, item, slot)
local level = exports['en-core']:GetRadiation(source) or 0
local rate = exports['en-core']:GetRadiationExposure(source) or 0
local reading = rate > 0
and ('%d rads absorbed, +%s per minute here.'):format(math.floor(level), rate)
or ('%d rads absorbed. Background is clean.'):format(math.floor(level))
encore.notify(source, {
title = 'Geiger Counter',
description = reading,
type = level >= 400 and 'error' or rate > 0 and 'warning' or 'inform',
icon = 'radiation',
})
return true
end)
-- Scrap bundle: unpack into scrap metal, but only if it fits.
encore.inventory.registerUsable('scrap_bundle', function(source, item, slot)
if not encore.inventory.canCarry(source, 'scrapmetal', 5) then
encore.notify(source, { description = 'No room for the scrap.', type = 'error' })
return false -- keep the bundle
end
encore.inventory.addItem(source, 'scrapmetal', 5)
encore.skills.addXP(source, 'scavenging', 2)
return true -- consume one bundle
end)A few things to know:
- Registration order doesn't matter. The library remembers your handler and re-applies it whenever en-inventory starts or restarts.
- Handlers are removed automatically when your resource stops.
- If an item is
usablebut nothing registered a handler, the console warnsItem "..." is usable but no resource registered what it does. - Weapons don't need a handler. Using one equips it.
Food, drink and medicine usually don't need a handler at all. Add the item to Config.items in en-consumables/config/shared.lua and describe its effects there; en-consumables registers it as usable, plays the animation and applies hunger, thirst, health, radiation or infection changes. See en-consumables.
5. Restart and test
Item definitions are read when resources start, and several resources read en-inventory's item list (en-shops, en-crafting, en-clothing and en-admin). The simplest way to pick up new items is a server restart.
Then give yourself the items:
/giveitem me geiger_counter 1
/giveitem me scrap_bundle 3Open the inventory with TAB, check the footprint and image, and use each item.
6. Get it into the world
An item nobody can find doesn't exist. Pick one or more ways for players to get it.
| Source | Where | Example |
|---|---|---|
| Zombie corpses | loot.common / loot.rare in en-zombies/config/server.lua | { item = 'scrap_bundle', min = 1, max = 1, weight = 6 } |
| Traders | Config.values and a trader's sells in en-shops/config/shared.lua | geiger_counter = 300 in values, then { item = 'geiger_counter', max = 2 } |
| Crafting | Config.recipes in en-crafting/config/shared.lua | A workbench recipe with result = { item = 'scrap_bundle', count = 1 } |
| Quest rewards | rewards.items in en-quests/data/quests.lua | items = { { item = 'geiger_counter', count = 1 } } |
| Starter kit | starterItems in en-core/config/shared.lua | Given once to each new character |
Remember that an item with no entry in Config.values can't be sold to any trader.
Related
- en-inventory: every export, command and inventory mechanic.
- Inventory interface: the
encore.inventoryAPI. - Add a trader and Create a quest.