• 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!
  • New resources must be posted under Resources tab. A discussion thread will be created automatically, you can't open threads manually anymore.

[Mod] Random Item Stats

Thank you
No, i just didnt know it was possible to do there, i made that looking others scripts...
But it you send, not work, even i changing
Code:
rate = rate * 1.5 -- If he's wearing the amulet then give him 50% extra chance

To
Code:
rate = rate * 1000.5 -- If he's wearing the amulet then give him 50% extra chance

Nothing change...

Are you getting any errors in the console?

Double check the code in this area, since I'm not using 0.4 so I'm not quite sure if it's written correctly:
Code:
            local ammy = getPlayerSlotItem(cid, CONST_SLOT_NECKLACE)
            if ammy then
                if ammy.itemid == bonusAmuletId then
                    rate = rate * 1.5 -- If he's wearing the amulet then give him 50% extra chance
                end
            end

If you don't get any errors in the console then use the function print() to find out where the problem is:
Code:
local bonusAmuletId = 11393
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) -- This will be (-1) since you're not using it so the addEvent will return 'rare' and not 's' [s == -1 and rate or s]
            local ammy = getPlayerSlotItem(cid, CONST_SLOT_NECKLACE)
            if ammy then
                print('Amulet found | Amulet ID: ' .. ammy.itemid) -- print
                if ammy.itemid == bonusAmuletId then
                    rate = rate * 1.5 -- If he's wearing the amulet then give him 50% extra chance
                end
            else
                print('Amulet not found') -- print
            end
            print('Rate: ' .. rate)
            addEvent(dropLoot, 0, getThingPos(target), v, s == -1 and rate or s, getCreatureMaster(target), cid, target)
        end
    end
    return true
end
 
Are you getting any errors in the console?

Double check the code in this area, since I'm not using 0.4 so I'm not quite sure if it's written correctly:
Code:
            local ammy = getPlayerSlotItem(cid, CONST_SLOT_NECKLACE)
            if ammy then
                if ammy.itemid == bonusAmuletId then
                    rate = rate * 1.5 -- If he's wearing the amulet then give him 50% extra chance
                end
            end

If you don't get any errors in the console then use the function print() to find out where the problem is:
Code:
local bonusAmuletId = 11393
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) -- This will be (-1) since you're not using it so the addEvent will return 'rare' and not 's' [s == -1 and rate or s]
            local ammy = getPlayerSlotItem(cid, CONST_SLOT_NECKLACE)
            if ammy then
                print('Amulet found | Amulet ID: ' .. ammy.itemid) -- print
                if ammy.itemid == bonusAmuletId then
                    rate = rate * 1.5 -- If he's wearing the amulet then give him 50% extra chance
                end
            else
                print('Amulet not found') -- print
            end
            print('Rate: ' .. rate)
            addEvent(dropLoot, 0, getThingPos(target), v, s == -1 and rate or s, getCreatureMaster(target), cid, target)
        end
    end
    return true
end

Bloody hell, thank you

Your code was printing:
Code:
Amulet found | Amulet ID: 11393
Rate: 1.5
Amulet found | Amulet ID: 0
Rate: 1.5

Thanks to imkingran i've fixed this and i would like to share to everybody
@Extrodus
You helps me, i would like to help you
It should be to useful to blazera.net:

Code:
local bonusAmuletId = 11393
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) -- This will be (-1) since you're not using it so the addEvent will return 'rare' and not 's' [s == -1 and rate or s]
            local ammy = getPlayerSlotItem(cid, CONST_SLOT_NECKLACE)
            local rate = getConfigInfo('rateLoot')
            if ammy.itemid ~= 0 then
                --print('Amulet found | Amulet ID: ' .. ammy.itemid) -- print
                if ammy.itemid == bonusAmuletId then
                    rate = rate * 1.5 -- If he's wearing the amulet then give him 50% extra chance
                end
            --else
            --    print('Amulet not found') -- print
            end
            --print('Rate: ' .. rate)
            addEvent(dropLoot, 0, getThingPos(target), v, s == -1 and rate or s, getCreatureMaster(target), cid, target)
        end
    end
    return true
end
 
@imkingran do you know how to remove percentage?
http://pastebin.com/BvnDST54

I mean
Code:
You see a bone club (Atk:12, Def:8).

Not more (its how working on script)
Code:
You see a deadly bone club (Atk:12 +5, Def:8). [Attack: +38%]

And be it (how i want)
Code:
You see a deadly bone club (Atk:12 +38, Def:8). [Attack: +38]

