• 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 0.X TFS 0.4 - Autoloot bug with rarity items

Vagnerking

Member
Joined
Jan 20, 2023
Messages
11
Reaction score
18
GitHub
Vagnerking
hello everyone, I have 2 mods on my otserver: one for autoloot and another for item drop by rarity.

The drop of items by rarity works as follows: every monster that the player kills has a chance of dropping rare, epic or legendary items.

But when my autoloot pulls the item from the dead monster, it kind of transforms the item into the original, it doesn't keep the rarity and attributes.


Mod AutoLoot:
<?xml version="1.0" encoding="ISO-8859-1"?>
<mod name="Loot System" version="1.0" author="Vodkart And Mkalo" contact="none.com" enabled="yes">
<config name="Loot_func"><![CDATA[

info = {
OnlyPremium = true,
AutomaticDeposit = true,
BlockMonsters = {},
BlockItemsList = {2123,2515}
}

function setPlayerStorageTable(cid, storage, tab)
local tabstr = "&"
for i,x in pairs(tab) do
tabstr = tabstr .. i .. "," .. x .. ";"
end
setPlayerStorageValue(cid, storage, tabstr:sub(1, #tabstr-1))
end
function getPlayerStorageTable(cid, storage)
local tabstr = getPlayerStorageValue(cid, storage)
local tab = {}
if type(tabstr) ~= "string" then
return {}
end
if tabstr:sub(1,1) ~= "&" then
return {}
end
local tabstr = tabstr:sub(2, #tabstr)
local a = string.explode(tabstr, ";")
for i,x in pairs(a) do
local b = string.explode(x, ",")
tab[tonumber(b[1]) or b[1]] = tonumber(b[2]) or b[2]
end
return tab
end

function isInTable(cid, item)
for _,i in pairs(getPlayerStorageTable(cid, 27000))do
if tonumber(i) == tonumber(item) then
return true
end
end
return false
end
function addItemTable(cid, item)
local x = {}
for i = 1,#getPlayerStorageTable(cid, 27000) do
table.insert(x,getPlayerStorageTable(cid, 27000))
end
if x ~= 0 then
table.insert(x,tonumber(item))
setPlayerStorageTable(cid, 27000, x)
else
setPlayerStorageTable(cid, 27000, {item})
end
end
function removeItemTable(cid, item)
local x = {}
for i = 1,#getPlayerStorageTable(cid, 27000) do
table.insert(x,getPlayerStorageTable(cid, 27000))
end
for i,v in ipairs(x) do
if tonumber(v) == tonumber(item) then
table.remove(x,i)
end
end
return setPlayerStorageTable(cid, 27000, x)
end
function ShowItemsTabble(cid)
local str,n = "-- Lista Autoloot --\n\n",0
for i = 1,#getPlayerStorageTable(cid, 27000) do
n = n + 1
str = str..""..n.." - "..getItemNameById(getPlayerStorageTable(cid, 27000)).."\n"
end
str = str.."\n\n Exemplos de uso: \n /autoloot add, NOME_ITEM -- Adiciona o item na lista"
str = str.."\n /autoloot remove, NOME_ITEM -- remove o item da list"
return doShowTextDialog(cid, 2529, str)
end
function getContainerItems(containeruid)
local items = {}
local containers = {}
if type(getContainerSize(containeruid)) ~= "number" then
return false
end
for slot = 0, getContainerSize(containeruid)-1 do
local item = getContainerItem(containeruid, slot)
if item.itemid == 0 then
break
end
if isContainer(item.uid) then
table.insert(containers, item.uid)
end
table.insert(items, item)
end
if #containers > 0 then
for i,x in ipairs(getContainerItems(containers[1])) do
table.insert(items, x)
end
table.remove(containers, 1)
end
return items
end
function getItemsInContainerById(container, itemid) -- Function By Kydrai
local items = {}
if isContainer(container) and getContainerSize(container) > 0 then
for slot=0, (getContainerSize(container)-1) do
local item = getContainerItem(container, slot)
if isContainer(item.uid) then
local itemsbag = getItemsInContainerById(item.uid, itemid)
for i=0, #itemsbag do
table.insert(items, itemsbag)
end
else
if itemid == item.itemid then
table.insert(items, item.uid)
end
end
end
end
return items
end
function doPlayerAddItemStacking(cid, itemid, quant) -- by mkalo
local item = getItemsInContainerById(getPlayerSlotItem(cid, 3).uid, itemid)
local piles = 0
if #item > 0 then
for i,x in pairs(item) do
if getThing(x).type < 100 then
local it = getThing(x)
doTransformItem(it.uid, itemid, it.type+quant)
if it.type+quant > 100 then
doPlayerAddItem(cid, itemid, it.type+quant-100)
end
else
piles = piles+1
end
break
end
else
return doPlayerAddItem(cid, itemid, quant)
end
if piles == #item then
doPlayerAddItem(cid, itemid, quant)
end
end
function AutomaticDeposit(cid,item,n)
local deposit = item == tonumber(2160) and (n*10000) or tonumber(item) == 2152 and (n*100) or (n*1)
return doPlayerDepositMoney(cid, deposit)
end
function corpseRetireItems(cid, pos)
local check = false
for i = 0, 255 do
pos.stackpos = i
tile = getTileThingByPos(pos)
if tile.uid > 0 and isCorpse(tile.uid) then
check = true break
end
end
if check == true then
local items = getContainerItems(tile.uid)
for i,x in pairs(items) do
if isInArray(getPlayerStorageTable(cid, 27000), tonumber(x.itemid)) then
if isItemStackable(x.itemid) then
doPlayerAddItemStacking(cid, x.itemid, x.type)
if info.AutomaticDeposit == true and isInArray({"2148","2152","2160"},tonumber(x.itemid)) then
AutomaticDeposit(cid,x.itemid,x.type)
end
else
doPlayerAddItem(cid, x.itemid)
end
doRemoveItem(x.uid)
end
end
end
end
]]></config>
<event type="login" name="LootLogin" event="script"><![CDATA[
function onLogin(cid)
registerCreatureEvent(cid, "MonsterAttack")
return true
end]]></event>
<event type="death" name="LootEventDeath" event="script"><![CDATA[
domodlib('Loot_func')
function onDeath(cid, corpse, deathList)
local killer,pos = deathList[1],getCreaturePosition(cid)
addEvent(corpseRetireItems,1,killer,pos)
return true
end]]></event>
<event type="combat" name="MonsterAttack" event="script"><![CDATA[
domodlib('Loot_func')
if isPlayer(cid) and isMonster(target) and not isInArray(info.BlockMonsters,string.lower(getCreatureName(target))) then
registerCreatureEvent(target, "LootEventDeath")
end
return true]]></event>
<talkaction words="!autoloot;/autoloot" event="buffer"><![CDATA[
domodlib('Loot_func')
local t = string.explode(string.lower(param), ",")
if info_OnlyPremium == true and not isPremium(cid) then
doPlayerSendCancel(cid, "you must be a premium account.") return true
elseif not t[1] then
ShowItemsTabble(cid) return true
elseif tonumber(t[1]) or tonumber(t[2]) then
doPlayerSendCancel(cid, "enter!autoloot add,name or !autoloot remove,name") return true
elseif isInArray({"add","remove"}, tostring(t[1])) then
local func,check = tostring(t[1]) == "add" and addItemTable or removeItemTable, tostring(t[1]) == "add" and true or false
local item = getItemIdByName(tostring(t[2]), false)
if not item then
doPlayerSendCancel(cid, "This item does not exist.") return true
elseif check == true and isInArray(info.BlockItemsList, item) then
doPlayerSendCancel(cid, "You can not add this item in the list!") return true
elseif isInTable(cid, item) == check then
doPlayerSendCancel(cid, "This Item "..(check == true and "already" or "is not").." in your list.") return true
end
func(cid, item)
doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE,check == true and "you added the item "..t[2].." in the list" or "you removed the item "..t[2].." from the list") return true
end
return true]]></talkaction>
</mod>


Mod Rarity Loot
<?xml version="1.0" encoding="UTF-8"?>
<mod name="Random Item Stats" enabled="1">
<config name="itemstats_conf"><![CDATA[
-- //
extra_loot_key = 123 --: optional storage for higher loot rate
vocation_base_attackspeed = getVocationInfo(1).attackSpeed --: used for attackSpeed stat
-- //

tiers, attr = {}, {}

tiers['rare'] = {
color = 66, -- color of 'RARE' text
extra = {0, 2},
attrNames = true, -- show attribute names instead of rare
chance = {
[1] = 3000,
[2] = 2000 -- chance for 2nd stat
}
}
tiers['epic'] = {
color = 35,
extra = {3, 5}, -- additional percent bonus
chance = {
[1] = 2000,
[2] = 1000
}
}
tiers['legendary'] = {
color = 149,
extra = {6, 10},
chance = {
[1] = 1000,
[2] = 500
}
}

MELEE = 0
DISTANCE = 1
ARMOR = 2
SHIELD = 3
WAND = 4
DURATION_RING = 5
CHARGES = 6

--! attributes
attr['fortified'] = {
attr = 'extraDefense',
base = 'defense',
name = 'Defense',
percent = {1, 5},
types = {MELEE, SHIELD}
}
attr['deadly'] = {
attr = 'extraAttack',
base = 'attack',
name = 'Attack',
types = {MELEE},
percent = {1, 5}
}
attr['strong'] = {
attr = 'armor',
name = 'Armor',
percent = {1, 5},
types = {ARMOR}
}
attr['hawkeye\'s'] = {
attr = 'hitChance',
name = 'Hit Chance',
percent = {1, 5},
types = {DISTANCE}
}
--[[ // not available without source edit
attr['farsight'] = {
attr = 'shootRange',
name = 'Shoot Range',
percent = {1, 5},
types = {DISTANCE, WAND}
}
]]
attr['charged'] = {
attr = 'charges',
name = 'Charges',
percent = {30, 45},
types = {CHARGES}
}
attr['divine'] = {
attr = 'duration',
name = 'Duration',
percent = {35, 50},
types = {DURATION_RING}
}
--/ attributes

rate = getConfigInfo('rateLoot') * 2

if( getConfigInfo('monsterLootMessage') ~= 0 )then
print('[Notice] Set monsterLootMessage = 0 to prevent duplicate loot messages')
end
]]></config>

<event type="kill" name="itemstats" event="script"><![CDATA[
domodlib('itemstats_conf')

function round(n, s)
return tonumber(('%.' .. (s or 0) .. 'f'):format(n))
end

function getContentDescription(uid, sep)
local ret, i, containers = '', 0, {}
while( i < getContainerSize(uid) )do
local v, s = getContainerItem(uid, i), ''
local k = getItemInfo(v.itemid)
k.name = getItemAttribute(v.uid, 'name') or k.name
if( k.name ~= '' )then
if( v.type > 1 and k.stackable and k.showCount )then
s = v.type .. ' ' .. k.plural
else
local article = getItemAttribute(v.uid, 'article') or k.article
s = (article == '' and '' or article .. ' ') .. k.name
end
ret = ret .. (i == 0 and not sep and '' or ', ') .. s
if( isContainer(v.uid) and getContainerSize(v.uid) ~= 0 )then
table.insert(containers, v.uid)
end
else
ret = ret .. (i == 0 and not sep and '' or ', ') .. 'an item of type ' .. v.itemid .. ', please report it to gamemaster'
end
i = i + 1
end
for i = 1, #containers do
ret = ret .. getContentDescription(containers, true)
end
return ret
end

local function send(cid, corpse, monster)
if( isPlayer(cid) )then
local ret = corpse and isContainer(corpse) and getContentDescription(corpse)
doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, 'Loot of ' .. monster .. ': ' .. (ret ~= '' and ret or 'nothing'))
local party = getPlayerParty(cid)
if( party )then
for _, pid in ipairs(getPartyMembers(party)) do
doPlayerSendChannelMessage(pid, '', 'Loot of ' .. monster .. ': ' .. (ret ~= '' and ret or 'nothing'), TALKTYPE_CHANNEL_W, CHANNEL_PARTY)
end
end
end
end

local function createLoot(i, ext)
local item = type(i.id) == 'table' and i.id[math.random(#i.id)] or i.id
local random = math.ceil(math.random(100000) / ext)
local tmpItem, f

if( random < i.chance )then
if i.subType == -1 then
f = getItemInfo(item)
end
tmpItem = doCreateItemEx(item,
i.subType ~= -1 and i.subType or
f.stackable and random % i.count + 1 or
f.charges ~= 0 and f.charges or
1
)
end

if( not tmpItem )then
return
end

if( i.actionId ~= -1 )then
doItemSetAttribute(tmpItem, 'aid', i.actionId)
end

if( i.uniqueId ~= -1 )then
doItemSetAttribute(tmpItem, 'uid', i.uniqueId)
end

if( i.text ~= '' )then
doItemSetAttribute(tmpItem, 'text', i.text)
end

local ret, done

for k, v in pairs(tiers) do
local cur, used = {}, {}
for i = 1, #v.chance do
if( math.random(100000) <= v.chance )then
if( f )then
f = getItemInfo(item)
end
if( not f.stackable )then
for m, n in pairs(attr) do
if( not table.find(used, m) and
(
( table.find(n.types, MELEE) and table.find({WEAPON_SWORD, WEAPON_CLUB, WEAPON_AXE}, f.weaponType) ) or
( table.find(n.types, DISTANCE) and f.weaponType == WEAPON_DIST and f.ammoType ~= 0 ) or
( table.find(n.types, ARMOR) and f.armor ~= 0 and f.wieldPosition ~= CONST_SLOT_NECKLACE ) or
( table.find(n.types, SHIELD) and f.defense ~= 0 and f.weaponType == WEAPON_SHIELD ) or
( table.find(n.types, WAND) and f.weaponType == WEAPON_WAND ) or
( table.find(n.types, DURATION_RING) and f.wieldPosition == CONST_SLOT_RING and f.transformEquipTo ~= 0 ) or
( table.find(n.types, CHARGES) and table.find({CONST_SLOT_RING, CONST_SLOT_NECKLACE}, f.wieldPosition) and f.charges ~= 0 )
) )then
table.insert(cur, m)
end
end

if( #cur ~= 0 )then
local n = cur[math.random(#cur)]
table.insert(used, n)

n = attr[n]
local percent, new, tmp = math.random(n.percent[1] + (v.extra[1] or 0), n.percent[2] + (v.extra[2] or 0))
-- hacks
if( n.attr == 'duration' )then
tmp = getItemInfo(f.transformEquipTo)
if tmp.transformDeEquipTo ~= item then
break
end
new = round( tmp.decayTime * (1 + percent / 100) * 1000 )
elseif( n.attr == 'attackSpeed' )then
new = round( vocation_base_attackspeed / (1 + percent / 100) )
elseif( n.attr == 'hitChance' ) then
new = round(
f.hitChance == -1 and
percent
or
f.hitChance * (1 + percent / 100)
)
else
new = round(
n.base and
f[n['attr']] + f[n['base']] * (percent / 100)
or
f[n['attr']] * (1 + percent / 100)
)

if( new == f[n[n.base and 'base' or 'attr']] )then -- no improvement
break
end
end

doItemSetAttribute(tmpItem, n.attr:lower(), new)

local name = getItemAttribute(tmpItem, 'name')
if( v.attrNames or not name )then
local name = (v.attrNames and used[#used] or k) .. ' ' .. (name or f.name)
doItemSetAttribute(tmpItem, 'name', name)

if( f.article ~= '' )then
local article = getArticle(name)
if( article ~= f.article )then
doItemSetAttribute(tmpItem, 'article', article)
end
end
end

local desc = getItemAttribute(tmpItem, 'description') or f.description
doItemSetAttribute(tmpItem, 'description', '[' .. n.name .. ': +' .. percent .. '%]' .. (desc == '' and '' or '\n' .. desc))

ret = k
end
cur = {}
if( #v.chance == i )then
done = true
end
end
else
done = i ~= 1
break
end
end
if( done )then
break
end
end

return tmpItem, ret
end

local function createChildLoot(parent, i, ext, pos)
if( not i or #i == 0 )then
return true
end

local size, cap = 0, getContainerCap(parent)
for k = 1, #i do
if( size == cap )then
break
end
local tmp, ret = createLoot(i[k], ext)
if( tmp )then
if( isContainer(tmp) )then
if( createChildLoot(tmp, i[k].child, ext, pos) )then
doAddContainerItemEx(parent, tmp)
size = size + 1
else
doRemoveItem(tmp)
end
else
if( ret )then
doSendMagicEffect(pos, CONST_ME_MAGIC_GREEN)
doSendAnimatedText(pos, ret:upper(), tiers[ret].color)
end
doAddContainerItemEx(parent, tmp)
size = size + 1
end
end
end

return size > 0
end

local function dropLoot(pos, v, ext, master, cid, target)
local corpse
if( not master or master == target )then -- 0.3/4
corpse = getTileItemById(pos, v.lookCorpse).uid
if( isContainer(corpse) )then
for i = 1, getContainerSize(corpse) do
doRemoveItem(getContainerItem(corpse, 0).uid)
end
local size, cap = 0, getContainerCap(corpse)
for i = 1, #v.loot do
if( size == cap )then
break
end
local tmp, ret = createLoot(v.loot, ext)
if( tmp )then
if( isContainer(tmp) )then
if( createChildLoot(tmp, v.loot.child, ext, pos) )then
doAddContainerItemEx(corpse, tmp)
size = size + 1
else
doRemoveItem(tmp)
end
else
if( ret )then
doSendMagicEffect(pos, CONST_ME_MAGIC_GREEN)
doSendAnimatedText(pos, ret:upper(), tiers[ret].color)
print('o player ' .. getCreatureName(cid) .. ' acabou de dropar o item de raridade: ' .. ret:upper() .. '.')
end
doAddContainerItemEx(corpse, tmp)
size = size + 1
end
end
end
end
end
send(cid, corpse, v.description)
end

function onKill(cid, target, damage, flags)
if( (damage == true or bit.band(flags, 1) == 1) and isMonster(target) )then -- 0.3/4
local v = getMonsterInfo(getCreatureName(target))
if( v and v.lookCorpse ~= 0 )then
local s = getCreatureStorage(cid, extra_loot_key)
addEvent(dropLoot, 0, getThingPos(target), v, s == -1 and rate or s, getCreatureMaster(target), cid, target)
end
end
return true
end
]]></event>

<event type="login" name="itemstats_login" event="buffer"><![CDATA[
registerCreatureEvent(cid, 'itemstats')
]]></event>

</mod>



If anyone knows how to solve it I would be very grateful, thank you!
 
Back
Top