[Mod] Random Item Stats

Discussion in 'Mods & Lua Functions' started by Cykotitan, Jun 1, 2011.

  1. Cykotitan

    Cykotitan Experienced G'

    Joined:
    Nov 4, 2008
    Messages:
    16,897
    A friendly warning: This mod most likely WON'T work on your 9.X server in case you're sticking to TFS 0.2.* aka Mystic Spirit - the required (0.3/0.4) functions are not available even if mod support was present

    I got the idea from http://otland.net/f485/random-item-stats-creatures-can-drop-boosted-items-111028/ which means devianceone gets the most of credit.

    Its based on my old loot ring script, modified to allow effects to be shown

    Installation is straightforward, create new .xml file in /mods folder with contents below:
    [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, true)
    end
    return ret
    end

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

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

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

    if( not tmpItem )then
    return
    end

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

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

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

    local ret, done

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

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

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

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

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

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

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

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

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

    return tmpItem, ret
    end

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

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

    return size > 0
    end

    local function dropLoot(pos, v, ext, master, cid, target)
    local corpse
    if( not master or master == target )then -- 0.3/4
    corpse = getTileItemById(pos, v.lookCorpse).uid
    if( isContainer(corpse) )then
    for i = 1, getContainerSize(corpse) do
    doRemoveItem(getContainerItem(corpse, 0).uid)
    end
    local size, cap = 0, getContainerCap(corpse)
    for i = 1, #v.loot do
    if( size == cap )then
    break
    end
    local tmp, ret = createLoot(v.loot, ext)
    if( tmp )then
    if( isContainer(tmp) )then
    if( createChildLoot(tmp, v.loot.child, ext, pos) )then
    doAddContainerItemEx(corpse, tmp)
    size = size + 1
    else
    doRemoveItem(tmp)
    end
    else
    if( ret )then
    doSendMagicEffect(pos, CONST_ME_MAGIC_GREEN)
    doSendAnimatedText(pos, ret:upper(), tiers[ret].color)
    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>[/lua]
    Post bugs and stuff here so they can get resolved asap

    Resources:
     
    Last edited: Aug 8, 2013
  2. Nyan Cat

    Nyan Cat ForgottenL.se

    Joined:
    Jul 26, 2010
    Messages:
    1,170
    Nice!
     
    Death Enforcer and XouruS like this.
  3. Bogart

    Bogart ...has super panda powers

    Joined:
    Jun 21, 2009
    Messages:
    7,743
    veri najs
     
    Beerboy and XouruS like this.
  4. XouruS

    XouruS Active Member

    Joined:
    Dec 29, 2009
    Messages:
    941
    awsome.
     
    Death Enforcer and Nyan Cat like this.
  5. VirrageS

    VirrageS ←•†ĿuĀ && ©¤¤•→

    Joined:
    May 30, 2010
    Messages:
    985
    Very nice master. ^_^

    But what if someone use yours loot ring and also want to use it??
     
  6. Summ

    Summ (\/)(;,,;)(\/) Y not? Staff Member Global Moderator

    Joined:
    Oct 15, 2008
    Messages:
    4,144
    Nice release. To bad I already made this half a year for own purposes :/

    => You must spread some Reputation around before giving it to Cykotitan again.
     
    Redleaf likes this.
  7. Derek Boom Boom

    Derek Boom Boom *

    Joined:
    Jun 11, 2010
    Messages:
    392
    Seems halfway legit.
     
  8. freshpro

    freshpro Banned User

    Joined:
    Jan 5, 2011
    Messages:
    246
    I love you :U
     
  9. Pyromental

    Pyromental ?

    Joined:
    Aug 1, 2009
    Messages:
    118
    crashed my ots xd
     
    Cykotitan and Bogart like this.
  10. Cykotitan

    Cykotitan Experienced G'

    Joined:
    Nov 4, 2008
    Messages:
    16,897
    be careful when adding it (don't add it mindlessly) and take some time to tweak the percent or chances before enabling the mod because the values i provided probably aren't ideal for every server
    !!
     
    Beerboy likes this.
  11. Exedion

    Exedion Member

    Joined:
    Jun 11, 2007
    Messages:
    621
    Is rare see scripts releases from cyko, the end of the world is near! thanks cyko :)
     
  12. Derek Boom Boom

    Derek Boom Boom *

    Joined:
    Jun 11, 2010
    Messages:
    392
    He wanted to take achievment inb4 world's end ^^.
     
  13. Mendela

    Mendela New Member

    Joined:
    Jun 1, 2011
    Messages:
    18
    very lovely thax man
     
  14. Lessaire

    Lessaire Omniscient Hypervisor

    Joined:
    Dec 29, 2009
    Messages:
    610
    Thank you for your continued contributions to our community good sir.
     
  15. Evil Puncker

    Evil Puncker I'd rather kiss a rattlesnake

    Joined:
    May 30, 2009
    Messages:
    5,372
    I love u cyko (no homo)
     
  16. God Nixez

    God Nixez Member

    Joined:
    Sep 20, 2009
    Messages:
    356
    Love ya heheh u released it :O xD
     
  17. Knight God

    Knight God Active Member

    Joined:
    Oct 19, 2008
    Messages:
    1,182
    awesome :DDDDDDDD
     
  18. God Nixez

    God Nixez Member

    Joined:
    Sep 20, 2009
    Messages:
    356
    works i guess ;d btw where have u been ;o
     
  19. Kudzu

    Kudzu Active Member

    Joined:
    Apr 9, 2008
    Messages:
    512
    Cool! Testing

    Edit: 0.4 DEV a lot of errors,spam ;s
     
    Last edited: Jun 2, 2011
  20. God Nixez

    God Nixez Member

    Joined:
    Sep 20, 2009
    Messages:
    356
    i tested it on 0.4 rev 3777 and it works as a knife in a cake 100% stable no crashes no bugs no errors ~
     

Share This Page