<?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['common'] = {
color = 66, -- color of 'RARE' text
extra = {0, 0},
attrNames = true, -- show attribute names instead of rare
chance = {
[1] = 10000,
[2] = 5000 -- chance for 2nd stat
}
}
tiers['rare'] = {
color = 35,
extra = {7, 20}, -- additional percent bonus
chance = {
[1] = 3333,
[2] = 25000
}
}
tiers['epic'] = {
color = 149,
extra = {20, 35},
chance = {
[1] = 1000,
[2] = 100000 -- 2 bonuses always
}
}
tiers['legendary'] = {
color = 122,
extra = {35, 55},
chance = {
[1] = 1000,
[2] = 100000 -- 2 bonuses always
}
}
tiers['mythic'] = {
color = 213,
extra = {55, 85},
chance = {
[1] = 1000,
[2] = 100000 -- 2 bonuses always
}
}
tiers['relic'] = {
color = 121,
extra = {85, 120},
chance = {
[1] = 1000,
[2] = 100000 -- 2 bonuses always
}
}
tiers['godlike'] = {
color = 14,
extra = {120, 200},
chance = {
[1] = 1000,
[2] = 100000 -- 2 bonuses always
}
}
tiers['unmortal'] = {
color = 6,
extra = {200, 350},
chance = {
[1] = 1000,
[2] = 100000 -- 2 bonuses always
}
}
MELEE = 0
DISTANCE = 1
ARMOR = 2
SHIELD = 3
WAND = 4
DURATION_RING = 5
CHARGES = 6
--! attributes
attr['quick'] = {
attr = 'attackSpeed',
name = 'Attack Speed',
percent = {6, 20},
types = {MELEE, DISTANCE, WAND}
}
attr['fortified'] = {
attr = 'extraDefense',
base = 'defense',
name = 'Defense',
percent = {7, 25},
types = {MELEE, SHIELD}
}
attr['deadly'] = {
attr = 'extraAttack',
base = 'attack',
name = 'Attack',
types = {MELEE},
percent = {7, 25}
}
attr['strong'] = {
attr = 'armor',
name = 'Armor',
percent = {7, 20},
types = {ARMOR}
}
attr['hawkeye\'s'] = {
attr = 'hitChance',
name = 'Hit Chance',
percent = {10, 25},
types = {DISTANCE}
}
--[[ // not available without source edit
attr['farsight'] = {
attr = 'shootRange',
name = 'Shoot Range',
percent = {17, 34},
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')
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[i], 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[i] 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[i], ext)
if tmp then
if isContainer(tmp) then
if createChildLoot(tmp, v.loot[i].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)
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>
Where i put in?here is slightly updated version of the code
Lua:<?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['common'] = { color = 66, -- color of 'RARE' text extra = {0, 0}, attrNames = true, -- show attribute names instead of rare chance = { [1] = 10000, [2] = 5000 -- chance for 2nd stat } } tiers['rare'] = { color = 35, extra = {7, 20}, -- additional percent bonus chance = { [1] = 3333, [2] = 25000 } } tiers['epic'] = { color = 149, extra = {20, 35}, chance = { [1] = 1000, [2] = 100000 -- 2 bonuses always } } tiers['legendary'] = { color = 122, extra = {35, 55}, chance = { [1] = 1000, [2] = 100000 -- 2 bonuses always } } tiers['mythic'] = { color = 213, extra = {55, 85}, chance = { [1] = 1000, [2] = 100000 -- 2 bonuses always } } tiers['relic'] = { color = 121, extra = {85, 120}, chance = { [1] = 1000, [2] = 100000 -- 2 bonuses always } } tiers['godlike'] = { color = 14, extra = {120, 200}, chance = { [1] = 1000, [2] = 100000 -- 2 bonuses always } } tiers['unmortal'] = { color = 6, extra = {200, 350}, chance = { [1] = 1000, [2] = 100000 -- 2 bonuses always } } MELEE = 0 DISTANCE = 1 ARMOR = 2 SHIELD = 3 WAND = 4 DURATION_RING = 5 CHARGES = 6 --! attributes attr['quick'] = { attr = 'attackSpeed', name = 'Attack Speed', percent = {6, 20}, types = {MELEE, DISTANCE, WAND} } attr['fortified'] = { attr = 'extraDefense', base = 'defense', name = 'Defense', percent = {7, 25}, types = {MELEE, SHIELD} } attr['deadly'] = { attr = 'extraAttack', base = 'attack', name = 'Attack', types = {MELEE}, percent = {7, 25} } attr['strong'] = { attr = 'armor', name = 'Armor', percent = {7, 20}, types = {ARMOR} } attr['hawkeye\'s'] = { attr = 'hitChance', name = 'Hit Chance', percent = {10, 25}, types = {DISTANCE} } --[[ // not available without source edit attr['farsight'] = { attr = 'shootRange', name = 'Shoot Range', percent = {17, 34}, 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') 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[i], 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[i] 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[i], ext) if tmp then if isContainer(tmp) then if createChildLoot(tmp, v.loot[i].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) 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 anybody has any idea what source edits it will take for things such as life leech etc let me know.