It's just change something here?
I think is here, but idk and i scare to do some shits
PHP:
                        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))
 
Didn't test buy try this: (edited from your pastebin link)

Code:
<?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, 0},
    attrNames = true, -- show attribute names instead of rare
    chance = {
        [1] = 10000,
        [2] = 5000 -- chance for 2nd stat
    }
}
tiers['epic'] = {
    color = 35,
    extra = {6, 8}, -- additional percent bonus
    chance = {
        [1] = 3333,
        [2] = 25000
    }
}
tiers['legendary'] = {
    color = 149,
    extra = {10, 20},
    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 = {25, 45},
    usePercents = true,
    types = {MELEE, DISTANCE, WAND}
}
attr['fortified'] = {
    attr = 'extraDefense',
    base = 'defense',
    name = 'Defense',
    percent = {25, 35},
    usePercents = true,   
    types = {MELEE, SHIELD}
}
attr['deadly'] = {
    attr = 'extraAttack',
    base = 'attack',
    name = 'Attack',
    types = {MELEE},
    usePercents = false,
    percent = {25, 50}
}
attr['strong'] = {
    attr = 'armor',
    name = 'Armor',
    percent = {25, 50},
    usePercents = true,
    types = {ARMOR}
}
attr['hawkeye\'s'] = {
    attr = 'hitChance',
    name = 'Hit Chance',
    percent = {35, 55},
    usePercents = true,
    types = {DISTANCE}
}
--[[ // not available without source edit
attr['farsight'] = {
    attr = 'shootRange',
    name = 'Shoot Range',
    percent = {10, 20},
    types = {DISTANCE, WAND}
}
]]
attr['charged'] = {
    attr = 'charges',
    name = 'Charges',
    percent = {40, 80},
    types = {CHARGES}
}
attr['divine'] = {
    attr = 'duration',
    name = 'Duration',
    percent = {40, 80},
    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 = n.usePercents and math.random(n.percent[1] + (v.extra[1] or 0), n.percent[2] + (v.extra[2] or 0)) or math.random(n.percent[1] + (v.extra[1] or 0), n.percent[2] + (v.extra[2] or 0)) * 100
                        -- 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
                        if n.usePercents then
                            doItemSetAttribute(tmpItem, 'description', '[' .. n.name .. ': +' .. percent .. '%]' .. (desc == '' and '' or '\n' .. desc))
                        else
                            doItemSetAttribute(tmpItem, 'description', '[' .. n.name .. ': +' .. percent .. ']' .. (desc == '' and '' or '\n' .. desc))
                        end
 
                        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>

I added the option usePercents = boolean (true/false) to the table so you can decide which one use % and which one doesn't.
Code:
attr['deadly'] = {
    attr = 'extraAttack',
    base = 'attack',
    name = 'Attack',
    types = {MELEE},
    usePercents = false,
    percent = {25, 50}
}
 
Didn't test buy try this: (edited from your pastebin link)

Code:
<?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, 0},
    attrNames = true, -- show attribute names instead of rare
    chance = {
        [1] = 10000,
        [2] = 5000 -- chance for 2nd stat
    }
}
tiers['epic'] = {
    color = 35,
    extra = {6, 8}, -- additional percent bonus
    chance = {
        [1] = 3333,
        [2] = 25000
    }
}
tiers['legendary'] = {
    color = 149,
    extra = {10, 20},
    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 = {25, 45},
    usePercents = true,
    types = {MELEE, DISTANCE, WAND}
}
attr['fortified'] = {
    attr = 'extraDefense',
    base = 'defense',
    name = 'Defense',
    percent = {25, 35},
    usePercents = true,  
    types = {MELEE, SHIELD}
}
attr['deadly'] = {
    attr = 'extraAttack',
    base = 'attack',
    name = 'Attack',
    types = {MELEE},
    usePercents = false,
    percent = {25, 50}
}
attr['strong'] = {
    attr = 'armor',
    name = 'Armor',
    percent = {25, 50},
    usePercents = true,
    types = {ARMOR}
}
attr['hawkeye\'s'] = {
    attr = 'hitChance',
    name = 'Hit Chance',
    percent = {35, 55},
    usePercents = true,
    types = {DISTANCE}
}
--[[ // not available without source edit
attr['farsight'] = {
    attr = 'shootRange',
    name = 'Shoot Range',
    percent = {10, 20},
    types = {DISTANCE, WAND}
}
]]
attr['charged'] = {
    attr = 'charges',
    name = 'Charges',
    percent = {40, 80},
    types = {CHARGES}
}
attr['divine'] = {
    attr = 'duration',
    name = 'Duration',
    percent = {40, 80},
    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 = n.usePercents and math.random(n.percent[1] + (v.extra[1] or 0), n.percent[2] + (v.extra[2] or 0)) or math.random(n.percent[1] + (v.extra[1] or 0), n.percent[2] + (v.extra[2] or 0)) * 100
                        -- 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
                        if n.usePercents then
                            doItemSetAttribute(tmpItem, 'description', '[' .. n.name .. ': +' .. percent .. '%]' .. (desc == '' and '' or '\n' .. desc))
                        else
                            doItemSetAttribute(tmpItem, 'description', '[' .. n.name .. ': +' .. percent .. ']' .. (desc == '' and '' or '\n' .. desc))
                        end
 
                        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>

I added the option usePercents = boolean (true/false) to the table so you can decide which one use % and which one doesn't.
Code:
attr['deadly'] = {
    attr = 'extraAttack',
    base = 'attack',
    name = 'Attack',
    types = {MELEE},
    usePercents = false,
    percent = {25, 50}
}

Wtf :O

Code:
03:44 You see an epic longsword (Atk:17 +952, Def:14).
It weighs 42.00 oz.
[Attack: +5600]
[Attack Speed: +48%]

03:44 You see an epic short sword (Atk:11, Def:11 +418).
It weighs 35.00 oz.
[Defense: +3800]
 
Wtf :O

Code:
03:44 You see an epic longsword (Atk:17 +952, Def:14).
It weighs 42.00 oz.
[Attack: +5600]
[Attack Speed: +48%]

03:44 You see an epic short sword (Atk:11, Def:11 +418).
It weighs 35.00 oz.
[Defense: +3800]

There is something wrong here:
Code:
                       if n.usePercents then
                            doItemSetAttribute(tmpItem, 'description', '[' .. n.name .. ': +' .. percent .. '%]' .. (desc == '' and '' or '\n' .. desc))
                        else
                            doItemSetAttribute(tmpItem, 'description', '[' .. n.name .. ': +' .. percent .. ']' .. (desc == '' and '' or '\n' .. desc))
                        end

I'm sorry, but i don't know how to fix
 
There is something wrong here:
Code:
                       if n.usePercents then
                            doItemSetAttribute(tmpItem, 'description', '[' .. n.name .. ': +' .. percent .. '%]' .. (desc == '' and '' or '\n' .. desc))
                        else
                            doItemSetAttribute(tmpItem, 'description', '[' .. n.name .. ': +' .. percent .. ']' .. (desc == '' and '' or '\n' .. desc))
                        end

I'm sorry, but i don't know how to fix

@imkingran know? anybody know?
 
Didn't test buy try this: (edited from your pastebin link)

Code:
<?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, 0},
    attrNames = true, -- show attribute names instead of rare
    chance = {
        [1] = 10000,
        [2] = 5000 -- chance for 2nd stat
    }
}
tiers['epic'] = {
    color = 35,
    extra = {6, 8}, -- additional percent bonus
    chance = {
        [1] = 3333,
        [2] = 25000
    }
}
tiers['legendary'] = {
    color = 149,
    extra = {10, 20},
    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 = {25, 45},
    usePercents = true,
    types = {MELEE, DISTANCE, WAND}
}
attr['fortified'] = {
    attr = 'extraDefense',
    base = 'defense',
    name = 'Defense',
    percent = {25, 35},
    usePercents = true,
    types = {MELEE, SHIELD}
}
attr['deadly'] = {
    attr = 'extraAttack',
    base = 'attack',
    name = 'Attack',
    types = {MELEE},
    usePercents = false,
    percent = {25, 50}
}
attr['strong'] = {
    attr = 'armor',
    name = 'Armor',
    percent = {25, 50},
    usePercents = true,
    types = {ARMOR}
}
attr['hawkeye\'s'] = {
    attr = 'hitChance',
    name = 'Hit Chance',
    percent = {35, 55},
    usePercents = true,
    types = {DISTANCE}
}
--[[ // not available without source edit
attr['farsight'] = {
    attr = 'shootRange',
    name = 'Shoot Range',
    percent = {10, 20},
    types = {DISTANCE, WAND}
}
]]
attr['charged'] = {
    attr = 'charges',
    name = 'Charges',
    percent = {40, 80},
    types = {CHARGES}
}
attr['divine'] = {
    attr = 'duration',
    name = 'Duration',
    percent = {40, 80},
    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 = n.usePercents and math.random(n.percent[1] + (v.extra[1] or 0), n.percent[2] + (v.extra[2] or 0)) or math.random(n.percent[1] + (v.extra[1] or 0), n.percent[2] + (v.extra[2] or 0)) * 100
                        -- 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
                        if n.usePercents then
                            doItemSetAttribute(tmpItem, 'description', '[' .. n.name .. ': +' .. percent .. '%]' .. (desc == '' and '' or '\n' .. desc))
                        else
                            doItemSetAttribute(tmpItem, 'description', '[' .. n.name .. ': +' .. percent .. ']' .. (desc == '' and '' or '\n' .. desc))
                        end
                        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>

I added the option usePercents = boolean (true/false) to the table so you can decide which one use % and which one doesn't.
Code:
attr['deadly'] = {
    attr = 'extraAttack',
    base = 'attack',
    name = 'Attack',
    types = {MELEE},
    usePercents = false,
    percent = {25, 50}
}



Wtf :O

Code:
03:44 You see an epic longsword (Atk:17 +952, Def:14).
It weighs 42.00 oz.
[Attack: +5600]
[Attack Speed: +48%]

03:44 You see an epic short sword (Atk:11, Def:11 +418).
It weighs 35.00 oz.
[Defense: +3800]

Why +952 atk, +418 def in epic items?
It shouldn't be 6~8

Like is in the script?
Code:
tiers['epic'] = {
    color = 35,
    extra = {6, 8}, -- additional percent bonus
    chance = {
        [1] = 3333,
        [2] = 25000
    }
}
 
bump it \/

Why +952 atk, +418 def in epic items?
It shouldn't be 6~8

Like is in the script?
Code:
tiers['epic'] = {
    color = 35,
    extra = {6, 8}, -- additional percent bonus
    chance = {
        [1] = 3333,
        [2] = 25000
    }
}
 
any one can help me to change chance for getPlayerStorageValue(cid, 170297) ?? work with player storage
change this
Code:
tiers['rare'] = {
    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
    }
}

