Skip to main content

Adding Items to MonolithRP

This comprehensive guide will walk you through the process of adding custom items to the MonolithRP gamemode. Items are defined in the gamemode/config/inventory_items/items.lua file and follow a specific structure.

Basic Item Structure

Every item in MonolithRP follows this basic pattern:

local ITEM = nil

ITEM = Monolith.InventoryItem( "item_id", L( "cfg.item.item_name" ), L( "cfg.item.desc.item_description" ) )
-- Item properties go here
ITEM.Category = ITEM.ITEM_CONSUMABLE
ITEM.Model = "models/path/to/model.mdl"
-- Additional properties...

-- Server-side functions (if needed)
if SERVER then
function ITEM:OnUse( pPlayer, data )
-- Use logic here
end

function ITEM:CanUse( pPlayer )
-- Return true/false and optional message
return true
end
end

Monolith.InventoryItems:RegisterItem( ITEM )

Required Properties

Basic Information

  • Item ID: First parameter of Monolith.InventoryItem() - unique string identifier
  • Name: Second parameter - localized name using L() function
  • Description: Third parameter - localized description using L() function

Essential Properties

  • ITEM.Category: Defines the item type (see Item Categories)
  • ITEM.Model: 3D model path for the item

Item Categories

Items are categorized using these constants:

CategoryDescriptionUsage
ITEM.ITEM_CONSUMABLEItems that can be used/consumedHealth kits, food, drugs
ITEM.ITEM_EQUIPMENTEquippable itemsArmor, accessories
ITEM.ITEM_INGREDIENTCrafting materialsRaw materials, components
ITEM.ITEM_ATTACHMENTWeapon/item attachmentsScopes, filters
ITEM.ITEM_LOOTGeneric loot itemsMoney, valuables
ITEM.ITEM_DEPLOYABLEItems that spawn entitiesStructures, machines
ITEM.ITEM_DEPRECATEDDeprecated itemsLegacy items

Equipment Slots

For equipment items (ITEM.ITEM_EQUIPMENT), specify the equipment slot:

SlotDescription
ITEM.EQUIPMENT_PRIMARYPrimary weapon slot
ITEM.EQUIPMENT_SECONDARYSecondary weapon slot
ITEM.EQUIPMENT_ARMORArmor slot
ITEM.EQUIPMENT_MISCMiscellaneous equipment
ITEM.Category = ITEM.ITEM_EQUIPMENT
ITEM.EquipSlot = ITEM.EQUIPMENT_ARMOR

Common Properties

Stack and Storage

ITEM.Stackable = true              -- Can items stack together
ITEM.MaxStack = 32 -- Maximum stack size
ITEM.Worth = 250 -- Base value in dollars
ITEM.Store = 5 -- Store ID where sold (-1 = not sold)

Behavior Flags

ITEM.DropOnDeath = true            -- Drop when player dies
ITEM.DeleteOnJobChange = true -- Delete when changing jobs
ITEM.CanConfiscate = true -- Police can confiscate
ITEM.NoMarket = true -- Cannot be sold on player market
ITEM.IgnoreStore = true -- Ignore store calculations
ITEM.NoDurability = true -- Item doesn't degrade
ITEM.PropertyOnly = false -- Can only be used in properties

Level Requirements

ITEM.Level = 25                    -- Required player level

Action Properties

-- Static action time
ITEM.ActionLength = 5

-- Dynamic action time based on conditions
ITEM.ActionLength = function( pPlayer )
local bInCombat = pPlayer:InCombat()
return bInCombat and 10 or 5
end

-- Action configuration
ITEM.ActionExtraTable = {
ActionTimeRemainingText = L( "cfg.item.action.using" ),
CancelOnAnyMovement = true
}

Server-Side Functions

OnUse Function

Called when a player uses the item:

if SERVER then
function ITEM:OnUse( pPlayer, data )
-- Heal player
pPlayer:SetHealth( math.min( pPlayer:Health() + 50, pPlayer:GetMaxHealth() ) )
pPlayer:EmitSound( "items/medshot4.wav" )
end
end

