Giving contents

List

Listing the contents with its type description

-- list all the contents with type description

local TYPES = {
    [0] = "Texture",
    [1] = "Sound",
    [3] = "Landmark",
    [5] = "Clothing",
    [6] = "Object",
    [7] = "Notecard",
    [10] = "Script",
    [13] = "Bodypart",
    [20] = "Animation",
    [21] = "Gesture",
    [56] = "Setting",
    [57] = "Material",
}

local function listInventory()
    for itemNumber = 1, ll.GetInventoryNumber(INVENTORY_ALL) do
        local itemName = ll.GetInventoryName(INVENTORY_ALL, itemNumber)
        local itemType = ll.GetInventoryType(itemName)
        local itemTypeDescription = TYPES[itemType]
        ll.OwnerSay(`{itemName} ({itemTypeDescription})`)
    end
end

LLEvents:on("touch_start", function(events)
    listInventory()
end)

--[[

list of constants for the inventory types:

    INVENTORY_ALL
    INVENTORY_TEXTURE
    INVENTORY_SOUND
    INVENTORY_LANDMARK
    INVENTORY_CLOTHING
    INVENTORY_OBJECT
    INVENTORY_NOTECARD
    INVENTORY_SCRIPT
    INVENTORY_BODYPART
    INVENTORY_ANIMATION
    INVENTORY_GESTURE

constant returned by ll.GetInventoryType(name) when name does not exist in the contents:

    INVENTORY_NONE

]]

Give

Giving the contents (except the scripts)

-- give all the contents except the scripts to the toucher

local function giveInventory(receiver)
    for itemNumber = 1, ll.GetInventoryNumber(INVENTORY_ALL) do
        local itemName = ll.GetInventoryName(INVENTORY_ALL, itemNumber)
        local itemType = ll.GetInventoryType(itemName)
        if itemType ~= INVENTORY_SCRIPT then
            ll.GiveInventory(receiver,itemName);
        end
    end
end

LLEvents:on("touch_start", function(events)
    for _, ev in events do
        local toucher = ev:getKey()
        giveInventory(toucher)
    end
end)

Give folder

Giving the contents in a folder

-- give all the contents except scripts in a folder

local function giveInventoryFolder(receiver)
    local items = {}
    local folderName = ll.GetObjectDesc()

    for itemNumber = 1, ll.GetInventoryNumber(INVENTORY_ALL) do
        local itemName = ll.GetInventoryName(INVENTORY_ALL, itemNumber)
        local itemType = ll.GetInventoryType(itemName)
        if itemType ~= INVENTORY_SCRIPT then
            table.insert(items, itemName)
        end
    end

    ll.GiveInventoryList(receiver, folderName, items)
end

LLEvents:on("touch_start", function(events)
    for _, ev in events do
        local toucher = ev:getKey()
        giveInventoryFolder(toucher)
    end
end)

Remove

Removing from the contents

-- remove an item from the contents

local function itemExists(name)
    return ll.GetInventoryType(name) ~= INVENTORY_NONE
end

local function removeInventory(name)
    if itemExists(name) then
        ll.RemoveInventory(name)
        ll.OwnerSay("The item '" .. name .. "' has been removed")
    else
        ll.OwnerSay("The item '" .. name .. "' does not exist")
    end
end

LLEvents:on("touch_start", function(events)
    removeInventory("Unuseful item")
end)

List one type

Listing the contents of one type

-- list contents of one type

local TYPES = {
    [0] = "Texture",
    [1] = "Sound",
    [3] = "Landmark",
    [5] = "Clothing",
    [6] = "Object",
    [7] = "Notecard",
    [10] = "Script",
    [13] = "Bodypart",
    [20] = "Animation",
    [21] = "Gesture",
    [56] = "Setting",
    [57] = "Material",
}

local TYPE_CODES = {
    texture = 0,
    sound = 1,
    landmark = 3,
    clothing = 5,
    object = 6,
    notecard = 7,
    script = 10,
    bodypart = 13,
    animation = 20,
    gesture = 21,
    setting = 56,
    material = 57,
}

local INVENTORY_CHANNEL = 5  -- channel to listen for the inventory type to list

