• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

[TFS 1.3] Small Autoloot

Sarah Wesker

ƐƖєgαηт Sуηтαx ❤
Staff member
TFS Developer
Support Team
Joined
Mar 16, 2017
Messages
1,407
Solutions
154
Reaction score
1,958
Location
London
GitHub
MillhioreBT
Twitch
millhiorebt
Hello, here is a small, totally local autoloot system that is easy to configure ;)

Required version: TFS 1.5 last version
*** If you are using an alternate version such as: TFS-1.5-Downgrades, maybe you should merge these changes: A new way to manage player storages.

data/scripts/small_autoloot.lua
Here I will leave the version with money to the bank:
These small changes are required: A new way to manage player storages
Lua:
local autoloot = {
    talkaction = "!autoloottest",
    storageBase = 50000,
    freeAccountLimit = 10,
    premiumAccountLimit = 20,
    currencyToBank = true
}

local currencyItems = {}
if autoloot.currencyToBank then
    for index, item in pairs(Game.getCurrencyItems()) do
        currencyItems[item:getId()] = true
    end
end

local autolootCache = {}
local textEditRequests = {}

local function getPlayerLimit(player)
    return player:isPremium() and autoloot.premiumAccountLimit or autoloot.freeAccountLimit
end

local function getPlayerAutolootItems(player)
    local limits = getPlayerLimit(player)
    local guid = player:getGuid()
    local itemsCache = autolootCache[guid]
    if itemsCache then
        if #itemsCache > limits then
            local newChache = {unpack(itemsCache, 1, limits)}
            autolootCache[guid] = newChache
            return newChache
        end
        return itemsCache
    end

    local items = {}
    for i = 1, limits do
        local itemType = ItemType(math.max(player.storage[autoloot.storageBase + i], 0))
        if itemType and itemType:getId() ~= 0 then
            items[#items +1] = itemType:getId()
        end
    end

    autolootCache[guid] = items
    return items
end

local function setPlayerAutolootItems(player, newItems)
    local items = getPlayerAutolootItems(player)
    for i = getPlayerLimit(player), 1, -1 do
        local itemId = newItems[i]
        if itemId then
            player.storage[autoloot.storageBase + i] = itemId
            items[i] = itemId
        else
            player.storage[autoloot.storageBase + i] = -1
            table.remove(items, i)
        end
    end
    return true
end

local function addPlayerAutolootItem(player, itemId)
    local items = getPlayerAutolootItems(player)
    for _, id in pairs(items) do
        if itemId == id then
            return false
        end
    end
    items[#items +1] = itemId
    return setPlayerAutolootItems(player, items)
end

local function removePlayerAutolootItem(player, itemId)
    local items = getPlayerAutolootItems(player)
    for i, id in pairs(items) do
        if itemId == id then
            table.remove(items, i)
            return setPlayerAutolootItems(player, items)
        end
    end
    return false
end

local function hasPlayerAutolootItem(player, itemId)
    for _, id in pairs(getPlayerAutolootItems(player)) do
        if itemId == id then
            return true
        end
    end
    return false
end

local ec = EventCallback

function ec.onDropLoot(monster, corpse)
    if not corpse:getType():isContainer() then
        return
    end

    local corpseOwner = Player(corpse:getCorpseOwner())
    local items = corpse:getItems()
    local warningCapacity = false
    for _, item in pairs(items) do
        local itemId = item:getId()
        if hasPlayerAutolootItem(corpseOwner, itemId) then
            if currencyItems[itemId] then
                local worth = item:getWorth()
                corpseOwner:setBankBalance(corpseOwner:getBankBalance() + worth)
                corpseOwner:sendTextMessage(MESSAGE_STATUS_SMALL, string.format("Your balance increases by %d gold coins.", worth))
                item:remove()
            elseif not item:moveTo(corpseOwner, 0) then
                warningCapacity = true
            end
        end
    end

    if warningCapacity then
        corpseOwner:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You no have capacity.")
    end
end

ec:register(3)

local talkAction = TalkAction(autoloot.talkaction)

function talkAction.onSay(player, words, param, type)
    local split = param:splitTrimmed(",")
    local action = split[1]
    if not action then
        player:showTextDialog(2160, string.format("Examples of use:\n%s add,gold coin\n%s remove,gold coin\n%s clear\n%s show\n%s edit\n\n~Available slots~\nfreeAccount: %d\npremiumAccount: %d\ncurrency to bank: %s", words, words, words, words, words, autoloot.freeAccountLimit, autoloot.premiumAccountLimit, autoloot.currencyToBank and "yes" or "no"), false)
        return false
    end

    if action == "clear" then
        setPlayerAutolootItems(player, {})
        player:sendCancelMessage("Autoloot list cleaned.")
        return false
    elseif action == "show" then
        local items = getPlayerAutolootItems(player)
        local description = {string.format('~ Your autoloot list, capacity: %d/%d ~\n', #items, getPlayerLimit(player))}
        for i, itemId in pairs(items) do
            description[#description +1] = string.format("%d) %s", i, ItemType(itemId):getName())
        end
        player:showTextDialog(2160, table.concat(description, '\n'), false)
        return false
    elseif action == "edit" then
        local items = getPlayerAutolootItems(player)
        if #items == 0 then
            -- Example
            items = {2160,2672,2432}
        end
        local description = {}
        for i, itemId in pairs(items) do
            description[#description +1] = ItemType(itemId):getName()
        end
        player:registerEvent("autolootTextEdit")
        player:showTextDialog(1948, string.format("To add articles you just have to write their IDs or names on each line\nfor example:\n\n%s", table.concat(description, '\n')), true, 666)
        textEditRequests[player:getGuid()] = true
        return false
    end

    local function getItemType()
        local itemType = ItemType(split[2])
        if not itemType or itemType:getId() == 0 then
            itemType = ItemType(math.max(tonumber(split[2]) or 0), 0)
            if not itemType or itemType:getId() == 0 then
                player:sendCancelMessage(string.format("The item %s does not exists!", split[2]))
                return false
            end
        end
        return itemType
    end

    if action == "add" then
        local itemType = getItemType()
        if itemType then
            local limits = getPlayerLimit(player)
            if #getPlayerAutolootItems(player) >= limits then
                player:sendCancelMessage(string.format("Your auto loot only allows you to add %d items.", limits))
                return false
            end

            if addPlayerAutolootItem(player, itemType:getId()) then
                player:sendCancelMessage(string.format("Perfect you have added to the list: %s", itemType:getName()))
            else
                player:sendCancelMessage(string.format("The item %s already exists!", itemType:getName()))
            end
        end
        return false
    elseif action == "remove" then
        local itemType = getItemType()
        if itemType then
            if removePlayerAutolootItem(player, itemType:getId()) then
                player:sendCancelMessage(string.format("Perfect you have removed to the list the article: %s", itemType:getName()))
            else
                player:sendCancelMessage(string.format("The item %s does not exists in the list.", itemType:getName()))
            end
        end
        return false
    end

    return false
end

talkAction:separator(" ")
talkAction:register()

local creatureEvent = CreatureEvent("autolootCleanCache")

function creatureEvent.onLogout(player)
    setPlayerAutolootItems(player, getPlayerAutolootItems(player))
    autolootCache[player:getGuid()] = nil
    return true
end

creatureEvent:register()

creatureEvent = CreatureEvent("autolootTextEdit")

function creatureEvent.onTextEdit(player, item, text)
    player:unregisterEvent("autolootTextEdit")

    local split = text:splitTrimmed("\n")
    local items = {}
    for index, name in pairs(split) do repeat
        local itemType = ItemType(name)
        if not itemType or itemType:getId() == 0 then
            itemType = ItemType(tonumber(name))
            if not itemType or itemType:getId() == 0 then
                break
            end

            break
        end

        items[#items +1] = itemType:getId()
    until true end
    setPlayerAutolootItems(player, items)
    player:sendCancelMessage(string.format("Perfect, you have modified the list of articles manually."))
    return true
end

creatureEvent:register()

Note: Fixed the issues I was having clear and added a new command: !autoloot edit this works for manually editing the item list
1645745757189.png
 
Last edited:
I really like how simple and effective it is! How easy is it possible to change the looting to when the body is opened instead of when it's killed?
Don't want to make things to easy. ;)
 
Last edited:
Lua Script Error: [Scripts Interface]
C:\Users\adria\Desktop\COMPILE TEST\otserv\data\scripts\small_autoloot.lua
...ktop\COMPILE TEST\otserv\data\scripts\small_autoloot.lua:79: attempt to index local 'ec' (a nil value)
stack traceback:
[C]: in function '__newindex'
...ktop\COMPILE TEST\otserv\data\scripts\small_autoloot.lua:79: in main chunk
small_autoloot.lua [error]

Got this error :(
 
Lua Script Error: [Scripts Interface]
C:\Users\adria\Desktop\COMPILE TEST\otserv\data\scripts\small_autoloot.lua
...ktop\COMPILE TEST\otserv\data\scripts\small_autoloot.lua:79: attempt to index local 'ec' (a nil value)
stack traceback:
[C]: in function '__newindex'
...ktop\COMPILE TEST\otserv\data\scripts\small_autoloot.lua:79: in main chunk


Got this error :(
your server doesn't have EventCallback support, this script is compatible only with latest tfs
 
@Sarah Wesker: How would i go about adding something like "You autolooted: 30 gold, rope" to my loot channel?

Lua:
local text = ("Loot of %s: %s"):format(mType:getNameDescription(), corpse:getContentDescription())
local type = TALKTYPE_CHANNEL_O
local player = Player(corpse:getCorpseOwner())
player:sendChannelMessage(player, text, type, 31)

This is the whole script
Code:
-- Loot channel
local STORAGEVALUE_LOOT = 8914
function Monster:onDropLoot(corpse)

    if configManager.getNumber(configKeys.RATE_LOOT) == 0 then
        return
    end
    local type = TALKTYPE_CHANNEL_O
    local player = Player(corpse:getCorpseOwner())
    local mType = self:getType()
    if not player or player:getStamina() > 840 then
        local monsterLoot = mType:getLoot()

        for i = 1, #monsterLoot do
            local item = corpse:createLootItem(monsterLoot[i])
            if not item then
                print('[Warning] DropLoot:', 'Could not add loot item to corpse.')
            end
        end

        if player then
            local text = ("Loot of %s: %s"):format(mType:getNameDescription(), corpse:getContentDescription())
            local party = player:getParty()
            if party then
                party:broadcastPartyLoot(text)
            else
                if player:getStorageValue(STORAGEVALUE_LOOT) == 1 then
                    player:sendChannelMessage(player, text, type, 31)
                else
                    player:sendTextMessage(MESSAGE_INFO_DESCR, text)
                end
            end
        end
    else
        local text = ("Loot of %s: nothing (you have no stamina)"):format(mType:getNameDescription())
        local party = player:getParty()
        if party then
            party:broadcastPartyLoot(text)
        else
            if player:getStorageValue(STORAGEVALUE_LOOT) == 1 then
                player:sendChannelMessage(player, text, type, 31)
            else
                player:sendTextMessage(MESSAGE_INFO_DESCR, text)
            end
        end
    end
end
 
Hey @Sarah Wesker.

I'm using the latest TFS 1.3 and my players told me, that !autoloot clear do not work (no feedback on console here).
Furthermore the system deletes an item, if you don't have free space in bp.
1631618620469.png

If I missed an new TFS 1.3 fix, I'm open up to implement it. Just tell me which one. :)
 
Hey @Sarah Wesker.

I'm using the latest TFS 1.3 and my players told me, that !autoloot clear do not work (no feedback on console here).
Furthermore the system deletes an item, if you don't have free space in bp.
View attachment 62081

If I missed an new TFS 1.3 fix, I'm open up to implement it. Just tell me which one. :)
For the clear function, I also found that out. I guess it's fixable with a quick loop.

For your error you see above is not about free space, but that there is prolly no owner towards the body.
I fixed it to add a check on that like this:
Lua:
function ec.onDropLoot(monster, corpse)
    if not corpse:getType():isContainer() then
        return
    end

    local corpseOwner = Player(corpse:getCorpseOwner())
    if not corpseOwner then
        return
    end   
    local items = corpse:getItems()
    for _, item in pairs(items) do
        if hasPlayerAutolootItem(corpseOwner, item:getId()) then
            if not item:moveTo(corpseOwner) then
                corpseOwner:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You no have capacity.")
                break
            end
        end
    end
end
 
For the clear function, I also found that out. I guess it's fixable with a quick loop.

For your error you see above is not about free space, but that there is prolly no owner towards the body.
I fixed it to add a check on that like this:
Lua:
function ec.onDropLoot(monster, corpse)
    if not corpse:getType():isContainer() then
        return
    end

    local corpseOwner = Player(corpse:getCorpseOwner())
    if not corpseOwner then
        return
    end  
    local items = corpse:getItems()
    for _, item in pairs(items) do
        if hasPlayerAutolootItem(corpseOwner, item:getId()) then
            if not item:moveTo(corpseOwner) then
                corpseOwner:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You no have capacity.")
                break
            end
        end
    end
end
Not working (/reload scripts or do I need to restart server?).
 
Someone has this error too? Strangely enough, not every player triggers the error. Just a handful, then repetitive.1632218982649.png
Edit: After a test I must say, that there is no loot dropped anymore. So it's not working with the latest TFS from github right now (cuz I'm using it). :-/
Edit 2: Nvm.. It's an global TFS problem right now.
 
Last edited:
What does the param number mean?
When you register an EventCallback you can define a priority, so that the one with the highest priority will be executed first before the one with the lowest priority.
in this case I have added priority 3, just to make sure this is the first event executed, this way the default onDropLoot event will work correctly with the corpse modified by this small autoloot

There is no EventCallback documentation on the Wiki yet, I will try to write a documentation explaining exactly how this works, although it is actually quite easy
 
I implemented it to use in otservbr but gives this error
[2021-06-10 23:42:55.445] [error] Lua script error: data/events/scripts/monster.lua:Monster@onDropLoot
[2021-06-10 23:42:55.445] [error] data/events/scripts/monster.lua:13: attempt to call method 'getItems' (a nil value)
stack traceback:
[C]: in function 'getItems'
data/events/scripts/monster.lua:13: in function <data/events/scripts/monster.lua:1>
 
Hi @Sarah Wesker
I'm getting this error:
data\scripts\small_autoloot.lua:85: attempt to call method 'getItems' (a nil value)
stack traceback:
[C]: in function 'getItems'

I don't know if you are still supporting this autoloot, but could you help me?
 
this script doesn't work on otservbr global :D
Just added some functions from tfs, now works xD
thanks for the answer!
and thanks Sarah for this simple autoloot code, works pretty well, I'll just ask one more thing, is it possible to put to specify a loot backpack?
 
Last edited:
Just added some functions from tfs, now works xD
thanks for the answer!
and thanks Sarah for this simple autoloot code, works pretty well, I'll just ask one more thing, is it possible to put to specify a loot backpack?
Hello bro, i'm using otservbr too. Hmm can u help to make it works ?
 
check the main post again and at the end you will see a version with money to bank
the auto deposit works perfect.

I was going crazy, trying the new script with !autoloot, and nothing happened, there were no errors in the console lol
and then I saw that the command was !autoloottest 😂😂😂😂😂

by the way, we never reported to you that the !autoloot clear command doesn't work, It only works !autoloot remove.

How can I activate the currencyToBank at first login, What storage should I assign to the player or the account?

thanks for your scripts and contributions, they are very helpful.
 
Back
Top