CanUse Function

Determines if the item can be used:

if SERVER then
function ITEM:CanUse( pPlayer )
-- Check if player needs healing
local canUse = pPlayer:Health() < pPlayer:GetMaxHealth()
local errorMessage = L( "cfg.item.notif.healthFull" )

return canUse, errorMessage
end
end

Equipment Functions

For equippable items:

if SERVER then
function ITEM:OnEquip( pPlayer, data )
-- Apply equipment effects
pPlayer:EquipCosmeticStringID( "gasmask", true )
pPlayer:EmitSound( "items/equip.wav" )
end

function ITEM:OnUnequip( pPlayer, data )
-- Remove equipment effects
pPlayer:EquipCosmeticStringID( "gasmask", false )
end

function ITEM:OnDrop( pPlayer, data, ent )
-- Handle item being dropped
if pPlayer:IsEquippedCosmetic( "gasmask" ) then
pPlayer:EquipCosmeticStringID( "gasmask", false )
end
end
end

Armor System

For armor items, define damage reduction:

ITEM.HitGroupReduction = {
[ HITGROUP_HEAD ] = 0.4, -- 60% damage reduction
[ HITGROUP_CHEST ] = 0.6, -- 40% damage reduction
[ HITGROUP_STOMACH ] = 0.6, -- 40% damage reduction
[ HITGROUP_GEAR ] = 0.6 -- 40% damage reduction
}

Lower values = more protection (0.0 = no damage, 1.0 = full damage)

Crafting System

For craftable items:

ITEM.CraftingCategory = L( "cfg.item.category.equipment" )
ITEM.CraftingSkill = "crafting" -- Required skill
ITEM.CraftingSkillLevel = 45 -- Required skill level
ITEM.CraftingRecipe = {
mat_plastic = 10, -- Required materials
mat_metal = 4,
mat_metal_steel = 2,
}

Containers

For container items:

ITEM.IsContainer = true

if SERVER then
function ITEM:OnUse( pPlayer, data )
local eEnt = ents.Create( "prop_physics" )
eEnt:SetModel( self.Model )
eEnt:SetPos( pPlayer:GetPos() + Vector( 50, 0, 20 ) )
eEnt:Spawn()
eEnt:Activate()

eEnt:SetNWBool( "IsContainer", true )
eEnt.Container = Monolith.InventoryContainers:CreateContainer(
eEnt,
L( "cfg.item.container_name" ),
10, -- Slot count
L( "cfg.item.container_desc" )
)

if data.extra.Container then
eEnt.Container = data.extra.Container
eEnt.Container:SetEntity( eEnt )
end

eEnt.Container:SetOwner( pPlayer )
end
end

Icon System

Customize item icons:

ITEM.IconCamData = {
pos = Vector(287.15, 242.00, 175.35),
ang = Angle(25, 220, 0),
entAng = Angle(60, 45, 0),
fov = 5.1,
}

Attachments

For items with attachments:

ITEM.Attachments = {
mat_gas_filter_job = true
}

-- For attachment items
ITEM.IsAttachment = true

Price Calculation

Dynamic pricing:

function ITEM:CalculatePrice( data )
local basePrice = 200
local finalPrice = data.count * basePrice
return finalPrice
end

Advanced Examples

Medical Item

ITEM = Monolith.InventoryItem( "medkit_advanced", L( "cfg.item.medkit_advanced" ), L( "cfg.item.desc.medkit_advanced" ) )
ITEM.Category = ITEM.ITEM_CONSUMABLE
ITEM.Model = "models/w_models/weapons/w_eq_medkit.mdl"
ITEM.MaxStack = 5
ITEM.Worth = 2500
ITEM.Level = 40
ITEM.DropOnDeath = true
ITEM.Store = 11

ITEM.ActionLength = function( pPlayer )
return pPlayer:InCombat() and 15 or 8
end