local function listInventory()
    for itemNumber = 1, ll.GetInventoryNumber(INVENTORY_ALL) do
        local itemName = ll.GetInventoryName(INVENTORY_ALL, itemNumber)
        local itemType = ll.GetInventoryType(itemName)
        local itemTypeDescription = TYPES[itemType]
        ll.OwnerSay(`{itemName} ({itemTypeDescription})`)
    end
end

local function listInventoryByType(type)
    type = type:lower()
    local typeCode = if type == "all" then INVENTORY_ALL else TYPE_CODES[type]

    if typeCode then
        for itemNumber = 1, ll.GetInventoryNumber(typeCode) do
            local itemName = ll.GetInventoryName(typeCode, itemNumber)
            local itemType = ll.GetInventoryType(itemName)
            local itemTypeDescription = TYPES[itemType]
            ll.OwnerSay(`{itemName} ({itemTypeDescription})`)
        end
    else
        ll.OwnerSay(`The type name {type} doesn't exist`)
    end
end

LLEvents:on("touch_start", function(events)
    listInventory()
end)

LLEvents:on("listen", function(channel, name, id, message)
    if channel == INVENTORY_CHANNEL then
        listInventoryByType(message)
    end
end)

ll.Listen(INVENTORY_CHANNEL, "", ll.GetOwner(), "")

List types totals

Listing the total of contents of each type

-- list total contents by type (with hold-touch)

local TYPES = {
    [0] = "Texture",
    [1] = "Sound",
    [3] = "Landmark",
    [5] = "Clothing",
    [6] = "Object",
    [7] = "Notecard",
    [10] = "Script",
    [13] = "Bodypart",
    [20] = "Animation",
    [21] = "Gesture",
    [56] = "Setting",
    [57] = "Material",
}

-- to use touch and hold-touch
local isHoldTouch = false
local timeTouch = 0

local function listInventory()
    for itemNumber = 1, ll.GetInventoryNumber(INVENTORY_ALL) do
        local itemName = ll.GetInventoryName(INVENTORY_ALL, itemNumber)
        local itemType = ll.GetInventoryType(itemName)
        local itemTypeDescription = TYPES[itemType]
        ll.OwnerSay(`{itemName} ({itemTypeDescription})`)
    end
end

local function listInventoryTotals()
    local totals = {}

    for itemNumber = 1, ll.GetInventoryNumber(INVENTORY_ALL) do
        local itemName = ll.GetInventoryName(INVENTORY_ALL, itemNumber)
        local itemType = ll.GetInventoryType(itemName)
        totals[itemType] = (totals[itemType] or 0) + 1
    end

    for itemType, total in totals do
        ll.OwnerSay(`{total} {TYPES[itemType]}`)
    end
end

LLEvents:on("touch_start", function(events)
    timeTouch = ll.GetTime()
    isHoldTouch = false
end)

LLEvents:on("touch", function(events)
    -- hold-touch to list the totals
    if ll.GetTime() - timeTouch > 1 and not isHoldTouch then
        isHoldTouch = true
        listInventoryTotals()
    end
end)

LLEvents:on("touch_end", function(events)
    -- touch to list the details
    if not isHoldTouch then
        listInventory()
    end
end)

Find by type

Finding items by type

-- find items by types (optional variadic multi-return function)

function findItemsByType(...)
    local types = if select("#", ...) == 0 then { INVENTORY_ALL } else { ... }
    local found = {}
    for _, type in types do
        for itemNumber = 1, ll.GetInventoryNumber(type) do
            local itemName = ll.GetInventoryName(type, itemNumber)
            table.insert(found, itemName)
        end
    end
    return found, #found
end

-- find notecards and textures
local items, total = findItemsByType(INVENTORY_NOTECARD, INVENTORY_TEXTURE)
print(`{total} items: {table.concat(items, ", ")}`)

-- find all
local items, total = findItemsByType()
print(`{total} items: {table.concat(items, ", ")}`)

Give to visitors

Giving contents to the visitors the first time that they come

Use this script in an object, like a welcome mat, at the door

-- give contents to visitors, to be used in a collider object

local THIS_SCRIPT = ll.GetScriptName()

