Table of Contents
- Inventory
- Overview
- GameItem
- Item Identification
- Original ID vs. Dynamic ID
- Key Properties
- Item Categories
- Equipment Positions
- Dynamic vs. Static Items
- Durability
- Material System
- Combustibility
- Tags
- FixtureInventory
- Creating an Inventory
- Capacity Modes
- Adding Items
- Removing Items
- Querying Items
- InventoryPair
- Item List
- Capacity Tracking
- Clearing Inventory
- ActorInventory
- Creating an Actor Inventory
- Equipment System
- Quickslots
- Dynamic Capacity
- Consuming Items
- Item Durability Management
- ItemCodex
- Item Images
- Inventory UI
- Best Practises
- Common Patterns
- Adding a Block to Inventory
- Equipping a Tool
- Checking Crafting Requirements
- Transferring Items Between Inventories
- Finding All Tools
- Serialisation
- Advanced Topics
- See Also
Inventory
The inventory system manages storage and organisation of items for actors, fixtures, and the world itself. It handles item stacking, equipment, encumbrance, and item persistence.
Overview
Terrarum uses a flexible inventory system with several specialisations:
- FixtureInventory — Base inventory for storage containers
- ActorInventory — Extended inventory for actors with equipment and quickslots
- ItemTable — Dynamic item storage for worlds
GameItem
All items in the game extend the GameItem base class.
Item Identification
Items use string-based IDs:
typealias ItemID = String
Item ID formats:
- Static items:
item@<module>:<id>(e.g.,item@basegame:1) - Blocks as items:
<module>:<id>(e.g.,basegame:32) - Dynamic items:
item@<module>:D<number>(e.g.,item@basegame:D4096) - Actor items: Special prefix for actors in pockets
Original ID vs. Dynamic ID
Items have two ID fields:
val originalID: ItemID // The base item type (e.g., "item@basegame:1")
var dynamicID: ItemID // The specific instance (may be same as originalID)
Static items have originalID == dynamicID and can stack.
Dynamic items get unique dynamicID values and represent individual instances (e.g., a worn pickaxe).
Key Properties
// Basic Properties
var baseMass: Double // Mass in kilograms
var baseToolSize: Double? // Physical size for tools
var inventoryCategory: String // "weapon", "tool", "armour", etc.
// Display
var originalName: String // Translation key
var name: String // Displayed name (with custom names)
var nameColour: Color // Item name colour (rarity, etc.)
var nameSecondary: String // Additional description
// Behaviour
val canBeDynamic: Boolean // Can create unique instances
val isCurrentlyDynamic: Boolean // Is this a dynamic instance
var stackable: Boolean // Can stack in inventory
val isConsumable: Boolean // Destroyed on use
var isUnique: Boolean // Only one can exist
var equipPosition: Int // Where to equip (EquipPosition enum)
Item Categories
The inventoryCategory classifies items:
weapon— Melee and ranged weaponstool— Pickaxes, axes, hammersarmour— Protective equipmentblock— Placeable blockswire— Wires and conduitsfixture— Furniture and fixturesgeneric— Miscellaneous itemsquest— Quest-related items
Equipment Positions
Items can be equipped in specific slots defined by EquipPosition:
EquipPosition.HAND_PRIMARY // Main hand
EquipPosition.HAND_SECONDARY // Off hand
EquipPosition.HEADGEAR // Helmet
EquipPosition.ARMOUR // Body armour
EquipPosition.LEGGINGS // Leg armour
EquipPosition.BOOTS // Footwear
EquipPosition.ACCESSORY_1 // Ring, amulet, etc.
EquipPosition.ACCESSORY_2
// ... more accessory slots
EquipPosition.NULL // Not equippable
Dynamic vs. Static Items
Static items are identical instances that stack:
- Blocks
- Consumables
- Resources
- Stackable materials
Dynamic items have individual state:
- Tools with durability
- Weapons with enchantments
- Armour with damage
- Custom-named items
Creating a dynamic instance:
val dynamicItem = staticItem.makeDynamic(inventory)
Dynamic items get assigned IDs in the range 32768..1048575.
Durability
Dynamic items can have durability:
var durability: Float // Current durability (0.0 = broken)
val maxDurability: Float // Maximum durability when new
When durability reaches 0, the item is destroyed.
Material System
Items are made from materials:
val materialId: String // Material identifier
val material: Material // Full material object
Materials affect item properties like mass, durability, and value. See MaterialCodex for available materials.
Combustibility
Items with the COMBUSTIBLE tag can be used as fuel:
var calories: Double // Energy content (game-calories)
var smokiness: Float // Smoke emission rate
1 game-calorie ≈ 5 watt-hours. An item burning for 80 seconds at 60 FPS has 4800 calories.
Tags
Items use tags to mark special properties:
interface TaggedProp {
fun hasTag(tag: String): Boolean
fun addTag(tag: String)
}
Common tags:
TOOL— Can be used as a toolWEAPON— Can be used as a weaponCOMBUSTIBLE— Can be burned as fuelMAGIC— Has magical propertiesTREASURE— Valuable loot
FixtureInventory
The base inventory class for storage containers.
Creating an Inventory
val inventory = FixtureInventory(
maxCapacity = 1000L, // Maximum capacity
capacityMode = CAPACITY_MODE_COUNT
)
Capacity Modes
CAPACITY_MODE_COUNT // Limit by item count
CAPACITY_MODE_WEIGHT // Limit by total weight
CAPACITY_MODE_NO_ENCUMBER // Unlimited capacity
Adding Items
// Add an item
inventory.add(item: GameItem, count: Long = 1L)
// Add by ID
inventory.add(itemID: ItemID, count: Long)
Items automatically stack if they're stackable and have matching IDs.
Removing Items
// Remove by ID
val removedCount = inventory.remove(itemID: ItemID, count: Long): Long
// Remove by item
val removedCount = inventory.remove(item: GameItem, count: Long): Long
Returns the actual number removed (may be less than requested).
Querying Items
// Check if item exists
val has: Boolean = inventory.has(itemID: ItemID, minCount: Long = 1L)
// Search by ID
val pair: InventoryPair? = inventory.searchByID(itemID: ItemID)
// Get item count
val count: Long = inventory.count(itemID: ItemID)
InventoryPair
Inventory items are stored as pairs:
data class InventoryPair(
val itm: ItemID, // Item ID
var qty: Long // Quantity
)
Item List
Access all items in the inventory:
val itemList: List<InventoryPair> = inventory.itemList
Important: Don't modify the item list directly. Use add() and remove() methods.
Capacity Tracking
val capacity: Long // Current used capacity
val maxCapacity: Long // Maximum capacity
val encumberment: Double // 0.0-1.0+ (over 1.0 = overencumbered)
Clearing Inventory
val removedItems: List<InventoryPair> = inventory.clear()
Returns all items that were removed.
ActorInventory
Extended inventory for actors (players, NPCs) with equipment and quickslots.
Creating an Actor Inventory
val inventory = ActorInventory(
actor = playerActor,
maxCapacity = 10000L,
capacityMode = CAPACITY_MODE_WEIGHT
)
Equipment System
Actors can equip items in specific slots:
// Equipment array (indexed by EquipPosition)
val itemEquipped: Array<ItemID?> = inventory.itemEquipped
// Equip an item
actor.equipItem(itemID: ItemID)
// Unequip an item
actor.unequipItem(itemID: ItemID)
Important: Equipped items must also exist in the main itemList. The equipment array stores references (dynamicID), not separate copies.
Quickslots
Actors have a quickslot bar for fast access:
val quickSlot: Array<ItemID?> = inventory.quickSlot // 10 slots by default
// Set quickslot
inventory.setQuickslotItem(slot: Int, dynamicID: ItemID?)
// Get quickslot item
val pair: InventoryPair? = inventory.getQuickslotItem(slot: Int?)
Quickslots reference items in the main inventory by their dynamicID.
Dynamic Capacity
For actors, capacity can scale with actor size:
val maxCapacityByActor: Double // Scaled by actor's scale squared
This means larger actors can carry more.
Consuming Items
inventory.consumeItem(item: GameItem, amount: Long = 1L)
This method:
- Removes consumable items
- Applies durability damage to tools/weapons
- Auto-equips replacements when tools break
- Handles dynamic item unpacking
Item Durability Management
When using tools/weapons, durability decreases:
// Damage calculation
val baseDamage = actor.avStrength / 1000.0
item.durability -= damageAmount
// When durability reaches 0
if (item.durability <= 0) {
// Item is removed and auto-replaced if available
}
ItemCodex
The global registry of all items:
object ItemCodex {
// Look up item by ID
operator fun get(itemID: ItemID): GameItem?
// Register a new item
fun registerItem(item: GameItem)
}
Access items via the global codex:
val item = ItemCodex[itemID]
Item Images
Items have associated sprites:
// Get item image (use this, not item.itemImage directly)
val texture: TextureRegion = ItemCodex.getItemImage(item)
Never read item.itemImage directly due to initialization order issues. Always use ItemCodex.getItemImage().
Inventory UI
ActorInventory integrates with several UI components:
UIQuickslotBar
Displays and manages quickslot items.
UIInventoryFull
Full inventory screen with equipment, storage, and crafting.
InventoryTransactionNegotiator
Handles item transfers between inventories (drag-and-drop, shift-click, etc.).
Best Practises
- Use dynamic IDs for equipment — Store
dynamicIDin equipment/quickslot arrays, notoriginalID - Check canBeDynamic before creating instances — Only dynamic items can have individual state
- Clean up equipment on item removal — ActorInventory does this automatically
- Validate item existence — Always check
ItemCodex[id]returns non-null - Use ItemCodex.getItemImage() — Never access
item.itemImagedirectly - Scale capacity by actor size — Use
maxCapacityByActorfor realistic encumbrance - Handle item consumption properly — Use
consumeItem()for tools to manage durability - Respect capacity modes — Check
encumbermentto limit player movement when overencumbered
Common Patterns
Adding a Block to Inventory
// Blocks use module:id format, not item@module:id
val blockID = "basegame:32"
inventory.add(blockID, 64L) // Add 64 blocks
Equipping a Tool
val pickaxe = ItemCodex["item@basegame:pickaxe_iron"]
if (pickaxe != null && pickaxe.canBeDynamic) {
// Create dynamic instance
val dynamicPickaxe = pickaxe.makeDynamic(actor.inventory)
actor.inventory.add(dynamicPickaxe)
// Equip it
actor.equipItem(dynamicPickaxe.dynamicID)
// Add to quickslot
actor.inventory.setQuickslotItem(0, dynamicPickaxe.dynamicID)
}
Checking Crafting Requirements
fun hasRequiredItems(inventory: FixtureInventory, requirements: Map<ItemID, Long>): Boolean {
return requirements.all { (itemID, count) ->
inventory.has(itemID, count)
}
}
Transferring Items Between Inventories
fun transferItem(from: FixtureInventory, to: FixtureInventory, itemID: ItemID, count: Long): Long {
val removed = from.remove(itemID, count)
if (removed > 0) {
to.add(itemID, removed)
}
return removed
}
Finding All Tools
val tools = actor.inventory.itemList
.mapNotNull { ItemCodex[it.itm] }
.filter { it.hasTag("TOOL") }
Serialisation
Inventories are serialisable for save games. However:
actorfield is@Transient— Reconstructed on reload- Equipment and quickslots are saved
- Dynamic items preserve their unique state
On reload, call:
inventory.actor = restoredActor
Advanced Topics
Custom Item Classes
Create custom items by extending GameItem:
class MyCustomItem : GameItem("item@mymod:custom_item") {
override var baseMass = 1.0
override var baseToolSize: Double? = null
override val canBeDynamic = true
override val materialId = "iron"
override var inventoryCategory = "generic"
init {
originalName = "ITEM_CUSTOM_NAME"
equipPosition = EquipPosition.HAND_PRIMARY
}
override fun startPrimaryUse(actor: ActorWithBody, delta: Float) {
// Custom usage logic
}
}
Inventory Filtering
Filter items by category or tag:
val weapons = inventory.itemList
.mapNotNull { ItemCodex[it.itm] }
.filter { it.inventoryCategory == "weapon" }
Weight Calculation
Calculate total inventory weight:
val totalWeight = inventory.itemList.sumOf { (itemID, quantity) ->
(ItemCodex[itemID]?.baseMass ?: 0.0) * quantity
}
See Also
- Glossary — Item and inventory terminology
- Modules:Items — Creating items in modules
- Development:Items — In-depth item implementation
- Actors — Actor system that uses inventories