ITEM.ActionExtraTable = {
ActionTimeRemainingText = L( "cfg.item.action.usingAdvancedMedkit" ),
CancelOnAnyMovement = true
}

if SERVER then
function ITEM:OnUse( pPlayer, data )
local healAmount = 75
local maxHealth = pPlayer:GetMaxHealth()
pPlayer:SetHealth( math.min( pPlayer:Health() + healAmount, maxHealth ) )
pPlayer:EmitSound( "items/medshot4.wav" )

-- Add temporary health boost
timer.Simple( 1, function()
if IsValid( pPlayer ) then
pPlayer:SetMaxHealth( maxHealth * 1.1 )
timer.Simple( 300, function() -- 5 minute boost
if IsValid( pPlayer ) then
pPlayer:SetMaxHealth( maxHealth )
end
end )
end
end )
end

function ITEM:CanUse( pPlayer )
local healthPercent = pPlayer:Health() / pPlayer:GetMaxHealth()
if healthPercent >= 0.9 then
return false, L( "cfg.item.notif.healthAlmostFull" )
end

if pPlayer:InCombat() then
return true -- Allowed but takes longer
end

return true
end
end
Monolith.InventoryItems:RegisterItem( ITEM )

Deployable Item

ITEM = Monolith.InventoryItem( "security_camera", L( "cfg.item.security_camera" ), L( "cfg.item.desc.security_camera" ) )
ITEM.Category = ITEM.ITEM_DEPLOYABLE
ITEM.Model = "models/props/cs_militia/security_camera.mdl"
ITEM.Worth = 5000
ITEM.Stackable = false
ITEM.Store = 8
ITEM.Level = 60
ITEM.PropertyOnly = true
ITEM.Class = "security_camera"

if SERVER then
function ITEM:OnUse( pPlayer, data )
local trace = pPlayer:GetEyeTrace()

local camera = ents.Create( "security_camera" )
camera:SetPos( trace.HitPos + trace.HitNormal * 10 )
camera:SetAngles( ( pPlayer:GetPos() - trace.HitPos ):Angle() )
camera:Spawn()
camera:Activate()
camera:SetOwner( pPlayer )

pPlayer:AddNotification( L( "cfg.item.notif.cameraDeployed" ) )
end

function ITEM:CanUse( pPlayer )
if not pPlayer:IsInProperty() then
return false, L( "cfg.item.notif.mustBeInProperty" )
end

return true
end
end
Monolith.InventoryItems:RegisterItem( ITEM )

Best Practices

1. Naming Conventions

  • Use descriptive, unique item IDs
  • Follow the existing localization pattern
  • Use consistent prefixes (e.g., consumable_, weapon_, tool_)

2. Balance Considerations

  • Set appropriate worth values
  • Consider level requirements for powerful items
  • Balance action times with item effectiveness

3. Performance

  • Use if SERVER then blocks for server-only functions
  • Avoid expensive operations in frequently called functions
  • Use timers appropriately for delayed effects

4. User Experience

  • Provide clear error messages in CanUse functions
  • Use appropriate sound effects
  • Give meaningful action text

5. Testing

  • Test items in different scenarios (combat, non-combat)
  • Verify stacking behavior
  • Test with different player levels
  • Ensure proper cleanup of entities/effects

Troubleshooting

Common Issues

  1. Item doesn't appear: Check the item ID is unique and registration is called
  2. Localization missing: Ensure language strings are defined
  3. Model not showing: Verify model path is correct and exists
  4. Functions not working: Check if SERVER then blocks are properly placed
  5. Stacking issues: Review Stackable and MaxStack properties

Debug Tips

  • Use print() statements to debug function calls
  • Check console for Lua errors
  • Test items in single-player first
  • Use developer console commands to spawn items directly

This guide covers the essential aspects of adding items to MonolithRP. For more complex functionality, examine existing items in the codebase and adapt their patterns to your needs.