local givenPeople = {}

local function giveInventory(receiver)
    local items = {}
    local folderName = ll.GetObjectDesc()

    for itemNumber = 1, ll.GetInventoryNumber(INVENTORY_ALL) do
        local itemName = ll.GetInventoryName(INVENTORY_ALL, itemNumber)
        if itemName ~= THIS_SCRIPT then
            table.insert(items, itemName)
        end
    end

    ll.GiveInventoryList(receiver, folderName, items)
end

local function addGiven(personId)
    local index = table.find(givenPeople, personId)
    -- if the person is in the list, delete them to add again in the last position
    if index then
        table.remove(givenPeople, index)
    end
    table.insert(givenPeople, personId)  -- adding at the end of the list
    -- to avoid script memory issues we only keep the last 500 people
    if #givenPeople > 500 then
        table.remove(givenPeople, 1)
    end
end

LLEvents:on("touch_start", function(events)
    for _, ev in events do
        local toucher = ev:getKey()
        -- if they touch we always give, no matter if they are in the list of given people
        giveInventory(toucher)
        -- we add them to the list, to not give on collision
        addGiven(toucher)
    end
end)

LLEvents:on("collision_start", function(events)
    for _, ev in events do
        local toucher = ev:getKey()
        if not table.find(givenPeople, toucher) then
            -- we give if they are not in the list of given people
            giveInventory(toucher)
        end
        addGiven(toucher) 
    end
end)

LLEvents:on("on_rez", function(start_param)
    ll.ResetScript()
end)

LLEvents:on("changed", function(change)
    if bit32.btest(change, bit32.bor(CHANGED_OWNER, CHANGED_INVENTORY)) then
        ll.ResetScript()  -- to empty the list of given people when we change the contents
    end
end)

ll.VolumeDetect(true)  -- make the object a collision detector

Give contents HUD

Giving the contents placed in the object to the nearby people

Use this script in a HUD

-- HUD to give contents to nearby people

local GIVING_RANGE = 40  -- max distance to give

local ME = ll.GetOwner()
local THIS_SCRIPT = ll.GetScriptName()
local PERMS_COPY_TRANSFER = bit32.bor(PERM_COPY, PERM_TRANSFER)

local WHITE = vector(1, 1, 1)
local RED = vector(1, 0, 0)
local GREEN = vector(0, 0.5, 0)

local folderName = ""

local function getFolderName()
    -- folder name is the name of the first item
    local itemName = ll.GetInventoryName(INVENTORY_ALL, 1)

    if itemName == THIS_SCRIPT then
        -- if the first item happens to be this script use the second item
        itemName = ll.GetInventoryName(INVENTORY_ALL, 2)
    end

    return itemName
end

local function checkInventoryPerms()
    local hasPerms = true
    local text = ""

    for itemNumber = 1, ll.GetInventoryNumber(INVENTORY_ALL) do
        local itemName = ll.GetInventoryName(INVENTORY_ALL, itemNumber)
        if itemName ~= THIS_SCRIPT then
            local itemOwnerPerms = ll.GetInventoryPermMask(itemName, MASK_OWNER)
            if bit32.band(itemOwnerPerms, PERMS_COPY_TRANSFER) ~= PERMS_COPY_TRANSFER then
                ll.OwnerSay(`{itemName} is not copy-transfer`)
                hasPerms = false
            end
            text ..= "\n" .. itemName
        end
    end

    ll.SetText(text, WHITE, 1)
    return hasPerms
end

local function checkContents()
    local totalItems = ll.GetInventoryNumber(INVENTORY_ALL)
    if totalItems == 1 then
        ll.SetColor(WHITE, ALL_SIDES)
        ll.OwnerSay("Empty, drop items")
        folderName = ""
        ll.SetText("", ZERO_VECTOR, 0)
    else
        if totalItems == 2 then
            folderName = getFolderName()
        end
        if checkInventoryPerms() then
            ll.SetColor(GREEN, ALL_SIDES)
            ll.OwnerSay(`Ready, {totalItems - 1} items, drop more or touch to give`)
        else
            ll.SetColor(RED, ALL_SIDES)
            ll.OwnerSay("Error, check properties")
        end
    end
