[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
    Likes Received:
    809
    Best Answers:
    1
    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:
    Code (Lua):
    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <mod name="Random Item Stats" enabled="1">
    3. <config name="itemstats_conf"><![CDATA[
    4. -- //
    5.     extra_loot_key = 123 --: optional storage for higher loot rate
    6.     vocation_base_attackspeed = getVocationInfo(1).attackSpeed --: used for attackSpeed stat
    7. -- //
    8.  
    9. tiers, attr = {}, {}
    10.  
    11. tiers['rare'] = {
    12.     color = 66, -- color of 'RARE' text
    13.     extra = {0, 0},
    14.     attrNames = true, -- show attribute names instead of rare
    15.     chance = {
    16.         [1] = 10000,
    17.         [2] = 5000 -- chance for 2nd stat
    18.     }
    19. }
    20. tiers['epic'] = {
    21.     color = 35,
    22.     extra = {7, 20}, -- additional percent bonus
    23.     chance = {
    24.         [1] = 3333,
    25.         [2] = 25000
    26.     }
    27. }
    28. tiers['legendary'] = {
    29.     color = 149,
    30.     extra = {20, 35},
    31.     chance = {
    32.         [1] = 1000,
    33.         [2] = 100000 -- 2 bonuses always
    34.     }
    35. }
    36.  
    37. MELEE = 0
    38. DISTANCE = 1
    39. ARMOR = 2
    40. SHIELD = 3
    41. WAND = 4
    42. DURATION_RING = 5
    43. CHARGES = 6
    44.  
    45. --! attributes
    46. attr['quick'] = {
    47.     attr = 'attackSpeed',
    48.     name = 'Attack Speed',
    49.     percent = {6, 20},
    50.     types = {MELEE, DISTANCE, WAND}
    51. }
    52. attr['fortified'] = {
    53.     attr = 'extraDefense',
    54.     base = 'defense',
    55.     name = 'Defense',
    56.     percent = {7, 25},
    57.     types = {MELEE, SHIELD}
    58. }
    59. attr['deadly'] = {
    60.     attr = 'extraAttack',
    61.     base = 'attack',
    62.     name = 'Attack',
    63.     types = {MELEE},
    64.     percent = {7, 25}
    65. }
    66. attr['strong'] = {
    67.     attr = 'armor',
    68.     name = 'Armor',
    69.     percent = {7, 20},
    70.     types = {ARMOR}
    71. }
    72. attr['hawkeye\'s'] = {
    73.     attr = 'hitChance',
    74.     name = 'Hit Chance',
    75.     percent = {10, 25},
    76.     types = {DISTANCE}
    77. }
    78. --[[ // not available without source edit
    79. attr['farsight'] = {
    80.     attr = 'shootRange',
    81.     name = 'Shoot Range',
    82.     percent = {17, 34},
    83.     types = {DISTANCE, WAND}
    84. }
    85. ]]
    86. attr['charged'] = {
    87.     attr = 'charges',
    88.     name = 'Charges',
    89.     percent = {30, 45},
    90.     types = {CHARGES}
    91. }
    92. attr['divine'] = {
    93.     attr = 'duration',
    94.     name = 'Duration',
    95.     percent = {35, 50},
    96.     types = {DURATION_RING}
    97. }
    98. --/ attributes
    99.  
    100. rate = getConfigInfo('rateLoot')
    101.  
    102. if( getConfigInfo('monsterLootMessage') ~= 0 )then
    103.     print('[Notice] Set monsterLootMessage = 0 to prevent duplicate loot messages')
    104. end
    105. ]]></config>
    106.  
    107. <event type="kill" name="itemstats" event="script"><![CDATA[
    108. domodlib('itemstats_conf')
    109.  
    110. function round(n, s)
    111.     return tonumber(('%.' .. (s or 0) .. 'f'):format(n))
    112. end
    113.  
    114. function getContentDescription(uid, sep)
    115.     local ret, i, containers = '', 0, {}
    116.     while( i < getContainerSize(uid) )do
    117.         local v, s = getContainerItem(uid, i), ''
    118.         local k = getItemInfo(v.itemid)
    119.         k.name = getItemAttribute(v.uid, 'name') or k.name
    120.         if( k.name ~= '' )then
    121.             if( v.type > 1 and k.stackable and k.showCount )then
    122.                 s = v.type .. ' ' .. k.plural
    123.             else
    124.                 local article = getItemAttribute(v.uid, 'article') or k.article
    125.                 s = (article == '' and '' or article .. ' ') .. k.name
    126.             end
    127.             ret = ret .. (i == 0 and not sep and '' or ', ') .. s
    128.             if( isContainer(v.uid) and getContainerSize(v.uid) ~= 0 )then
    129.                 table.insert(containers, v.uid)
    130.             end
    131.         else
    132.             ret = ret .. (i == 0 and not sep and '' or ', ') .. 'an item of type ' .. v.itemid .. ', please report it to gamemaster'
    133.         end
    134.         i = i + 1
    135.     end
    136.     for i = 1, #containers do
    137.         ret = ret .. getContentDescription(containers[i], true)
    138.     end
    139.     return ret
    140. end
    141.  
    142. local function send(cid, corpse, monster)
    143.     if( isPlayer(cid) )then
    144.         local ret = corpse and isContainer(corpse) and getContentDescription(corpse)
    145.         doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, 'Loot of ' .. monster .. ': ' .. (ret ~= '' and ret or 'nothing'))
    146.         local party = getPlayerParty(cid)
    147.         if( party )then
    148.             for _, pid in ipairs(getPartyMembers(party)) do
    149.                 doPlayerSendChannelMessage(pid, '', 'Loot of ' .. monster .. ': ' .. (ret ~= '' and ret or 'nothing'), TALKTYPE_CHANNEL_W, CHANNEL_PARTY)
    150.             end
    151.         end
    152.     end
    153. end
    154.  
    155. local function createLoot(i, ext)
    156.     local item = type(i.id) == 'table' and i.id[math.random(#i.id)] or i.id
    157.     local random = math.ceil(math.random(100000) / ext)
    158.     local tmpItem, f
    159.  
    160.     if( random < i.chance )then
    161.         if i.subType == -1 then
    162.             f = getItemInfo(item)
    163.         end
    164.         tmpItem = doCreateItemEx(item,
    165.             i.subType ~= -1 and i.subType or
    166.             f.stackable and random % i.count + 1 or
    167.             f.charges ~= 0 and f.charges or
    168.             1
    169.         )
    170.     end
    171.  
    172.     if( not tmpItem )then
    173.         return
    174.     end
    175.  
    176.     if( i.actionId ~= -1 )then
    177.         doItemSetAttribute(tmpItem, 'aid', i.actionId)
    178.     end
    179.  
    180.     if( i.uniqueId ~= -1 )then
    181.         doItemSetAttribute(tmpItem, 'uid', i.uniqueId)
    182.     end
    183.  
    184.     if( i.text ~= '' )then
    185.         doItemSetAttribute(tmpItem, 'text', i.text)
    186.     end
    187.  
    188.     local ret, done
    189.  
    190.     for k, v in pairs(tiers) do
    191.         local cur, used = {}, {}
    192.         for i = 1, #v.chance do
    193.             if( math.random(100000) <= v.chance[i] )then
    194.                 if( f )then
    195.                     f = getItemInfo(item)
    196.                 end
    197.                 if( not f.stackable )then
    198.                     for m, n in pairs(attr) do
    199.                         if( not table.find(used, m) and
    200.                         (
    201.                             ( table.find(n.types, MELEE) and table.find({WEAPON_SWORD, WEAPON_CLUB, WEAPON_AXE}, f.weaponType) ) or
    202.                             ( table.find(n.types, DISTANCE) and f.weaponType == WEAPON_DIST and f.ammoType ~= 0 ) or
    203.                             ( table.find(n.types, ARMOR) and f.armor ~= 0 and f.wieldPosition ~= CONST_SLOT_NECKLACE ) or
    204.                             ( table.find(n.types, SHIELD) and f.defense ~= 0 and f.weaponType == WEAPON_SHIELD ) or
    205.                             ( table.find(n.types, WAND) and f.weaponType == WEAPON_WAND ) or
    206.                             ( table.find(n.types, DURATION_RING) and f.wieldPosition == CONST_SLOT_RING and f.transformEquipTo ~= 0 ) or
    207.                             ( table.find(n.types, CHARGES) and table.find({CONST_SLOT_RING, CONST_SLOT_NECKLACE}, f.wieldPosition) and f.charges ~= 0 )
    208.                         ) )then
    209.                             table.insert(cur, m)
    210.                         end
    211.                     end
    212.  
    213.                     if( #cur ~= 0 )then
    214.                         local n = cur[math.random(#cur)]
    215.                         table.insert(used, n)
    216.  
    217.                         n = attr[n]
    218.                         local percent, new, tmp = math.random(n.percent[1] + (v.extra[1] or 0), n.percent[2] + (v.extra[2] or 0))
    219.                         -- hacks
    220.                         if( n.attr == 'duration' )then
    221.                             tmp = getItemInfo(f.transformEquipTo)
    222.                             if tmp.transformDeEquipTo ~= item then
    223.                                 break
    224.                             end
    225.                             new = round( tmp.decayTime * (1 + percent / 100) * 1000 )
    226.                         elseif( n.attr == 'attackSpeed' )then
    227.                             new = round( vocation_base_attackspeed / (1 + percent / 100) )
    228.                         elseif( n.attr == 'hitChance' ) then
    229.                             new = round(
    230.                                 f.hitChance == -1 and
    231.                                     percent
    232.                                 or
    233.                                     f.hitChance * (1 + percent / 100)
    234.                             )
    235.                         else
    236.                             new = round(
    237.                                 n.base and
    238.                                     f[n['attr']] + f[n['base']] * (percent / 100)
    239.                                 or
    240.                                     f[n['attr']] * (1 + percent / 100)
    241.                             )
    242.  
    243.                             if( new == f[n[n.base and 'base' or 'attr']] )then -- no improvement
    244.                                 break
    245.                             end
    246.                         end
    247.  
    248.                         doItemSetAttribute(tmpItem, n.attr:lower(), new)
    249.  
    250.                         local name = getItemAttribute(tmpItem, 'name')
    251.                         if( v.attrNames or not name )then
    252.                             local name = (v.attrNames and used[#used] or k) .. ' ' .. (name or f.name)
    253.                             doItemSetAttribute(tmpItem, 'name', name)
    254.  
    255.                             if( f.article ~= '' )then
    256.                                 local article = getArticle(name)
    257.                                 if( article ~= f.article )then
    258.                                     doItemSetAttribute(tmpItem, 'article', article)
    259.                                 end
    260.                             end
    261.                         end
    262.  
    263.                         local desc = getItemAttribute(tmpItem, 'description') or f.description
    264.                         doItemSetAttribute(tmpItem, 'description', '[' .. n.name .. ': +' .. percent .. '%]' .. (desc == '' and '' or '\n' .. desc))
    265.  
    266.                         ret = k
    267.                     end
    268.                     cur = {}
    269.                     if( #v.chance == i )then
    270.                         done = true
    271.                     end
    272.                 end
    273.             else
    274.                 done = i ~= 1
    275.                 break
    276.             end
    277.         end
    278.         if( done )then
    279.             break
    280.         end
    281.     end
    282.  
    283.     return tmpItem, ret
    284. end
    285.  
    286. local function createChildLoot(parent, i, ext, pos)
    287.     if( not i or #i == 0 )then
    288.         return true
    289.     end
    290.  
    291.     local size, cap = 0, getContainerCap(parent)
    292.     for k = 1, #i do
    293.         if( size == cap )then
    294.             break
    295.         end
    296.         local tmp, ret = createLoot(i[k], ext)
    297.         if( tmp )then
    298.             if( isContainer(tmp) )then
    299.                 if( createChildLoot(tmp, i[k].child, ext, pos) )then
    300.                     doAddContainerItemEx(parent, tmp)
    301.                     size = size + 1
    302.                 else
    303.                     doRemoveItem(tmp)
    304.                 end
    305.             else
    306.                 if( ret )then
    307.                     doSendMagicEffect(pos, CONST_ME_MAGIC_GREEN)
    308.                     doSendAnimatedText(pos, ret:upper(), tiers[ret].color)
    309.                 end
    310.                 doAddContainerItemEx(parent, tmp)
    311.                 size = size + 1
    312.             end
    313.         end
    314.     end
    315.  
    316.     return size > 0
    317. end
    318.  
    319. local function dropLoot(pos, v, ext, master, cid, target)
    320.     local corpse
    321.     if( not master or master == target )then -- 0.3/4
    322.         corpse = getTileItemById(pos, v.lookCorpse).uid
    323.         if( isContainer(corpse) )then
    324.             for i = 1, getContainerSize(corpse) do
    325.                 doRemoveItem(getContainerItem(corpse, 0).uid)
    326.             end
    327.             local size, cap = 0, getContainerCap(corpse)
    328.             for i = 1, #v.loot do
    329.                 if( size == cap )then
    330.                     break
    331.                 end
    332.                 local tmp, ret = createLoot(v.loot[i], ext)
    333.                 if( tmp )then
    334.                     if( isContainer(tmp) )then
    335.                         if( createChildLoot(tmp, v.loot[i].child, ext, pos) )then
    336.                             doAddContainerItemEx(corpse, tmp)
    337.                             size = size + 1
    338.                         else
    339.                             doRemoveItem(tmp)
    340.                         end
    341.                     else
    342.                         if( ret )then
    343.                             doSendMagicEffect(pos, CONST_ME_MAGIC_GREEN)
    344.                             doSendAnimatedText(pos, ret:upper(), tiers[ret].color)
    345.                         end
    346.                         doAddContainerItemEx(corpse, tmp)
    347.                         size = size + 1
    348.                     end
    349.                 end
    350.             end
    351.         end
    352.     end
    353.     send(cid, corpse, v.description)
    354. end
    355.  
    356. function onKill(cid, target, damage, flags)
    357.     if( (damage == true or bit.band(flags, 1) == 1) and isMonster(target) )then -- 0.3/4
    358.         local v = getMonsterInfo(getCreatureName(target))
    359.         if( v and v.lookCorpse ~= 0 )then
    360.             local s = getCreatureStorage(cid, extra_loot_key)
    361.             addEvent(dropLoot, 0, getThingPos(target), v, s == -1 and rate or s, getCreatureMaster(target), cid, target)
    362.         end
    363.     end
    364.     return true
    365. end
    366. ]]></event>
    367.  
    368. <event type="login" name="itemstats_login" event="buffer"><![CDATA[
    369.     registerCreatureEvent(cid, 'itemstats')
    370. ]]></event>
    371.  
    372. </mod>
    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
    Likes Received:
    66
    Best Answers:
    0
    Nice!
     
    Death Enforcer and XouruS like this.
  3. Bogart

    Bogart ...has super panda powers

    Joined:
    Jun 21, 2009
    Messages:
    7,780
    Likes Received:
    421
    Best Answers:
    6
    veri najs
     
    Beerboy and XouruS like this.
  4. XouruS

    XouruS Active Member

    Joined:
    Dec 29, 2009
    Messages:
    941
    Likes Received:
    39
    Best Answers:
    0
    awsome.
     
    Death Enforcer and Nyan Cat like this.
  5. VirrageS

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

    Joined:
    May 30, 2010
    Messages:
    984
    Likes Received:
    58
    Best Answers:
    0
    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,134
    Likes Received:
    1,036
    Best Answers:
    2
    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
    Likes Received:
    13
    Best Answers:
    0
    Seems halfway legit.
     
  8. freshpro

    freshpro Banned User

    Joined:
    Jan 5, 2011
    Messages:
    246
    Likes Received:
    1
    Best Answers:
    0
    I love you :U
     
  9. Pyromental

    Pyromental ?

    Joined:
    Aug 1, 2009
    Messages:
    118
    Likes Received:
    22
    Best Answers:
    1
    crashed my ots xd
     
    Cykotitan and Bogart like this.
  10. Cykotitan

    Cykotitan Experienced G'

    Joined:
    Nov 4, 2008
    Messages:
    16,897
    Likes Received:
    809
    Best Answers:
    1
    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 Active Member

    Joined:
    Jun 11, 2007
    Messages:
    622
    Likes Received:
    25
    Best Answers:
    0
    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
    Likes Received:
    13
    Best Answers:
    0
    He wanted to take achievment inb4 world's end ^^.
     
  13. Mendela

    Mendela New Member

    Joined:
    Jun 1, 2011
    Messages:
    18
    Likes Received:
    0
    Best Answers:
    0
    very lovely thax man
     
  14. Lessaire

    Lessaire Omniscient Hypervisor

    Joined:
    Dec 29, 2009
    Messages:
    610
    Likes Received:
    36
    Best Answers:
    0
    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,366
    Likes Received:
    1,593
    Best Answers:
    1
    I love u cyko (no homo)
     
  16. God Nixez

    God Nixez Member

    Joined:
    Sep 20, 2009
    Messages:
    375
    Likes Received:
    13
    Best Answers:
    0
    Love ya heheh u released it :O xD
     
  17. Knight God

    Knight God Active Member

    Joined:
    Oct 19, 2008
    Messages:
    1,180
    Likes Received:
    20
    Best Answers:
    0
    awesome :DDDDDDDD
     
  18. God Nixez

    God Nixez Member

    Joined:
    Sep 20, 2009
    Messages:
    375
    Likes Received:
    13
    Best Answers:
    0
    works i guess ;d btw where have u been ;o
     
  19. Kudzu

    Kudzu Active Member

    Joined:
    Apr 9, 2008
    Messages:
    512
    Likes Received:
    37
    Best Answers:
    0
    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:
    375
    Likes Received:
    13
    Best Answers:
    0
    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

Loading...