to be like this
Code:
tiers['rare'] = {
    color = 66, -- color of 'RARE' text
    extra = {0, 0},
    attrNames = true, -- show attribute names instead of rare
    chance = {
        [1] = getPlayerStorageValue(cid, 170297),
        [2] = getPlayerStorageValue(cid, 170297)-- chance for 2nd stat
    }
}
anyone can help me to do it ?
use 0.4
 
any one can help me??
For a quick ugly test, add this
Code:
    tiers['rare'].chance[1] = getPlayerStorageValue(cid, 170297)
    tiers['rare'].chance[2] = getPlayerStorageValue(cid, 170297)
below line 158
 
For a quick ugly test, add this
Code:
    tiers['rare'].chance[1] = getPlayerStorageValue(cid, 170297)
    tiers['rare'].chance[2] = getPlayerStorageValue(cid, 170297)
below line 158
give me error <lua getcreaturestorage> creature not found
 
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['rare'] = {
    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['epic'] = {
    color = 35,
    extra = {7, 20}, -- additional percent bonus
    chance = {
        [1] = 3333,
        [2] = 25000
    }
}
tiers['legendary'] = {
    color = 149,
    extra = {20, 35},
    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, cid)
    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
    tiers['rare'].chance[1] = getPlayerStorageValue(cid, 170297)
    tiers['rare'].chance[2] = getPlayerStorageValue(cid, 170297)

    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, cid)
    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, cid)
        if( tmp )then
            if( isContainer(tmp) )then
                if( createChildLoot(tmp, i[k].child, ext, pos, cid) )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, cid)
                if( tmp )then
                    if( isContainer(tmp) )then
                        if( createChildLoot(tmp, v.loot[i].child, ext, pos, cid) )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>
try this
 
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['rare'] = {
    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['epic'] = {
    color = 35,
    extra = {7, 20}, -- additional percent bonus
    chance = {
        [1] = 3333,
        [2] = 25000
    }
}
tiers['legendary'] = {
    color = 149,
    extra = {20, 35},
    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, cid)
    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
    tiers['rare'].chance[1] = getPlayerStorageValue(cid, 170297)
    tiers['rare'].chance[2] = getPlayerStorageValue(cid, 170297)

    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, cid)
    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, cid)
        if( tmp )then
            if( isContainer(tmp) )then
                if( createChildLoot(tmp, i[k].child, ext, pos, cid) )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, cid)
                if( tmp )then
                    if( isContainer(tmp) )then
                        if( createChildLoot(tmp, v.loot[i].child, ext, pos, cid) )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>
try this
same error
 
Back
Top