end

local function getInventory()
    local items = {}

    for itemNumber = 1, ll.GetInventoryNumber(INVENTORY_ALL) do
        local itemName = ll.GetInventoryName(INVENTORY_ALL, itemNumber)
        if itemName ~= THIS_SCRIPT then
            table.insert(items, itemName)
        end
    end

    return items
end

local function removeInventory()
    for itemNumber = ll.GetInventoryNumber(INVENTORY_ALL), 1, -1 do
        local itemName = ll.GetInventoryName(INVENTORY_ALL, itemNumber)
        if itemName ~= THIS_SCRIPT then
            ll.RemoveInventory(itemName)
        end
    end
end

local function giveInventory()
    local items = getInventory()
    local people = ll.GetAgentList(AGENT_LIST_PARCEL, {})
    local myPos = ll.GetPos()
    local peopleSent = 0

    ll.OwnerSay("Sending...")
    for _, personId in people do
        if personId ~= ME then
            local personPos = ll.GetObjectDetails(personId, {OBJECT_POS})[1]
            local personDistance = ll.VecDist(myPos, personPos)
            if personDistance < GIVING_RANGE then
                ll.GiveInventoryList(personId, folderName, items)
                peopleSent += 1
                ll.OwnerSay("Given to " .. ll.GetDisplayName(personId))
            end
        end
    end

    removeInventory()
    ll.OwnerSay(`{#items} items given to {peopleSent} people`)
    checkContents()
end

local function initialize()
    local objectName = ll.GetDisplayName(ME) .. "'s contents giver"
    ll.SetObjectName(objectName)
    if ll.GetObjectName() ~= objectName then
        -- name has unicode characters, changed to "?" in the object name
        ll.SetObjectName(ll.GetUsername(ME) .. "'s contents giver")
    end
    checkContents()
end

LLEvents:on("touch_start", function(events)
    if ll.GetColor(ALL_SIDES) == GREEN then
        giveInventory()
    else
        ll.OwnerSay("Not ready, drop items")
    end
end)

LLEvents:on("changed", function(change)
    if bit32.btest(change, CHANGED_INVENTORY) then
        checkContents()
    end
    if bit32.btest(change, CHANGED_OWNER) then
        ll.ResetScript()
    end
end)

LLEvents:on("on_rez", function(start_param)
    ll.ResetScript()
end)

initialize()

Give a random item

Giving a random item to the visitors only after some time

Useful to encourage visitors to come regularly to get all the items

-- random giver with wait time

local HOURS = 3  -- hours between gives, HOURS=0 one give daily (SL time)

local THIS_SCRIPT = ll.GetScriptName()

local setMidnightTimer

