Advertisement
Guest User

Untitled

a guest
Dec 4th, 2016
223
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 9.76 KB | None | 0 0
  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 = {6, 8}, -- additional percent bonus
  23. chance = {
  24. [1] = 3333,
  25. [2] = 25000
  26. }
  27. }
  28. tiers['legendary'] = {
  29. color = 149,
  30. extra = {10, 20},
  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 = {25, 45},
  50. types = {MELEE, DISTANCE, WAND}
  51. }
  52. attr['fortified'] = {
  53. attr = 'extraDefense',
  54. base = 'defense',
  55. name = 'Defense',
  56. percent = {25, 35},
  57. types = {MELEE, SHIELD}
  58. }
  59. attr['deadly'] = {
  60. attr = 'extraAttack',
  61. base = 'attack',
  62. name = 'Attack',
  63. types = {MELEE},
  64. percent = {25, 50}
  65. }
  66. attr['strong'] = {
  67. attr = 'armor',
  68. name = 'Armor',
  69. percent = {25, 50},
  70. types = {ARMOR}
  71. }
  72. attr['hawkeye\'s'] = {
  73. attr = 'hitChance',
  74. name = 'Hit Chance',
  75. percent = {35, 55},
  76. types = {DISTANCE}
  77. }
  78. --[[ // not available without source edit
  79. attr['farsight'] = {
  80. attr = 'shootRange',
  81. name = 'Shoot Range',
  82. percent = {10, 20},
  83. types = {DISTANCE, WAND}
  84. }
  85. ]]
  86. attr['charged'] = {
  87. attr = 'charges',
  88. name = 'Charges',
  89. percent = {40, 80},
  90. types = {CHARGES}
  91. }
  92. attr['divine'] = {
  93. attr = 'duration',
  94. name = 'Duration',
  95. percent = {40, 80},
  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>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement