• 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!

My Autoloot System for TFS 0.4

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
I recognize that there are already some systems that deal with the same, but this had been saved for some time and I want to share it with you now more than ever, as I plan to resume my stay as a scriptwriter, so this will be a good start. thanks!

go to their folder "mods" and create a new file "xml" with the name you want and paste the following code:
Code:
<?xml version="1.0" encoding="ISO-8859-1"?> 
<mod name="Autoloot Revolution BT" version="1.0" author="Millhiore BT" contact="none.com" enabled="yes">
<config name="AutolootRevolutionBT">
<![CDATA[
---@ You configurations:
autolootConfig = {}
autolootConfig.storageItems = 27000
autolootConfig.blockMonsters = {}
autolootConfig.goldToBank = true
autolootConfig.goldBankAnimation = true
autolootConfig.distanceLooting = 7
autolootConfig.freeSlots = 3
autolootConfig.premiumSlots = 6
autolootConfig.vipStorage = {0,0}
autolootConfig.playerLevel = 8
autolootConfig.corpseEffect = CONST_ME_NONE
autolootConfig.golds = {2148, 2152, 2160}
autolootConfig.tittle = "Welcomen to Autoloot System\nCreate by Millhiore BT\n\nOptions: help, clear, add, remove\n"
autolootConfig.notHaveCapDrop = false
---@ getPlayerPremiumEnabled(cid)
--# Return boolean
--# Create by Millhiore BT
function getPlayerPremiumEnabled(cid)
    if getConfigValue("freePremium") then
        return true
    end
    return getPlayerPremiumDays(cid) > 0
end
---@ getPlayerAutolootVip(cid)
--# Return boolean
--# Create by Millhiore BT
function getPlayerAutolootVip(cid)
    if autolootConfig.vipStorage[1] > 0 then
        if autolootConfig.vipStorage[2] > 0 then
            return getPlayerStorageValue(cid, autolootConfig.vipStorage[1]) == autolootConfig.vipStorage[2]
        else
            return getPlayerStorageValue(cid, autolootConfig.vipStorage[1]) >= os.time()
        end
    end
    return true
end
---@ getPositionDistance(position, positionEx)
--# Return number
--# Create by Millhiore BT
function getPositionDistance(position, positionEx)
    return math.max(math.max(math.abs(position.x - positionEx.x), math.abs(position.y - positionEx.y)), math.abs(position.z - positionEx.z))
end
---@ getPositionCorpse(pos [, itemid])
--# Return thing/nil
--# Create by Millhiore BT
function getPositionCorpse(pos, itemid)
    local tile = getTileInfo(pos)
    for index = 0, tile.things do
        pos.stackpos = index
        local corpse = getThingFromPos(pos, false)
        if corpse.itemid > 1 and isCorpse(corpse.uid) then
            local itemid = itemid or corpse.itemid
            if corpse.itemid == itemid then
                return corpse
            end
        end
    end
end
---@ getContainerItems(uid [, array])
--# Return array
--# Create by Millhiore BT
function getContainerItems(container, array, haveCap)
    array = array or {}
    haveCap = haveCap or false
    if not isContainer(container.uid) or getContainerSize(container.uid) == 0 then
        array[#array +1] = container
    else
        local size = getContainerSize(container.uid)
        haveCap = (getContainerCap(container.uid) -size) > 0
        for slot = 0, (size -1) do
            local item = getContainerItem(container.uid, slot)
            if item.itemid > 1 then
                getContainerItems(item, array, haveCap)
            end
        end
    end
    return #array >= 1 and array, haveCap
end
---@ getContainerItemsById(array/uid, itemid)
--# Return array
--# Create by Millhiore BT
function getContainerItemsById(container, itemid)
    local founds = {}
    local items = not container.uid and container or getContainerItems(container)
    for index, item in pairs(items) do
        if item.itemid == itemid then
            founds[#founds +1] = item
        end
    end
    return #founds >= 1 and founds
end
---@ doPlayerAddItemStackable(cid, itemid, count)
--# Return boolean
--# Create by Millhiore BT
function doPlayerAddItemStackable(cid, itemid, count)
    local container = getPlayerSlotItem(cid, CONST_SLOT_BACKPACK)
    if container.itemid > 1 then
        local items = getContainerItemsById(container, itemid)
        if not items then
            return doPlayerAddItem(cid, itemid, count)
        else
            local piles = #items
            for index, item in pairs(items) do
                if item.type < 100 then
                    local sum = item.type + count
                    local result = doTransformItem(item.uid, itemid, sum)
                    if sum <= 100 then
                        return result
                    else
                        return doPlayerAddItem(cid, itemid, sum - 100)
                    end
                else
                    piles = piles - 1
                    if piles == 0 then
                        return doPlayerAddItem(cid, itemid, count)
                    end
                end
            end
        end
    end
    return false
end
---@ getContainerMoneys(items)
--# Return array/false
--# Create by Millhiore BT
function getContainerMoneys(items)
    local moneys = {}
    for slot, item in pairs(items) do
        if isInArray(autolootConfig.golds, item.itemid) then
            moneys[#moneys +1] = item
        end
    end
    return #moneys > 0 and moneys
end
---@ doCorpseAutoloot(cid, pos itemid)
--# Return boolean/nil
--# Create by Millhiore BT
function doCorpseAutoloot(cid, pos, itemid, moneys)
    if not getPlayerAutolootVip(cid) or getPositionDistance(getThingPosition(cid), pos) > autolootConfig.distanceLooting
    or getPlayerLevel(cid) < autolootConfig.playerLevel then
        return nil
    end
    local corpse = getPositionCorpse(pos, itemid)
    if corpse and isContainer(corpse.uid) then
        local items, haveCap = moneys or getContainerItems(corpse)
        local sendEffect = false
        if items then
            for slot, item in pairs(items) do
                if isInArray(getPlayerAutolootItems(cid), item.itemid) then
                    sendEffect = true
                    local removeIt = false
                    local goldToBank = autolootConfig.goldToBank and isInArray(autolootConfig.golds, item.itemid)
                    if not goldToBank then
                        if not haveCap and autolootConfig.notHaveCapDrop then
                            return doCorpseAutoloot(cid, pos, itemid, getContainerMoneys(items))
                        end
                        if isItemStackable(item.itemid) then
                            removeIt = doPlayerAddItemStackable(cid, item.itemid, item.type)
                        else
                            removeIt = doPlayerAddItem(cid, item.itemid)
                        end
                    else
                        local add = (getItemInfo(item.itemid).worth * item.type)
                        removeIt = doPlayerSetBalance(cid, getPlayerBalance(cid) + add)
                        if autolootConfig.goldBankAnimation then
                            doSendAnimatedText(pos, string.format("+%u gps", add), TEXTCOLOR_YELLOW, cid)
                        end
                    end
                    if removeIt then
                        doRemoveItem(item.uid)
                    end
                end
            end
            if sendEffect then
                doSendMagicEffect(pos, autolootConfig.corpseEffect, cid)
            end
            return true
        end
    end
end
---@ getPlayerAutolootItems(cid)
--# Return array
--# Create by Millhiore BT
function getPlayerAutolootItems(cid)
    local array = {}
    local content = tostring(getPlayerStorageValue(cid, autolootConfig.storageItems))
    for itemid in string.gmatch(content, "(%d+):") do
        array[#array +1] = tonumber(itemid)
    end
    return array
end
---@ getPlayerAutolootItem(cid, itemid)
--# Return number/nil
--# Create by Millhiore BT
function getPlayerAutolootItem(cid, itemid)
    local autolootItems = getPlayerAutolootItems(cid)
    for index, id in pairs(autolootItems) do
        if itemid == id then
            return index
        end
    end
end
---@ setPlayerAutolootItems(cid [, array])
--# Return boolean
--# Create by Millhiore BT
function setPlayerAutolootItems(cid, array)
    local array = array or {}
    local content = "&"
    for index, itemid in pairs(array) do
        content = string.format("%s%u:", content, itemid)
    end
    return setPlayerStorageValue(cid, autolootConfig.storageItems, content)
end
---@ addPlayerAutolootItem(cid, itemid)
--# Return boolean
--# Create by Millhiore BT
function addPlayerAutolootItem(cid, itemid)
    local autolootItems = getPlayerAutolootItems(cid)
    local found = getPlayerAutolootItem(cid, itemid)
    if not found then
        autolootItems[#autolootItems +1] = itemid
        return setPlayerAutolootItems(cid, autolootItems)
    end
end
---@ removePlayerAutolootItem(cid, itemid)
--# Return boolean/nil
--# Create by Millhiore BT
function removePlayerAutolootItem(cid, itemid)
    local autolootItems = getPlayerAutolootItems(cid)
    local found = getPlayerAutolootItem(cid, itemid)
    if found then
        table.remove(autolootItems, found)
        return setPlayerAutolootItems(cid, autolootItems)
    end
end
---@ showPlayerAutoloot(cid)
--# Return boolean
--# Create by Millhiore BT
function showPlayerAutoloot(cid)
    local maxItems = getPlayerPremiumEnabled(cid) and autolootConfig.premiumSlots or autolootConfig.freeSlots
    local content = string.format("%sYou available slots: %u\n\n%s", autolootConfig.tittle, maxItems, "Your autoloot items:\n")
    local autolootItems = getPlayerAutolootItems(cid)
    for index, itemid in pairs(autolootItems) do
        local it = getItemInfo(itemid)
        content = string.format("%s %c %s\n", content, 155, it.name)
    end
    return doShowTextDialog(cid, 2529, content)
end
---@ showPlayerAutolootHelp(cid)
--# Return boolean
--# Create by Millhiore BT
function showPlayerAutolootHelp(cid)
    local content = autolootConfig.tittle .. "For you info:\n"
    for key, value in pairs(autolootConfig) do
        if not isInArray({"storageItems", "blockMonsters", "vipStorage", "golds", "tittle"}, key) then
            content = string.format("%s%c %s: %s\n", content, 155, tostring(key), tostring(value))
        end
    end
    return doShowTextDialog(cid, 2529, content)
end
]]>
</config>
<event type="login" name="AutolootRBTLogin" event="script">
<![CDATA[
function onLogin(cid)
    local maxItems = getPlayerPremiumEnabled(cid) and autolootConfig.premiumSlots or autolootConfig.freeSlots
    local autolootItems = getPlayerAutolootItems(cid)
    if #autolootItems > maxItems then
        for index = autolootConfig.premiumSlots, autolootConfig.freeSlots, -1 do
            table.remove(autolootItems, index)
        end
        setPlayerAutolootItems(cid, autolootItems)
        doPlayerSendCancel(cid, "Your list of items overflowed, you should check your list.")
    end
    registerCreatureEvent(cid, "AutolootRBTCombat")
    return true
end
]]>
</event>
<event type="death" name="AutolootRBTDeath" event="script">
<![CDATA[
domodlib("AutolootRevolutionBT")
function onDeath(cid, corpse, deathList)
    local killer = deathList[1]
    local position = getThingPosition(cid)
    if corpse.itemid > 1 then
        addEvent(doCorpseAutoloot, 100, killer, position, corpse.itemid)
    end
    return true
end
]]>
</event>
<event type="combat" name="AutolootRBTCombat" event="script">
<![CDATA[
if isPlayer(cid) and isMonster(target) then
    registerCreatureEvent(target, "AutolootRBTDeath")
end
return true
]]>
</event>
<talkaction words="!autoloot;/autoloot" event="buffer">
<![CDATA[
domodlib("AutolootRevolutionBT")
local split = string.explode(param, ",") or {}
local action = tostring(split[1]):lower()
local name = tostring(split[2])
local itemid = getItemIdByName(name, false)
local it = getItemInfo(itemid and itemid or 0)
local position = getThingPosition(cid)
if action == "add" then
    if not it then
        doPlayerSendCancel(cid, string.format("This item %s does not exist.", name))
    else
        local maxItems = getPlayerPremiumEnabled(cid) and autolootConfig.premiumSlots or autolootConfig.freeSlots
        local autolootItems = getPlayerAutolootItems(cid)
        if #autolootItems > maxItems then
            for index = autolootConfig.premiumSlots, autolootConfig.freeSlots, -1 do
                table.remove(autolootItems, index)
            end
            setPlayerAutolootItems(cid, autolootItems)
            doPlayerSendCancel(cid, "Your list of items overflowed, you should check your list.")
        elseif #autolootItems == maxItems then
            doPlayerSendCancel(cid, string.format("Your maximum limit is %u items, you must eliminate one to add another.", maxItems))
        elseif addPlayerAutolootItem(cid, itemid) then
            doPlayerSendCancel(cid, string.format("You added the item %s in the list.", name))
        else
            doSendMagicEffect(position, CONST_ME_POFF, cid)
            doPlayerSendCancel(cid, string.format("This item %s already in the list.", name))
        end
    end
elseif action == "remove" then
    if not it then
        doPlayerSendCancel(cid, string.format("This item %s does not exist.", name))
    else
        if removePlayerAutolootItem(cid, itemid) then
            doPlayerSendCancel(cid, string.format("You removed the item %s from the list.", name))
        else
            doPlayerSendCancel(cid, string.format("This item %s is not in the list.", name))
        end
    end
elseif action == "help" then
    showPlayerAutolootHelp(cid)
elseif action == "clear" then
    setPlayerAutolootItems(cid)
    doPlayerSendCancel(cid, "The list of items is now empty.")
else
    showPlayerAutoloot(cid)
end
return true
]]>
</talkaction>
</mod>
that's it all!

The command is:
Code:
!autoloot
!autoloot help
!autoloot clear
!autoloot add
!autoloot remove

How to configure it?
in the first lines you will find the following:
Code:
---@ You configurations:
autolootConfig = {}
autolootConfig.storageItems = 27000
autolootConfig.blockMonsters = {}
autolootConfig.goldToBank = true
autolootConfig.goldBankAnimation = true
autolootConfig.distanceLooting = 7
autolootConfig.freeSlots = 3
autolootConfig.premiumSlots = 6
autolootConfig.vipStorage = {0,0}
autolootConfig.playerLevel = 8
autolootConfig.corpseEffect = CONST_ME_NONE
autolootConfig.golds = {2148, 2152, 2160}
autolootConfig.tittle = "Welcomen to Autoloot System\nCreate by Millhiore BT\n\nOptions: help, clear, add, remove\n"
autolootConfig.notHaveCapDrop = false

Notes:
  • I'm not sure if it works 100%, but I can assure +/- 99%
  • If you want to help me improve it, you are welcome to do it.
  • The system eliminates the elements that exceed the limit automatically, for example when you lose "premium".
 
Last edited:
This mod has errors please update, or some other system!
Post automatically merged:

XML:
[8/6/2020 13:19:26] [Error - TalkAction Interface]
[8/6/2020 13:19:26] local cid = 268476459
[8/6/2020 13:19:26] local words = "!autoloot"
[8/6/2020 13:19:26] local param = ""
[8/6/2020 13:19:26] local channel = 65534
[8/6/2020 13:19:26] domodlib("AutolootRevolutionBT")
[8/6/2020 13:19:26] local split = string.explode(param, ",") or {}
[8/6/2020 13:19:26] local action = tostring(split[1]):lower()
[8/6/2020 13:19:26] local name = tostring(split[2])
[8/6/2020 13:19:26] local itemid = getItemIdByName(name, false)
[8/6/2020 13:19:26] local it = getItemInfo(itemid and itemid or 0)
[8/6/2020 13:19:26] local position = getThingPosition(cid)
[8/6/2020 13:19:27] if action == "add" then
[8/6/2020 13:19:27]     if not it then
[8/6/2020 13:19:27]         doPlayerSendCancel(cid, string.format("This item %s does not exist.", name))
[8/6/2020 13:19:27]     else
[8/6/2020 13:19:27]         local maxItems = getPlayerPremiumEnabled(cid) and autolootConfig.premiumSlots or autolootConfig.freeSlots
[8/6/2020 13:19:27]         local autolootItems = getPlayerAutolootItems(cid)
[8/6/2020 13:19:27]         if #autolootItems > maxItems then
[8/6/2020 13:19:27]             for index = autolootConfig.premiumSlots, autolootConfig.freeSlots, -1 do
[8/6/2020 13:19:27]                 table.remove(autolootItems, index)
[8/6/2020 13:19:27]             end
[8/6/2020 13:19:27]             setPlayerAutolootItems(cid, autolootItems)
[8/6/2020 13:19:27]             doPlayerSendCancel(cid, "Your list of items overflowed, you should check your list.")
[8/6/2020 13:19:27]         elseif #autolootItems == maxItems then
[8/6/2020 13:19:27]             doPlayerSendCancel(cid, string.format("Your maximum limit is %u items, you must eliminate one to add another.", maxItems))
[8/6/2020 13:19:27]         elseif addPlayerAutolootItem(cid, itemid) then
[8/6/2020 13:19:27]             doPlayerSendCancel(cid, string.format("You added the item %s in the list.", name))
[8/6/2020 13:19:27]         else
[8/6/2020 13:19:27]             doSendMagicEffect(position, CONST_ME_POFF, cid)
[8/6/2020 13:19:27]             doPlayerSendCancel(cid, string.format("This item %s already in the list.", name))
[8/6/2020 13:19:27]         end
[8/6/2020 13:19:27]     end
[8/6/2020 13:19:27] elseif action == "remove" then
[8/6/2020 13:19:27]     if not it then
[8/6/2020 13:19:27]         doPlayerSendCancel(cid, string.format("This item %s does not exist.", name))
[8/6/2020 13:19:27]     else
[8/6/2020 13:19:27]         if removePlayerAutolootItem(cid, itemid) then
[8/6/2020 13:19:27]             doPlayerSendCancel(cid, string.format("You removed the item %s from the list.", name))
[8/6/2020 13:19:27]         else
[8/6/2020 13:19:27]             doPlayerSendCancel(cid, string.format("This item %s is not in the list.", name))
[8/6/2020 13:19:27]         end
[8/6/2020 13:19:27]     end
[8/6/2020 13:19:27] elseif action == "help" then
[8/6/2020 13:19:27]     showPlayerAutolootHelp(cid)
[8/6/2020 13:19:27] elseif action == "clear" then
[8/6/2020 13:19:27]     setPlayerAutolootItems(cid)
[8/6/2020 13:19:27]     doPlayerSendCancel(cid, "The list of items is now empty.")
[8/6/2020 13:19:27] else
[8/6/2020 13:19:27]     showPlayerAutoloot(cid)
[8/6/2020 13:19:27] end
[8/6/2020 13:19:27] return true

[8/6/2020 13:19:27] Description:
[8/6/2020 13:19:27] (LuaInterface::luaGetItemIdByName) Item not found
 
Last edited:
Hello there!
i have a problem
when the autoloot take de loot, the char get debug =(
my client is extended.... maybe thats why?
 
Code:
[3:52:02.026] [Error - CreatureScript Interface]
[3:52:02.026] In a timer event called from:
[3:52:02.026] domodlib("AutolootRevolutionBT")
[3:52:02.026] function onDeath(cid, corpse, deathList)
[3:52:02.026]     local killer = deathList[1]
[3:52:02.026]     local position = getThingPosition(cid)
[3:52:02.026]     if corpse.itemid > 1 then
[3:52:02.026]         addEvent(doCorpseAutoloot, 100, killer, position, corpse.itemid)
[3:52:02.026]     end
[3:52:02.026]     return true
[3:52:02.026] end
[3:52:02.026] :onDeath
[3:52:02.026] Description:
[3:52:02.026] (luaGetThingPosition) Thing not found

[3:52:02.026] [Error - CreatureScript Interface]
[3:52:02.026] In a timer event called from:
[3:52:02.026] domodlib("AutolootRevolutionBT")
[3:52:02.026] function onDeath(cid, corpse, deathList)
[3:52:02.026]     local killer = deathList[1]
[3:52:02.026]     local position = getThingPosition(cid)
[3:52:02.026]     if corpse.itemid > 1 then
[3:52:02.026]         addEvent(doCorpseAutoloot, 100, killer, position, corpse.itemid)
[3:52:02.026]     end
[3:52:02.026]     return true
[3:52:02.026] end
[3:52:02.026] :onDeath
[3:52:02.026] Description:
[3:52:02.026] [string "---@ You configurations:..."]:42: attempt to index local 'position' (a boolean value)
[3:52:02.026] stack traceback:
[3:52:02.026]   [string "---@ You configurations:..."]:42: in function 'getPositionDistance'
[3:52:02.026]   [string "---@ You configurations:..."]:140: in function <[string "---@ You configurations:..."]:139>
any idea?
 
Hello there!
i have a problem
when the autoloot take de loot, the char get debug =(
my client is extended.... maybe thats why?
If your custom client doesn't have animated text, that's probably the reason why you debug your client

@kedlei / @Darbaxa
This mod is for TFS 0.4 and possibly also compatible with TFS 0.3.6, but OTX is not on the compatibility list

Use the forum search engine, I am sure you will find some other mod, you can even ask for help by requesting a script in the support board - request
 
Last edited:
I recognize that there are already some systems that deal with the same, but this had been saved for some time and I want to share it with you now more than ever, as I plan to resume my stay as a scriptwriter, so this will be a good start. thanks!

go to their folder "mods" and create a new file "xml" with the name you want and paste the following code:
Code:
<?xml version="1.0" encoding="ISO-8859-1"?> 
<mod name="Autoloot Revolution BT" version="1.0" author="Millhiore BT" contact="none.com" enabled="yes">
<config name="AutolootRevolutionBT">
<![CDATA[
---@ You configurations:
autolootConfig = {}
autolootConfig.storageItems = 27000
autolootConfig.blockMonsters = {}
autolootConfig.goldToBank = true
autolootConfig.goldBankAnimation = true
autolootConfig.distanceLooting = 7
autolootConfig.freeSlots = 3
autolootConfig.premiumSlots = 6
autolootConfig.vipStorage = {0,0}
autolootConfig.playerLevel = 8
autolootConfig.corpseEffect = CONST_ME_NONE
autolootConfig.golds = {2148, 2152, 2160}
autolootConfig.tittle = "Welcomen to Autoloot System\nCreate by Millhiore BT\n\nOptions: help, clear, add, remove\n"
autolootConfig.notHaveCapDrop = false
---@ getPlayerPremiumEnabled(cid)
--# Return boolean
--# Create by Millhiore BT
function getPlayerPremiumEnabled(cid)
    if getConfigValue("freePremium") then
        return true
    end
    return getPlayerPremiumDays(cid) > 0
end
---@ getPlayerAutolootVip(cid)
--# Return boolean
--# Create by Millhiore BT
function getPlayerAutolootVip(cid)
    if autolootConfig.vipStorage[1] > 0 then
        if autolootConfig.vipStorage[2] > 0 then
            return getPlayerStorageValue(cid, autolootConfig.vipStorage[1]) == autolootConfig.vipStorage[2]
        else
            return getPlayerStorageValue(cid, autolootConfig.vipStorage[1]) >= os.time()
        end
    end
    return true
end
---@ getPositionDistance(position, positionEx)
--# Return number
--# Create by Millhiore BT
function getPositionDistance(position, positionEx)
    return math.max(math.max(math.abs(position.x - positionEx.x), math.abs(position.y - positionEx.y)), math.abs(position.z - positionEx.z))
end
---@ getPositionCorpse(pos [, itemid])
--# Return thing/nil
--# Create by Millhiore BT
function getPositionCorpse(pos, itemid)
    local tile = getTileInfo(pos)
    for index = 0, tile.things do
        pos.stackpos = index
        local corpse = getThingFromPos(pos, false)
        if corpse.itemid > 1 and isCorpse(corpse.uid) then
            local itemid = itemid or corpse.itemid
            if corpse.itemid == itemid then
                return corpse
            end
        end
    end
end
---@ getContainerItems(uid [, array])
--# Return array
--# Create by Millhiore BT
function getContainerItems(container, array, haveCap)
    array = array or {}
    haveCap = haveCap or false
    if not isContainer(container.uid) or getContainerSize(container.uid) == 0 then
        array[#array +1] = container
    else
        local size = getContainerSize(container.uid)
        haveCap = (getContainerCap(container.uid) -size) > 0
        for slot = 0, (size -1) do
            local item = getContainerItem(container.uid, slot)
            if item.itemid > 1 then
                getContainerItems(item, array, haveCap)
            end
        end
    end
    return #array >= 1 and array, haveCap
end
---@ getContainerItemsById(array/uid, itemid)
--# Return array
--# Create by Millhiore BT
function getContainerItemsById(container, itemid)
    local founds = {}
    local items = not container.uid and container or getContainerItems(container)
    for index, item in pairs(items) do
        if item.itemid == itemid then
            founds[#founds +1] = item
        end
    end
    return #founds >= 1 and founds
end
---@ doPlayerAddItemStackable(cid, itemid, count)
--# Return boolean
--# Create by Millhiore BT
function doPlayerAddItemStackable(cid, itemid, count)
    local container = getPlayerSlotItem(cid, CONST_SLOT_BACKPACK)
    if container.itemid > 1 then
        local items = getContainerItemsById(container, itemid)
        if not items then
            return doPlayerAddItem(cid, itemid, count)
        else
            local piles = #items
            for index, item in pairs(items) do
                if item.type < 100 then
                    local sum = item.type + count
                    local result = doTransformItem(item.uid, itemid, sum)
                    if sum <= 100 then
                        return result
                    else
                        return doPlayerAddItem(cid, itemid, sum - 100)
                    end
                else
                    piles = piles - 1
                    if piles == 0 then
                        return doPlayerAddItem(cid, itemid, count)
                    end
                end
            end
        end
    end
    return false
end
---@ getContainerMoneys(items)
--# Return array/false
--# Create by Millhiore BT
function getContainerMoneys(items)
    local moneys = {}
    for slot, item in pairs(items) do
        if isInArray(autolootConfig.golds, item.itemid) then
            moneys[#moneys +1] = item
        end
    end
    return #moneys > 0 and moneys
end
---@ doCorpseAutoloot(cid, pos itemid)
--# Return boolean/nil
--# Create by Millhiore BT
function doCorpseAutoloot(cid, pos, itemid, moneys)
    if not getPlayerAutolootVip(cid) or getPositionDistance(getThingPosition(cid), pos) > autolootConfig.distanceLooting
    or getPlayerLevel(cid) < autolootConfig.playerLevel then
        return nil
    end
    local corpse = getPositionCorpse(pos, itemid)
    if corpse and isContainer(corpse.uid) then
        local items, haveCap = moneys or getContainerItems(corpse)
        local sendEffect = false
        if items then
            for slot, item in pairs(items) do
                if isInArray(getPlayerAutolootItems(cid), item.itemid) then
                    sendEffect = true
                    local removeIt = false
                    local goldToBank = autolootConfig.goldToBank and isInArray(autolootConfig.golds, item.itemid)
                    if not goldToBank then
                        if not haveCap and autolootConfig.notHaveCapDrop then
                            return doCorpseAutoloot(cid, pos, itemid, getContainerMoneys(items))
                        end
                        if isItemStackable(item.itemid) then
                            removeIt = doPlayerAddItemStackable(cid, item.itemid, item.type)
                        else
                            removeIt = doPlayerAddItem(cid, item.itemid)
                        end
                    else
                        local add = (getItemInfo(item.itemid).worth * item.type)
                        removeIt = doPlayerSetBalance(cid, getPlayerBalance(cid) + add)
                        if autolootConfig.goldBankAnimation then
                            doSendAnimatedText(pos, string.format("+%u gps", add), TEXTCOLOR_YELLOW, cid)
                        end
                    end
                    if removeIt then
                        doRemoveItem(item.uid)
                    end
                end
            end
            if sendEffect then
                doSendMagicEffect(pos, autolootConfig.corpseEffect, cid)
            end
            return true
        end
    end
end
---@ getPlayerAutolootItems(cid)
--# Return array
--# Create by Millhiore BT
function getPlayerAutolootItems(cid)
    local array = {}
    local content = tostring(getPlayerStorageValue(cid, autolootConfig.storageItems))
    for itemid in string.gmatch(content, "(%d+):") do
        array[#array +1] = tonumber(itemid)
    end
    return array
end
---@ getPlayerAutolootItem(cid, itemid)
--# Return number/nil
--# Create by Millhiore BT
function getPlayerAutolootItem(cid, itemid)
    local autolootItems = getPlayerAutolootItems(cid)
    for index, id in pairs(autolootItems) do
        if itemid == id then
            return index
        end
    end
end
---@ setPlayerAutolootItems(cid [, array])
--# Return boolean
--# Create by Millhiore BT
function setPlayerAutolootItems(cid, array)
    local array = array or {}
    local content = "&"
    for index, itemid in pairs(array) do
        content = string.format("%s%u:", content, itemid)
    end
    return setPlayerStorageValue(cid, autolootConfig.storageItems, content)
end
---@ addPlayerAutolootItem(cid, itemid)
--# Return boolean
--# Create by Millhiore BT
function addPlayerAutolootItem(cid, itemid)
    local autolootItems = getPlayerAutolootItems(cid)
    local found = getPlayerAutolootItem(cid, itemid)
    if not found then
        autolootItems[#autolootItems +1] = itemid
        return setPlayerAutolootItems(cid, autolootItems)
    end
end
---@ removePlayerAutolootItem(cid, itemid)
--# Return boolean/nil
--# Create by Millhiore BT
function removePlayerAutolootItem(cid, itemid)
    local autolootItems = getPlayerAutolootItems(cid)
    local found = getPlayerAutolootItem(cid, itemid)
    if found then
        table.remove(autolootItems, found)
        return setPlayerAutolootItems(cid, autolootItems)
    end
end
---@ showPlayerAutoloot(cid)
--# Return boolean
--# Create by Millhiore BT
function showPlayerAutoloot(cid)
    local maxItems = getPlayerPremiumEnabled(cid) and autolootConfig.premiumSlots or autolootConfig.freeSlots
    local content = string.format("%sYou available slots: %u\n\n%s", autolootConfig.tittle, maxItems, "Your autoloot items:\n")
    local autolootItems = getPlayerAutolootItems(cid)
    for index, itemid in pairs(autolootItems) do
        local it = getItemInfo(itemid)
        content = string.format("%s %c %s\n", content, 155, it.name)
    end
    return doShowTextDialog(cid, 2529, content)
end
---@ showPlayerAutolootHelp(cid)
--# Return boolean
--# Create by Millhiore BT
function showPlayerAutolootHelp(cid)
    local content = autolootConfig.tittle .. "For you info:\n"
    for key, value in pairs(autolootConfig) do
        if not isInArray({"storageItems", "blockMonsters", "vipStorage", "golds", "tittle"}, key) then
            content = string.format("%s%c %s: %s\n", content, 155, tostring(key), tostring(value))
        end
    end
    return doShowTextDialog(cid, 2529, content)
end
]]>
</config>
<event type="login" name="AutolootRBTLogin" event="script">
<![CDATA[
function onLogin(cid)
    local maxItems = getPlayerPremiumEnabled(cid) and autolootConfig.premiumSlots or autolootConfig.freeSlots
    local autolootItems = getPlayerAutolootItems(cid)
    if #autolootItems > maxItems then
        for index = autolootConfig.premiumSlots, autolootConfig.freeSlots, -1 do
            table.remove(autolootItems, index)
        end
        setPlayerAutolootItems(cid, autolootItems)
        doPlayerSendCancel(cid, "Your list of items overflowed, you should check your list.")
    end
    registerCreatureEvent(cid, "AutolootRBTCombat")
    return true
end
]]>
</event>
<event type="death" name="AutolootRBTDeath" event="script">
<![CDATA[
domodlib("AutolootRevolutionBT")
function onDeath(cid, corpse, deathList)
    local killer = deathList[1]
    local position = getThingPosition(cid)
    if corpse.itemid > 1 then
        addEvent(doCorpseAutoloot, 100, killer, position, corpse.itemid)
    end
    return true
end
]]>
</event>
<event type="combat" name="AutolootRBTCombat" event="script">
<![CDATA[
domodlib("AutolootRevolutionBT")
if isPlayer(cid) and isMonster(target) then
    if not isInArray(autolootConfig.blockMonsters, string.lower(getCreatureName(target))) then
        registerCreatureEvent(target, "AutolootRBTDeath")
    end
end
return true
]]>
</event>
<talkaction words="!autoloot;/autoloot" event="buffer">
<![CDATA[
domodlib("AutolootRevolutionBT")
local split = string.explode(param, ",") or {}
local action = tostring(split[1]):lower()
local name = tostring(split[2])
local itemid = getItemIdByName(name, false)
local it = getItemInfo(itemid and itemid or 0)
local position = getThingPosition(cid)
if action == "add" then
    if not it then
        doPlayerSendCancel(cid, string.format("This item %s does not exist.", name))
    else
        local maxItems = getPlayerPremiumEnabled(cid) and autolootConfig.premiumSlots or autolootConfig.freeSlots
        local autolootItems = getPlayerAutolootItems(cid)
        if #autolootItems > maxItems then
            for index = autolootConfig.premiumSlots, autolootConfig.freeSlots, -1 do
                table.remove(autolootItems, index)
            end
            setPlayerAutolootItems(cid, autolootItems)
            doPlayerSendCancel(cid, "Your list of items overflowed, you should check your list.")
        elseif #autolootItems == maxItems then
            doPlayerSendCancel(cid, string.format("Your maximum limit is %u items, you must eliminate one to add another.", maxItems))
        elseif addPlayerAutolootItem(cid, itemid) then
            doPlayerSendCancel(cid, string.format("You added the item %s in the list.", name))
        else
            doSendMagicEffect(position, CONST_ME_POFF, cid)
            doPlayerSendCancel(cid, string.format("This item %s already in the list.", name))
        end
    end
elseif action == "remove" then
    if not it then
        doPlayerSendCancel(cid, string.format("This item %s does not exist.", name))
    else
        if removePlayerAutolootItem(cid, itemid) then
            doPlayerSendCancel(cid, string.format("You removed the item %s from the list.", name))
        else
            doPlayerSendCancel(cid, string.format("This item %s is not in the list.", name))
        end
    end
elseif action == "help" then
    showPlayerAutolootHelp(cid)
elseif action == "clear" then
    setPlayerAutolootItems(cid)
    doPlayerSendCancel(cid, "The list of items is now empty.")
else
    showPlayerAutoloot(cid)
end
return true
]]>
</talkaction>
</mod>
that's it all!

The command is:
Code:
!autoloot
!autoloot help
!autoloot clear
!autoloot add
!autoloot remove

How to configure it?
in the first lines you will find the following:
Code:
---@ You configurations:
autolootConfig = {}
autolootConfig.storageItems = 27000
autolootConfig.blockMonsters = {}
autolootConfig.goldToBank = true
autolootConfig.goldBankAnimation = true
autolootConfig.distanceLooting = 7
autolootConfig.freeSlots = 3
autolootConfig.premiumSlots = 6
autolootConfig.vipStorage = {0,0}
autolootConfig.playerLevel = 8
autolootConfig.corpseEffect = CONST_ME_NONE
autolootConfig.golds = {2148, 2152, 2160}
autolootConfig.tittle = "Welcomen to Autoloot System\nCreate by Millhiore BT\n\nOptions: help, clear, add, remove\n"
autolootConfig.notHaveCapDrop = false

Notes:
  • I'm not sure if it works 100%, but I can assure +/- 99%
  • If you want to help me improve it, you are welcome to do it.
  • The system eliminates the elements that exceed the limit automatically, for example when you lose "premium".
There's a chance to release a version for OTX 2? Look better then my actual MOD
 
Replacing:
Lua:
domodlib("AutolootRevolutionBT")
if isPlayer(cid) and isMonster(target) then
    if not isInArray(autolootConfig.blockMonsters, string.lower(getCreatureName(target))) then
        registerCreatureEvent(target, "AutolootRBTDeath")
    end
end
return true
with:
Lua:
if isPlayer(cid) and isMonster(target) then
    registerCreatureEvent(target, "AutolootRBTDeath")
end
return true
reduced CPU usage by that system 141 times! Before it used 50% of CPU with 100 online on evo server.

Blocking monsters does not work anymore, but CPU usage change is incredible.
That blockMonsters check should be added in onDeath event.

I'm not sure, if that domodlib("AutolootRevolutionBT") is required at all. Isn't config of module loaded automatically before loading code of each event?
(domodlib("AutolootRevolutionBT") is thing that used that 50% CPU)

@Sarah Wesker
Please move that blockMonsters to death event or remove blockMonsters feature in first post. People will probably visit thread, copy code from first post, do not check for my comment and after few months get surprised that their server use 80% CPU with 100 online.
 
Replacing:
Lua:
domodlib("AutolootRevolutionBT")
if isPlayer(cid) and isMonster(target) then
    if not isInArray(autolootConfig.blockMonsters, string.lower(getCreatureName(target))) then
        registerCreatureEvent(target, "AutolootRBTDeath")
    end
end
return true
with:
Lua:
if isPlayer(cid) and isMonster(target) then
    registerCreatureEvent(target, "AutolootRBTDeath")
end
return true
reduced CPU usage by that system 141 times! Before it used 50% of CPU with 100 online on evo server.

Blocking monsters does not work anymore, but CPU usage change is incredible.
That blockMonsters check should be added in onDeath event.

I'm not sure, if that domodlib("AutolootRevolutionBT") is required at all. Isn't config of module loaded automatically before loading code of each event?
(domodlib("AutolootRevolutionBT") is thing that used that 50% CPU)

@Sarah Wesker
Please move that blockMonsters to death event or remove blockMonsters feature in first post. People will probably visit thread, copy code from first post, do not check for my comment and after few months get surprised that their server use 80% CPU with 100 online.
You can reduce it to 0% by not doing dumb shit like table traversal. Example:

Lua:
-- === TABLE TRAVERSAL ===
local monsters = {
	"rat",
	"cave rat",
	"demon",
	"troll",
	"snake"
}

if not isInArray(monsters, getCreatureName(target):lower()) then
	...
end

Do this instead:
Lua:
-- === TABLE INDEX LOOKUP ===
local monsters = {
	["rat"] = true,
	["cave rat"] = true,
	["demon"] = true,
	["troll"] = true,
	["snake"] = true,
}

if not monsters[getCreatureName(target):lower()] then
	...
end

What is the difference?
The difference is that isInArray or table.contains does something like this internally
Lua:
for _, value in pairs(monsters) do
    if monsterName == value then
        return true
    end
end
Basically iterating over the entire table and checking every single element 1 by 1. The bigger the table is, the slower this code will run.
On the other hand, doing a table index lookup is basically instantaneous. It doesn't iterate over the whole table, and table size isn't really a factor at all.

Side note: The probably bigger reason why this code is slow is because it's running on TFS 0.4. In TFS 0.4, isInArray is actually in the engine, and running super slow. https://github.com/Fir3element/3777/blob/master/src/luascript.cpp#L8592-L8700

If you want to keep using the isInArray function, delete it from the engine and move it to a Lua library. Here is a Lua implementation:
Lua:
function isInArray(tbl, value, caseSensitive)
	local lookingForString = type(value) == "string"
	if lookingForString and (not caseSensitive) then
		value = value:lower()
	end

	for _, targetColumn in pairs(tbl) do
		if lookingForString and (not caseSensitive) then
			if targetColumn:lower() == value then
				return true
			end
		else
			if targetColumn == value then
				return true
			end
		end
	end
	
	return false
end
 
@Alpha
Problem with that code is domodlib("AutolootRevolutionBT"), which probably runs 'config' part of module by Lua interpreter like dofile. It's like loading 260 lines of Lua code from different file each function execution.

How Lua version of isInArray can be faster than C++?! It's doing same thing, iterate over table and compare every value.

Of course table with reverse index - by creature name - is better. At least should be, as it should lookup std::map using hash.
 
How Lua version of isInArray can be faster than C++?! It's doing same thing, iterate over table and compare every value.
Never investigated it much, but I remember the C++ implementation with boost there being slow as fuck, feel free to benchmark it yourself

@Yamaken had a similar story with it
 
Never investigated it much, but I remember the C++ implementation with boost there being slow as fuck, feel free to benchmark it yourself

@Yamaken had a similar story with it
I did benchmarks.

Data:
Table with 30 strings, each 7-10 letters.

Test:
Search for 3 existing elements 100.000 times case-sensitive.
Search for 3 existing elements 100.000 times case-insensitive.
Total 600.000 searches.
I ran test 3 times using C++ and Lua implementation.

Machine:
Ryzen 3600 with 64 GB DDR4 ( Dedicated Root Server Hosting - Hetzner Online GmbH (https://www.hetzner.com/dedicated-rootserver/ax41-nvme) )

Results are quite dependent on engine compilation parameters.
C++ version of isInArray compiled without optimization is almost 381% slower than Lua version.
C++ version of isInArray compiled with optimization is around 24% slower than Lua version.

1. Debug mode: -O0 -g3 -std=c++11
C++ implementation: 3789 ms
Lua
implementation: 994 ms

2. Release mode:
-Ofast -march=native -std=c++11
C++ implementation: 1218 ms
Lua
implementation: 978 ms

3. Release mode 2:
-Ofast -std=c++11
C++ implementation: 1213 ms
Lua
implementation: 1001 ms

4. Release with debug
-Ofast -g3 -march=native -std=c++11
C++ implementation: 1196 ms
Lua
implementation: 956 ms

5. Release with debug 2
-Ofast -g3 -std=c++11
C++ implementation: 1142 ms
Lua
implementation: 986 ms

// weird modes

6.
-O0 -march=native -std=c++11
C++ implementation: 3900 ms
Lua
implementation: 993 ms

7.
-O0 -std=c++11
C++ implementation: 3836ms
Lua
implementation: 1001 ms

8.
-O0 -g3 -march=native -std=c++11
C++ implementation: 3860 ms
Lua
implementation: 982 ms

EDIT:

Other benchmarks on that machine with 'release mode' compilation:
getCreatureName(cid) - 6kk executions per second
getCreatureStorage(cid, 521002345) - 3kk executions per second with no storages on player
getCreatureStorage(cid, 521002345) - 2kk executions per second with 10k storages set on player (521002345 was in range of these 10k storages)
doCreatureSetStorage(cid, 521002345, 1) - 250k executions per second, number of already set player storages did not affect execution time

EDIT 2:
After replacing 0.4 dispatcher and schedule with 1.2 code, scheduler CPU usage is 10 times lower - on evo server with ~100k 'addEvent' per minute.
After dispatcher&scheduler replace, change 'autogen.sh' (0.4) to CMakeLists.txt (1.x+) compilation and compilation parameters change from -O0 -g3 to -Ofast -g3 -match=native CPU usage is 2-3 times lower.
You can save a looot of CPU without any real work on optimization of Lua/C++ code. Just copy some code from new TFS to 0.4 and change compilation parameters.
 
Last edited:
You can reduce it to 0% by not doing dumb shit like table traversal. Example:

Lua:
-- === TABLE TRAVERSAL ===
local monsters = {
    "rat",
    "cave rat",
    "demon",
    "troll",
    "snake"
}

if not isInArray(monsters, getCreatureName(target):lower()) then
    ...
end

Do this instead:
Lua:
-- === TABLE INDEX LOOKUP ===
local monsters = {
    ["rat"] = true,
    ["cave rat"] = true,
    ["demon"] = true,
    ["troll"] = true,
    ["snake"] = true,
}

if not monsters[getCreatureName(target):lower()] then
    ...
end

What is the difference?
The difference is that isInArray or table.contains does something like this internally
Lua:
for _, value in pairs(monsters) do
    if monsterName == value then
        return true
    end
end
Basically iterating over the entire table and checking every single element 1 by 1. The bigger the table is, the slower this code will run.
On the other hand, doing a table index lookup is basically instantaneous. It doesn't iterate over the whole table, and table size isn't really a factor at all.

Side note: The probably bigger reason why this code is slow is because it's running on TFS 0.4. In TFS 0.4, isInArray is actually in the engine, and running super slow. https://github.com/Fir3element/3777/blob/master/src/luascript.cpp#L8592-L8700

If you want to keep using the isInArray function, delete it from the engine and move it to a Lua library. Here is a Lua implementation:
Lua:
function isInArray(tbl, value, caseSensitive)
    local lookingForString = type(value) == "string"
    if lookingForString and (not caseSensitive) then
        value = value:lower()
    end

    for _, targetColumn in pairs(tbl) do
        if lookingForString and (not caseSensitive) then
            if targetColumn:lower() == value then
                return true
            end
        else
            if targetColumn == value then
                return true
            end
        end
    end
   
    return false
end

Did i done something wrong?
Code:
Welcomen to Autoloot System Create by Millhiore BT Options: help, clear, add, remove You available slots: 3 Your autoloot items:  legion helmet

Code:
00:53 Loot of a rotworm: a legion helmet, a lump of dirt, a mace, ham, meat, 11 gold coins.
and legion helmet did not come to my bag:

Code:
<?xml version="1.0" encoding="ISO-8859-1"?> 
<mod name="Autoloot Revolution BT" version="1.0" author="Millhiore BT" contact="none.com" enabled="yes">
<config name="AutolootRevolutionBT">
<![CDATA[
---@ You configurations:
autolootConfig = {}
autolootConfig.storageItems = 27000
autolootConfig.blockMonsters = {}
autolootConfig.goldToBank = true
autolootConfig.goldBankAnimation = true
autolootConfig.distanceLooting = 7
autolootConfig.freeSlots = 3
autolootConfig.premiumSlots = 6
autolootConfig.vipStorage = {0,0}
autolootConfig.playerLevel = 8
autolootConfig.corpseEffect = CONST_ME_NONE
autolootConfig.golds = {2148, 2152, 2160}
autolootConfig.tittle = "Welcomen to Autoloot System\nCreate by Millhiore BT\n\nOptions: help, clear, add, remove\n"
autolootConfig.notHaveCapDrop = false
---@ getPlayerPremiumEnabled(cid)
--# Return boolean
--# Create by Millhiore BT
function getPlayerPremiumEnabled(cid)
    if getConfigValue("freePremium") then
        return true
    end
    return getPlayerPremiumDays(cid) > 0
end
---@ getPlayerAutolootVip(cid)
--# Return boolean
--# Create by Millhiore BT
function getPlayerAutolootVip(cid)
    if autolootConfig.vipStorage[1] > 0 then
        if autolootConfig.vipStorage[2] > 0 then
            return getPlayerStorageValue(cid, autolootConfig.vipStorage[1]) == autolootConfig.vipStorage[2]
        else
            return getPlayerStorageValue(cid, autolootConfig.vipStorage[1]) >= os.time()
        end
    end
    return true
end
---@ getPositionDistance(position, positionEx)
--# Return number
--# Create by Millhiore BT
function getPositionDistance(position, positionEx)
    return math.max(math.max(math.abs(position.x - positionEx.x), math.abs(position.y - positionEx.y)), math.abs(position.z - positionEx.z))
end
---@ getPositionCorpse(pos [, itemid])
--# Return thing/nil
--# Create by Millhiore BT
function getPositionCorpse(pos, itemid)
    local tile = getTileInfo(pos)
    for index = 0, tile.things do
        pos.stackpos = index
        local corpse = getThingFromPos(pos, false)
        if corpse.itemid > 1 and isCorpse(corpse.uid) then
            local itemid = itemid or corpse.itemid
            if corpse.itemid == itemid then
                return corpse
            end
        end
    end
end
---@ getContainerItems(uid [, array])
--# Return array
--# Create by Millhiore BT
function getContainerItems(container, array, haveCap)
    array = array or {}
    haveCap = haveCap or false
    if not isContainer(container.uid) or getContainerSize(container.uid) == 0 then
        array[#array +1] = container
    else
        local size = getContainerSize(container.uid)
        haveCap = (getContainerCap(container.uid) -size) > 0
        for slot = 0, (size -1) do
            local item = getContainerItem(container.uid, slot)
            if item.itemid > 1 then
                getContainerItems(item, array, haveCap)
            end
        end
    end
    return #array >= 1 and array, haveCap
end
---@ getContainerItemsById(array/uid, itemid)
--# Return array
--# Create by Millhiore BT
function getContainerItemsById(container, itemid)
    local founds = {}
    local items = not container.uid and container or getContainerItems(container)
    for index, item in pairs(items) do
        if item.itemid == itemid then
            founds[#founds +1] = item
        end
    end
    return #founds >= 1 and founds
end
---@ doPlayerAddItemStackable(cid, itemid, count)
--# Return boolean
--# Create by Millhiore BT
function doPlayerAddItemStackable(cid, itemid, count)
    local container = getPlayerSlotItem(cid, CONST_SLOT_BACKPACK)
    if container.itemid > 1 then
        local items = getContainerItemsById(container, itemid)
        if not items then
            return doPlayerAddItem(cid, itemid, count)
        else
            local piles = #items
            for index, item in pairs(items) do
                if item.type < 100 then
                    local sum = item.type + count
                    local result = doTransformItem(item.uid, itemid, sum)
                    if sum <= 100 then
                        return result
                    else
                        return doPlayerAddItem(cid, itemid, sum - 100)
                    end
                else
                    piles = piles - 1
                    if piles == 0 then
                        return doPlayerAddItem(cid, itemid, count)
                    end
                end
            end
        end
    end
    return false
end
---@ getContainerMoneys(items)
--# Return array/false
--# Create by Millhiore BT
function getContainerMoneys(items)
    local moneys = {}
    for slot, item in pairs(items) do
        if isInArray(autolootConfig.golds, item.itemid) then
            moneys[#moneys +1] = item
        end
    end
    return #moneys > 0 and moneys
end
---@ doCorpseAutoloot(cid, pos itemid)
--# Return boolean/nil
--# Create by Millhiore BT
function doCorpseAutoloot(cid, pos, itemid, moneys)
    if not getPlayerAutolootVip(cid) or getPositionDistance(getThingPosition(cid), pos) > autolootConfig.distanceLooting
    or getPlayerLevel(cid) < autolootConfig.playerLevel then
        return nil
    end
    local corpse = getPositionCorpse(pos, itemid)
    if corpse and isContainer(corpse.uid) then
        local items, haveCap = moneys or getContainerItems(corpse)
        local sendEffect = false
        if items then
            for slot, item in pairs(items) do
                if isInArray(getPlayerAutolootItems(cid), item.itemid) then
                    sendEffect = true
                    local removeIt = false
                    local goldToBank = autolootConfig.goldToBank and isInArray(autolootConfig.golds, item.itemid)
                    if not goldToBank then
                        if not haveCap and autolootConfig.notHaveCapDrop then
                            return doCorpseAutoloot(cid, pos, itemid, getContainerMoneys(items))
                        end
                        if isItemStackable(item.itemid) then
                            removeIt = doPlayerAddItemStackable(cid, item.itemid, item.type)
                        else
                            removeIt = doPlayerAddItem(cid, item.itemid)
                        end
                    else
                        local add = (getItemInfo(item.itemid).worth * item.type)
                        removeIt = doPlayerSetBalance(cid, getPlayerBalance(cid) + add)
                        if autolootConfig.goldBankAnimation then
                            doSendAnimatedText(pos, string.format("+%u gps", add), TEXTCOLOR_YELLOW, cid)
                        end
                    end
                    if removeIt then
                        doRemoveItem(item.uid)
                    end
                end
            end
            if sendEffect then
                doSendMagicEffect(pos, autolootConfig.corpseEffect, cid)
            end
            return true
        end
    end
end
---@ getPlayerAutolootItems(cid)
--# Return array
--# Create by Millhiore BT
function getPlayerAutolootItems(cid)
    local array = {}
    local content = tostring(getPlayerStorageValue(cid, autolootConfig.storageItems))
    for itemid in string.gmatch(content, "(%d+):") do
        array[#array +1] = tonumber(itemid)
    end
    return array
end
---@ getPlayerAutolootItem(cid, itemid)
--# Return number/nil
--# Create by Millhiore BT
function getPlayerAutolootItem(cid, itemid)
    local autolootItems = getPlayerAutolootItems(cid)
    for index, id in pairs(autolootItems) do
        if itemid == id then
            return index
        end
    end
end
---@ setPlayerAutolootItems(cid [, array])
--# Return boolean
--# Create by Millhiore BT
function setPlayerAutolootItems(cid, array)
    local array = array or {}
    local content = "&"
    for index, itemid in pairs(array) do
        content = string.format("%s%u:", content, itemid)
    end
    return setPlayerStorageValue(cid, autolootConfig.storageItems, content)
end
---@ addPlayerAutolootItem(cid, itemid)
--# Return boolean
--# Create by Millhiore BT
function addPlayerAutolootItem(cid, itemid)
    local autolootItems = getPlayerAutolootItems(cid)
    local found = getPlayerAutolootItem(cid, itemid)
    if not found then
        autolootItems[#autolootItems +1] = itemid
        return setPlayerAutolootItems(cid, autolootItems)
    end
end
---@ removePlayerAutolootItem(cid, itemid)
--# Return boolean/nil
--# Create by Millhiore BT
function removePlayerAutolootItem(cid, itemid)
    local autolootItems = getPlayerAutolootItems(cid)
    local found = getPlayerAutolootItem(cid, itemid)
    if found then
        table.remove(autolootItems, found)
        return setPlayerAutolootItems(cid, autolootItems)
    end
end
---@ showPlayerAutoloot(cid)
--# Return boolean
--# Create by Millhiore BT
function showPlayerAutoloot(cid)
    local maxItems = getPlayerPremiumEnabled(cid) and autolootConfig.premiumSlots or autolootConfig.freeSlots
    local content = string.format("%sYou available slots: %u\n\n%s", autolootConfig.tittle, maxItems, "Your autoloot items:\n")
    local autolootItems = getPlayerAutolootItems(cid)
    for index, itemid in pairs(autolootItems) do
        local it = getItemInfo(itemid)
        content = string.format("%s %c %s\n", content, 155, it.name)
    end
    return doShowTextDialog(cid, 2529, content)
end
---@ showPlayerAutolootHelp(cid)
--# Return boolean
--# Create by Millhiore BT
function showPlayerAutolootHelp(cid)
    local content = autolootConfig.tittle .. "For you info:\n"
    for key, value in pairs(autolootConfig) do
        if not isInArray({"storageItems", "blockMonsters", "vipStorage", "golds", "tittle"}, key) then
            content = string.format("%s%c %s: %s\n", content, 155, tostring(key), tostring(value))
        end
    end
    return doShowTextDialog(cid, 2529, content)
end
]]>
</config>
<event type="login" name="AutolootRBTLogin" event="script">
<![CDATA[
function onLogin(cid)
    local maxItems = getPlayerPremiumEnabled(cid) and autolootConfig.premiumSlots or autolootConfig.freeSlots
    local autolootItems = getPlayerAutolootItems(cid)
    if #autolootItems > maxItems then
        for index = autolootConfig.premiumSlots, autolootConfig.freeSlots, -1 do
            table.remove(autolootItems, index)
        end
        setPlayerAutolootItems(cid, autolootItems)
        doPlayerSendCancel(cid, "Your list of items overflowed, you should check your list.")
    end
    registerCreatureEvent(cid, "AutolootRBTCombat")
    return true
end
]]>
</event>
<event type="death" name="AutolootRBTDeath" event="script">
<![CDATA[
domodlib("AutolootRevolutionBT")
function onDeath(cid, corpse, deathList)
    local killer = deathList[1]
    local position = getThingPosition(cid)
    if corpse.itemid > 1 then
        addEvent(doCorpseAutoloot, 100, killer, position, corpse.itemid)
    end
    return true
end
]]>
</event>
<event type="combat" name="AutolootRBTCombat" event="script">
<![CDATA[
local monsters = {
    ["rat"] = true,
    ["cave rat"] = true,
    ["demon"] = true,
    ["troll"] = true,
    ["rotworm"] = true,
}
if not monsters[getCreatureName(target):lower()] then
    registerCreatureEvent(target, "AutolootRBTDeath")
end
return true
]]>
</event>
<talkaction words="!autoloot;/autoloot" event="buffer">
<![CDATA[
domodlib("AutolootRevolutionBT")
local split = string.explode(param, ",") or {}
local action = tostring(split[1]):lower()
local name = tostring(split[2])
local itemid = getItemIdByName(name, false)
local it = getItemInfo(itemid and itemid or 0)
local position = getThingPosition(cid)
if action == "add" then
    if not it then
        doPlayerSendCancel(cid, string.format("This item %s does not exist.", name))
    else
        local maxItems = getPlayerPremiumEnabled(cid) and autolootConfig.premiumSlots or autolootConfig.freeSlots
        local autolootItems = getPlayerAutolootItems(cid)
        if #autolootItems > maxItems then
            for index = autolootConfig.premiumSlots, autolootConfig.freeSlots, -1 do
                table.remove(autolootItems, index)
            end
            setPlayerAutolootItems(cid, autolootItems)
            doPlayerSendCancel(cid, "Your list of items overflowed, you should check your list.")
        elseif #autolootItems == maxItems then
            doPlayerSendCancel(cid, string.format("Your maximum limit is %u items, you must eliminate one to add another.", maxItems))
        elseif addPlayerAutolootItem(cid, itemid) then
            doPlayerSendCancel(cid, string.format("You added the item %s in the list.", name))
        else
            doSendMagicEffect(position, CONST_ME_POFF, cid)
            doPlayerSendCancel(cid, string.format("This item %s already in the list.", name))
        end
    end
elseif action == "remove" then
    if not it then
        doPlayerSendCancel(cid, string.format("This item %s does not exist.", name))
    else
        if removePlayerAutolootItem(cid, itemid) then
            doPlayerSendCancel(cid, string.format("You removed the item %s from the list.", name))
        else
            doPlayerSendCancel(cid, string.format("This item %s is not in the list.", name))
        end
    end
elseif action == "help" then
    showPlayerAutolootHelp(cid)
elseif action == "clear" then
    setPlayerAutolootItems(cid)
    doPlayerSendCancel(cid, "The list of items is now empty.")
else
    showPlayerAutoloot(cid)
end
return true
]]>
</talkaction>
</mod>
 
Did i done something wrong?
Code:
Welcomen to Autoloot System Create by Millhiore BT Options: help, clear, add, remove You available slots: 3 Your autoloot items:  legion helmet

Code:
00:53 Loot of a rotworm: a legion helmet, a lump of dirt, a mace, ham, meat, 11 gold coins.
and legion helmet did not come to my bag:

Code:
<?xml version="1.0" encoding="ISO-8859-1"?>
<mod name="Autoloot Revolution BT" version="1.0" author="Millhiore BT" contact="none.com" enabled="yes">
<config name="AutolootRevolutionBT">
<![CDATA[
---@ You configurations:
autolootConfig = {}
autolootConfig.storageItems = 27000
autolootConfig.blockMonsters = {}
autolootConfig.goldToBank = true
autolootConfig.goldBankAnimation = true
autolootConfig.distanceLooting = 7
autolootConfig.freeSlots = 3
autolootConfig.premiumSlots = 6
autolootConfig.vipStorage = {0,0}
autolootConfig.playerLevel = 8
autolootConfig.corpseEffect = CONST_ME_NONE
autolootConfig.golds = {2148, 2152, 2160}
autolootConfig.tittle = "Welcomen to Autoloot System\nCreate by Millhiore BT\n\nOptions: help, clear, add, remove\n"
autolootConfig.notHaveCapDrop = false
---@ getPlayerPremiumEnabled(cid)
--# Return boolean
--# Create by Millhiore BT
function getPlayerPremiumEnabled(cid)
    if getConfigValue("freePremium") then
        return true
    end
    return getPlayerPremiumDays(cid) > 0
end
---@ getPlayerAutolootVip(cid)
--# Return boolean
--# Create by Millhiore BT
function getPlayerAutolootVip(cid)
    if autolootConfig.vipStorage[1] > 0 then
        if autolootConfig.vipStorage[2] > 0 then
            return getPlayerStorageValue(cid, autolootConfig.vipStorage[1]) == autolootConfig.vipStorage[2]
        else
            return getPlayerStorageValue(cid, autolootConfig.vipStorage[1]) >= os.time()
        end
    end
    return true
end
---@ getPositionDistance(position, positionEx)
--# Return number
--# Create by Millhiore BT
function getPositionDistance(position, positionEx)
    return math.max(math.max(math.abs(position.x - positionEx.x), math.abs(position.y - positionEx.y)), math.abs(position.z - positionEx.z))
end
---@ getPositionCorpse(pos [, itemid])
--# Return thing/nil
--# Create by Millhiore BT
function getPositionCorpse(pos, itemid)
    local tile = getTileInfo(pos)
    for index = 0, tile.things do
        pos.stackpos = index
        local corpse = getThingFromPos(pos, false)
        if corpse.itemid > 1 and isCorpse(corpse.uid) then
            local itemid = itemid or corpse.itemid
            if corpse.itemid == itemid then
                return corpse
            end
        end
    end
end
---@ getContainerItems(uid [, array])
--# Return array
--# Create by Millhiore BT
function getContainerItems(container, array, haveCap)
    array = array or {}
    haveCap = haveCap or false
    if not isContainer(container.uid) or getContainerSize(container.uid) == 0 then
        array[#array +1] = container
    else
        local size = getContainerSize(container.uid)
        haveCap = (getContainerCap(container.uid) -size) > 0
        for slot = 0, (size -1) do
            local item = getContainerItem(container.uid, slot)
            if item.itemid > 1 then
                getContainerItems(item, array, haveCap)
            end
        end
    end
    return #array >= 1 and array, haveCap
end
---@ getContainerItemsById(array/uid, itemid)
--# Return array
--# Create by Millhiore BT
function getContainerItemsById(container, itemid)
    local founds = {}
    local items = not container.uid and container or getContainerItems(container)
    for index, item in pairs(items) do
        if item.itemid == itemid then
            founds[#founds +1] = item
        end
    end
    return #founds >= 1 and founds
end
---@ doPlayerAddItemStackable(cid, itemid, count)
--# Return boolean
--# Create by Millhiore BT
function doPlayerAddItemStackable(cid, itemid, count)
    local container = getPlayerSlotItem(cid, CONST_SLOT_BACKPACK)
    if container.itemid > 1 then
        local items = getContainerItemsById(container, itemid)
        if not items then
            return doPlayerAddItem(cid, itemid, count)
        else
            local piles = #items
            for index, item in pairs(items) do
                if item.type < 100 then
                    local sum = item.type + count
                    local result = doTransformItem(item.uid, itemid, sum)
                    if sum <= 100 then
                        return result
                    else
                        return doPlayerAddItem(cid, itemid, sum - 100)
                    end
                else
                    piles = piles - 1
                    if piles == 0 then
                        return doPlayerAddItem(cid, itemid, count)
                    end
                end
            end
        end
    end
    return false
end
---@ getContainerMoneys(items)
--# Return array/false
--# Create by Millhiore BT
function getContainerMoneys(items)
    local moneys = {}
    for slot, item in pairs(items) do
        if isInArray(autolootConfig.golds, item.itemid) then
            moneys[#moneys +1] = item
        end
    end
    return #moneys > 0 and moneys
end
---@ doCorpseAutoloot(cid, pos itemid)
--# Return boolean/nil
--# Create by Millhiore BT
function doCorpseAutoloot(cid, pos, itemid, moneys)
    if not getPlayerAutolootVip(cid) or getPositionDistance(getThingPosition(cid), pos) > autolootConfig.distanceLooting
    or getPlayerLevel(cid) < autolootConfig.playerLevel then
        return nil
    end
    local corpse = getPositionCorpse(pos, itemid)
    if corpse and isContainer(corpse.uid) then
        local items, haveCap = moneys or getContainerItems(corpse)
        local sendEffect = false
        if items then
            for slot, item in pairs(items) do
                if isInArray(getPlayerAutolootItems(cid), item.itemid) then
                    sendEffect = true
                    local removeIt = false
                    local goldToBank = autolootConfig.goldToBank and isInArray(autolootConfig.golds, item.itemid)
                    if not goldToBank then
                        if not haveCap and autolootConfig.notHaveCapDrop then
                            return doCorpseAutoloot(cid, pos, itemid, getContainerMoneys(items))
                        end
                        if isItemStackable(item.itemid) then
                            removeIt = doPlayerAddItemStackable(cid, item.itemid, item.type)
                        else
                            removeIt = doPlayerAddItem(cid, item.itemid)
                        end
                    else
                        local add = (getItemInfo(item.itemid).worth * item.type)
                        removeIt = doPlayerSetBalance(cid, getPlayerBalance(cid) + add)
                        if autolootConfig.goldBankAnimation then
                            doSendAnimatedText(pos, string.format("+%u gps", add), TEXTCOLOR_YELLOW, cid)
                        end
                    end
                    if removeIt then
                        doRemoveItem(item.uid)
                    end
                end
            end
            if sendEffect then
                doSendMagicEffect(pos, autolootConfig.corpseEffect, cid)
            end
            return true
        end
    end
end
---@ getPlayerAutolootItems(cid)
--# Return array
--# Create by Millhiore BT
function getPlayerAutolootItems(cid)
    local array = {}
    local content = tostring(getPlayerStorageValue(cid, autolootConfig.storageItems))
    for itemid in string.gmatch(content, "(%d+):") do
        array[#array +1] = tonumber(itemid)
    end
    return array
end
---@ getPlayerAutolootItem(cid, itemid)
--# Return number/nil
--# Create by Millhiore BT
function getPlayerAutolootItem(cid, itemid)
    local autolootItems = getPlayerAutolootItems(cid)
    for index, id in pairs(autolootItems) do
        if itemid == id then
            return index
        end
    end
end
---@ setPlayerAutolootItems(cid [, array])
--# Return boolean
--# Create by Millhiore BT
function setPlayerAutolootItems(cid, array)
    local array = array or {}
    local content = "&"
    for index, itemid in pairs(array) do
        content = string.format("%s%u:", content, itemid)
    end
    return setPlayerStorageValue(cid, autolootConfig.storageItems, content)
end
---@ addPlayerAutolootItem(cid, itemid)
--# Return boolean
--# Create by Millhiore BT
function addPlayerAutolootItem(cid, itemid)
    local autolootItems = getPlayerAutolootItems(cid)
    local found = getPlayerAutolootItem(cid, itemid)
    if not found then
        autolootItems[#autolootItems +1] = itemid
        return setPlayerAutolootItems(cid, autolootItems)
    end
end
---@ removePlayerAutolootItem(cid, itemid)
--# Return boolean/nil
--# Create by Millhiore BT
function removePlayerAutolootItem(cid, itemid)
    local autolootItems = getPlayerAutolootItems(cid)
    local found = getPlayerAutolootItem(cid, itemid)
    if found then
        table.remove(autolootItems, found)
        return setPlayerAutolootItems(cid, autolootItems)
    end
end
---@ showPlayerAutoloot(cid)
--# Return boolean
--# Create by Millhiore BT
function showPlayerAutoloot(cid)
    local maxItems = getPlayerPremiumEnabled(cid) and autolootConfig.premiumSlots or autolootConfig.freeSlots
    local content = string.format("%sYou available slots: %u\n\n%s", autolootConfig.tittle, maxItems, "Your autoloot items:\n")
    local autolootItems = getPlayerAutolootItems(cid)
    for index, itemid in pairs(autolootItems) do
        local it = getItemInfo(itemid)
        content = string.format("%s %c %s\n", content, 155, it.name)
    end
    return doShowTextDialog(cid, 2529, content)
end
---@ showPlayerAutolootHelp(cid)
--# Return boolean
--# Create by Millhiore BT
function showPlayerAutolootHelp(cid)
    local content = autolootConfig.tittle .. "For you info:\n"
    for key, value in pairs(autolootConfig) do
        if not isInArray({"storageItems", "blockMonsters", "vipStorage", "golds", "tittle"}, key) then
            content = string.format("%s%c %s: %s\n", content, 155, tostring(key), tostring(value))
        end
    end
    return doShowTextDialog(cid, 2529, content)
end
]]>
</config>
<event type="login" name="AutolootRBTLogin" event="script">
<![CDATA[
function onLogin(cid)
    local maxItems = getPlayerPremiumEnabled(cid) and autolootConfig.premiumSlots or autolootConfig.freeSlots
    local autolootItems = getPlayerAutolootItems(cid)
    if #autolootItems > maxItems then
        for index = autolootConfig.premiumSlots, autolootConfig.freeSlots, -1 do
            table.remove(autolootItems, index)
        end
        setPlayerAutolootItems(cid, autolootItems)
        doPlayerSendCancel(cid, "Your list of items overflowed, you should check your list.")
    end
    registerCreatureEvent(cid, "AutolootRBTCombat")
    return true
end
]]>
</event>
<event type="death" name="AutolootRBTDeath" event="script">
<![CDATA[
domodlib("AutolootRevolutionBT")
function onDeath(cid, corpse, deathList)
    local killer = deathList[1]
    local position = getThingPosition(cid)
    if corpse.itemid > 1 then
        addEvent(doCorpseAutoloot, 100, killer, position, corpse.itemid)
    end
    return true
end
]]>
</event>
<event type="combat" name="AutolootRBTCombat" event="script">
<![CDATA[
local monsters = {
    ["rat"] = true,
    ["cave rat"] = true,
    ["demon"] = true,
    ["troll"] = true,
    ["rotworm"] = true,
}
if not monsters[getCreatureName(target):lower()] then
    registerCreatureEvent(target, "AutolootRBTDeath")
end
return true
]]>
</event>
<talkaction words="!autoloot;/autoloot" event="buffer">
<![CDATA[
domodlib("AutolootRevolutionBT")
local split = string.explode(param, ",") or {}
local action = tostring(split[1]):lower()
local name = tostring(split[2])
local itemid = getItemIdByName(name, false)
local it = getItemInfo(itemid and itemid or 0)
local position = getThingPosition(cid)
if action == "add" then
    if not it then
        doPlayerSendCancel(cid, string.format("This item %s does not exist.", name))
    else
        local maxItems = getPlayerPremiumEnabled(cid) and autolootConfig.premiumSlots or autolootConfig.freeSlots
        local autolootItems = getPlayerAutolootItems(cid)
        if #autolootItems > maxItems then
            for index = autolootConfig.premiumSlots, autolootConfig.freeSlots, -1 do
                table.remove(autolootItems, index)
            end
            setPlayerAutolootItems(cid, autolootItems)
            doPlayerSendCancel(cid, "Your list of items overflowed, you should check your list.")
        elseif #autolootItems == maxItems then
            doPlayerSendCancel(cid, string.format("Your maximum limit is %u items, you must eliminate one to add another.", maxItems))
        elseif addPlayerAutolootItem(cid, itemid) then
            doPlayerSendCancel(cid, string.format("You added the item %s in the list.", name))
        else
            doSendMagicEffect(position, CONST_ME_POFF, cid)
            doPlayerSendCancel(cid, string.format("This item %s already in the list.", name))
        end
    end
elseif action == "remove" then
    if not it then
        doPlayerSendCancel(cid, string.format("This item %s does not exist.", name))
    else
        if removePlayerAutolootItem(cid, itemid) then
            doPlayerSendCancel(cid, string.format("You removed the item %s from the list.", name))
        else
            doPlayerSendCancel(cid, string.format("This item %s is not in the list.", name))
        end
    end
elseif action == "help" then
    showPlayerAutolootHelp(cid)
elseif action == "clear" then
    setPlayerAutolootItems(cid)
    doPlayerSendCancel(cid, "The list of items is now empty.")
else
    showPlayerAutoloot(cid)
end
return true
]]>
</talkaction>
</mod>

did i put the if in a wrong place?
Code:
<event type="combat" name="AutolootRBTCombat" event="script">
<![CDATA[
local monsters = {
    ["rat"] = true,
    ["cave rat"] = true,
    ["demon"] = true,
    ["troll"] = true,
    ["rotworm"] = true,
}
if not monsters[getCreatureName(target):lower()] then
    registerCreatureEvent(target, "AutolootRBTDeath")
end
return true
]]>
</event>
 
Back
Top