local function sendGift(toucher)
    local items = {}

    for itemNumber = 1, ll.GetInventoryNumber(INVENTORY_ALL) do
        local itemName = ll.GetInventoryName(INVENTORY_ALL, itemNumber)
        if itemName ~= THIS_SCRIPT then
            table.insert(items, itemName)
        end
    end

    local randomItem = math.random(1, #items)
    local wait = if HOURS == 0 then "tomorrow (SL time)" else "in " .. tostring(HOURS) .. " hours"
    ll.RegionSayTo(toucher, 0, "Your gift is on the way, you can get a gift again " .. wait)
    ll.GiveInventory(toucher, items[randomItem])
end

local function newDay()
    if HOURS ~= 0 then
        local keys = ll.LinksetDataListKeys(1, 0)
        for _, key in keys do
            local lastGift = tonumber(ll.LinksetDataRead(key))
            if 86400 - lastGift < HOURS * 3600 then
                ll.LinksetDataWrite(key, tostring(lastGift - 86400))
            else
                ll.LinksetDataDelete(key)
            end
        end
    else
        ll.LinksetDataReset()
    end
    setMidnightTimer()
end

local function giftReady(toucher)
    local ok = true
    local time = ll.GetWallclock()
    if time < (tonumber(ll.LinksetDataRead(" ")) or 0) then
        newDay()
    end
    local lastGift = tonumber(ll.LinksetDataRead(tostring(toucher)))
    if lastGift then
        if HOURS == 0 then
            ok = false
            ll.RegionSayTo(toucher, 0, "Your next gift will be ready tomorrow (in SL time)")
        elseif (time - lastGift) / 3600 < HOURS then
            ok = false
            local timeWait = HOURS - ((time - lastGift) / 3600)
            local hours = math.floor(timeWait)
            local minutes = math.ceil((timeWait - hours) * 60)
            ll.RegionSayTo(toucher, 0, "Your next gift will be ready in " .. string.format("%d:%02d", hours, minutes) .. " hours")
        end
    end
    if ok then
        ll.LinksetDataWrite(tostring(toucher), tostring(time))
    end
    setMidnightTimer()
    return ok
end

function setMidnightTimer()
    local time = ll.GetWallclock()
    ll.LinksetDataWrite(" ", tostring(time))
    LLTimers:off(newDay)
    LLTimers:once(86400 - time + 1, newDay)
end

LLEvents:on("touch_start", function(events)
    for _, ev in events do
        local toucher = ev:getKey()
        if giftReady(toucher) then
            sendGift(toucher)
        end
    end
end)

math.randomseed(os.time)
setMidnightTimer()

Unpacker

Unpacking items into a folder, with options for contact and information

The script is configured for this image:

Unpacker image
Download here an unnamed image to use adding your name
-- unpacker (by Suzanna Linn, 2025-12-05)


-- replace this section with your data

local OPTIONS = { "INFO", "CONTACT", "UNPACK" }

-- circles on top must come before circles underneath
local CIRCLES = { vector(0.253, 0.780, 0.146), vector(0.747, 0.780, 0.146), vector(0.485, 0.485, 0.342) }

local TEXTURE_UNPACK = uuid("c4125f85-ad79-c446-8dd0-85c2a733529a")  -- your texture for the unpacker HUD
local NOTECARD_INFO = "Hi, I'm Suzanna, nice to meet you :)"         -- name of your notecard with info (in the object contents)
local CONTACT_UUID = uuid("0f16c0e1-384e-4b5f-b7ce-886dda3bce41")    -- your uuid

-- end of replace section



local function findOption(touchPos)
    local option

    for index, circle in CIRCLES do
        local radius = circle.z
        circle = vector(circle.x, circle.y, 0)
        if ll.VecDist(circle, touchPos) <= radius then
            option = OPTIONS[index]
            break
        end
    end

    return option
end

local function unpackContents(toucher)
    local items = {}
    local folderName = (ll.GetObjectName():split(" ("))[1]

    for itemNumber = 1, ll.GetInventoryNumber(INVENTORY_ALL) do
        local itemName = ll.GetInventoryName(INVENTORY_ALL, itemNumber)
        if itemName ~= ll.GetScriptName() and itemName ~= NOTECARD_INFO then
            table.insert(items, itemName)
        end
    end

    if #items > 0 then
        ll.RegionSayTo(toucher, 0, "Unpacking to your folder " .. folderName .. "...")
        ll.GiveInventoryList(toucher, folderName, items)
    else
        ll.RegionSayTo(toucher, 0, "Sorry, there aren't items to unpack")
    end

    ll.RequestPermissions(toucher, PERMISSION_ATTACH)
end

local function unpackOrInfo(toucher, touchPos)
    local option = findOption(touchPos)
    if option == "UNPACK" then
        unpackContents(toucher)
    elseif option == "INFO" then
        ll.GiveInventory(toucher, NOTECARD_INFO)
    elseif option == "CONTACT" then
        ll.RegionSayTo(toucher, 0, "secondlife:///app/agent/" .. tostring(CONTACT_UUID) .. "/about")
    end
end

LLEvents:on("touch_start", function(events)
    for _, ev in events do
        local toucher = ev:getKey()
        local touchPos = ev:getTouchST()
        touchPos = vector(touchPos.x, 1 - touchPos.y, touchPos.z)
        unpackOrInfo(toucher, touchPos)
    end
end)

LLEvents:on("run_time_permissions", function(perm)
    if bit32.btest(perm, PERMISSION_ATTACH) then
        ll.DetachFromAvatar()
    end
end)

ll.SetTexture(TEXTURE_UNPACK, ALL_SIDES)