• 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!

Lua increase loot chance in function

proudera

New Member
Joined
Jun 11, 2017
Messages
33
Reaction score
0
I'm using this script with tfs 1.2 client 10.99.

Code:
-- Random Stats 
local rare_popup = true
local rare_effect = true
local rare_effect_id = CONST_ME_STUN

-- Tiers
local tiers = {
   [1] = {
       prefix = 'rare',
       chance = {
           [1] = 10000, -- chance for basic stat
           [2] = 5000 -- chance for second stat
       }
   },

   [2] = {
       prefix = 'epic',
       chance = {
           [1] = 3333,
           [2] = 25000
       }
   },

   [3] = {
       prefix = 'legendary',
       chance = {
           [1] = 1000,
           [2] = 100000 -- Legendary rolls always have two affixes.
       }
   },
}

-- Attributes
local attr = {
   atk = {
       name = 'Attack',
       rare = {1, 3}, -- Customize roll numbers here
       epic = {4, 6},
       legendary = {7, 10},
   },
   def = {
       name = 'Defense',
       rare = {1, 2},
       epic = {3, 4},
       legendary = {5, 6},
   },
   extradef = {
       name = 'Extra Defense',
       rare = {1, 1},
       epic = {2, 3},
       legendary = {4, 5},
   },
   arm = {
       name = 'Armor',
       rare = {1, 1},
       epic = {2, 3},
       legendary = {4, 5},
   },
   hitchance = {
       name = 'Accuracy',
       rare = {1, 5},
       epic = {6, 10},
       legendary = {11, 15},
   },
   shootrange = {
       name = 'Range',
       rare = {1, 1},
       epic = {2, 2},
       legendary = {3, 3},
   },
   charges = {
       name = 'Charges',
       rare = {10, 20},
   },
   duration = {
       name = 'Time',
       rare = {10, 20},
   },

   -- Not available in 1.X
   -- attackSpeed = {},
   -- extraAttack = {},
}

local stats = {
   [1] = {ITEM_ATTRIBUTE_ATTACK, attr.atk},
   [2] = {ITEM_ATTRIBUTE_DEFENSE, attr.def},
   [3] = {ITEM_ATTRIBUTE_EXTRADEFENSE, attr.extradef},
   [4] = {ITEM_ATTRIBUTE_ARMOR, attr.arm},
   [5] = {ITEM_ATTRIBUTE_HITCHANCE, attr.hitchance},
   [6] = {ITEM_ATTRIBUTE_SHOOTRANGE, attr.shootrange},
   [7] = {ITEM_ATTRIBUTE_CHARGES, attr.charges},
   [8] = {ITEM_ATTRIBUTE_DURATION, attr.duration},
 
   -- Not available in 1.X
   -- [9] = {ITEM_ATTRIBUTE_ATTACKSPEED, attr.attackSpeed},
   -- [10] = {ITEM_ATTRIBUTE_EXTRAATTACK, attr.extraAttack},
}

function stat_getItemDuration(item)
   local it_id = item:getId()
   local tid = ItemType(it_id):getTransformEquipId()
   if tid > 0 then
       item:transform(tid)
       local vx = item:getAttribute(ITEM_ATTRIBUTE_DURATION)
       item:transform(it_id)
       item:removeAttribute(ITEM_ATTRIBUTE_DURATION)
       return vx
   end
   return 0
end

function loot_attrToVal(item, attr)
   local id = ItemType(item:getId())
   local v = {
       [ITEM_ATTRIBUTE_ATTACK] = id:getAttack(),
       [ITEM_ATTRIBUTE_DEFENSE] = id:getDefense(),
       [ITEM_ATTRIBUTE_EXTRADEFENSE] = id:getExtraDefense(),
       [ITEM_ATTRIBUTE_ARMOR] = id:getArmor(),
       [ITEM_ATTRIBUTE_HITCHANCE] = id:getHitChance(),
       [ITEM_ATTRIBUTE_SHOOTRANGE] = id:getShootRange(),
       [ITEM_ATTRIBUTE_CHARGES] = id:getCharges(),
       [ITEM_ATTRIBUTE_DURATION] = stat_getItemDuration(item),

       -- Not available in 1.X
       -- [ITEM_ATTRIBUTE_ATTACKSPEED] = item:getAttackSpeed(),
       -- [ITEM_ATTRIBUTE_EXTRAATTACK] = item:getExtraAttack(),
   }
   return v[attr]
end

function assign_loot_Stat(c)
   local rares = 0
   local h = c:getItemHoldingCount()
   if h > 0 then
       for i = 1, h do
           local available_stats = {}
           it_u = c:getItem(i - 1)
           it_id = ItemType(it_u:getId())
           if it_u:isContainer() then
               local crares = assign_loot_Stat(it_u)
               rares = rares + crares
           else
               if not it_id:isStackable() then
                   local wp = it_id:getWeaponType()
                   if wp > 0 then
                       if wp == WEAPON_SHIELD then
                           table.insert(available_stats, stats[2])
                       elseif wp == WEAPON_DISTANCE then -- type bow
                           table.insert(available_stats, stats[1])
                           table.insert(available_stats, stats[5])
                           table.insert(available_stats, stats[6])
                           -- Not available in 1.1
                           -- table.insert(available_stats, stats[9])
                       -- elseif wp == WEAPON_WAND then -- type wand
                           -- SHOOTRANGE doesn't seem to be working for wands
                           -- table.insert(available_stats, stats[6])
                           -- table.insert(available_stats, stats[9])
                       elseif isInArray({WEAPON_SWORD, WEAPON_CLUB, WEAPON_AXE}, wp) then -- melee weapon

                           if it_id:getAttack() > 0 then
                               table.insert(available_stats, stats[1])
                           end
             
                           if it_id:getDefense() > 0 then
                               table.insert(available_stats, stats[2])
                           end
             
                           if it_id:getExtraDefense() ~= 0 then
                               table.insert(available_stats, stats[3])
                           end
                           -- Not available in 1.1
                           -- table.insert(available_stats, stats[10])
                       end
                   else -- Armors, Amulets, Runes and Rings
                       if it_id:getArmor() > 0 then -- Ignore clothing
                           table.insert(available_stats, stats[4])
                       end

                       -- Rather not roll on consumables
                       -- if it_id:getCharges() > 0 then
                       --    table.insert(available_stats, stats[7])
                       -- end

                       -- Not working
                       -- local eq_id = it_id:getTransformEquipId()
                       -- if eq_id > 0 then
                       --   table.insert(available_stats, stats[8])
                       -- end
                   end
               end
           end

           if #available_stats > 0 then
               -- Skips it all if it's empty
               local tier = math.random(1, #tiers)
               if #tiers[tier].chance > 0 then
                   local statsStored = 0
                   local stats_used = {}
                   for stat = 1, #tiers[tier].chance do
                       if #available_stats > 0 then
                           -- Stops if no more stats available
                           if stat - 1 == statsStored then
                               -- Checks when it's time to stop adding stats
                               if math.random(1, 100000) <= tiers[tier].chance[stat] then
                                   statsStored = statsStored + 1

                                   local selected_stat = math.random(1, #available_stats)
                                   table.insert(stats_used, available_stats[selected_stat])
                                   table.remove(available_stats, selected_stat)
                               end
                           end
                       end
                   end

                   if #stats_used > 0 then
                       rares = rares + 1
                       local stat_desc = {}
                       for stat = 1, #stats_used do
         
                           if tiers[tier].prefix == tiers[3].prefix then
                               statmin = stats_used[stat][2].legendary[1]
                               statmax = stats_used[stat][2].legendary[2]
                           elseif tiers[tier].prefix == tiers[2].prefix then
                               statmin = stats_used[stat][2].epic[1]
                               statmax = stats_used[stat][2].epic[2]
                           else
                               statmin = stats_used[stat][2].rare[1]
                               statmax = stats_used[stat][2].rare[2]
                           end
             
                           local v = math.random(
                               statmin,
                               statmax
                           )
             
                           local basestat = loot_attrToVal(it_u, stats_used[stat][1])
                           it_u:setAttribute(stats_used[stat][1], basestat + v) -- Converted to static bonus
                           table.insert(stat_desc, '[' .. stats_used[stat][2].name .. ': +' .. v .. ']') -- Put roll results into a table
                       end
         
                       -- Replace article if item rolls as epic
                       if tiers[tier].prefix == "epic" then
                           it_u:setAttribute(ITEM_ATTRIBUTE_ARTICLE, "an")
                       end
                     
                       -- Add rarity prefix to item name
                       it_u:setAttribute(ITEM_ATTRIBUTE_NAME, tiers[tier].prefix .. " " .. it_id:getName())
         
                       -- It item has a description, retain it instead of over-writing it
                       if it_id:getDescription() == "" then
                           it_u:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, table.concat(stat_desc, "\n"))
                       else
                           it_u:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, table.concat(stat_desc, "\n")  .. "\n" .. it_id:getDescription())
                       end

                       -- Capitalize tier.prefix to be used for the animated text above corpses
                       rare_text = (tiers[tier].prefix:gsub("^%l", string.upper) .. "!")
         
                   end
               end
           end
       end
   end
   return rares
end

function find_loot_Container(pos)
   local rares = 0
   local c = Tile(pos):getTopDownItem()
   if c ~= nil then
       if c:isContainer() then
           rares = rares + assign_loot_Stat(c)
           if rares > 0 then -- If a rare item was rolled, play animation
               if rare_popup then
                   local spectators = Game.getSpectators(pos, false, true, 7, 7, 5, 5)
                   for i = 1, #spectators do
                       spectators[i]:say(rare_text, TALKTYPE_MONSTER_SAY, false, spectators[i], pos)
                   end
               end
               if rare_effect then
                   pos:sendMagicEffect(rare_effect_id)
               end
           end
           return true
       end
   end
end

function MonsterType.createLoot(self, corpse, modifier)
   if not corpse then
       return
   end
 
   -- Take lootrate into consideration
   if configManager.getNumber(configKeys.RATE_LOOT) == 0 then
       return
   end

   local owner = Player(corpse:getAttribute(ITEM_ATTRIBUTE_CORPSEOWNER))

   if not owner or owner:getStamina() > 840 then
 
       -- The code below caused loot to be added to corpses in the wrong order (gold at the end, items at the start)
       --
       --   for _, v in pairs(self:getLoot()) do
       --   itemList = self:createLootItem(v, modifier)
       --   if itemList then
       --        for _, item in ipairs(itemList) do
       --        print(item:getName())
       --           if item:isContainer() then
       --               local lootContainer = self:createLootContainer(item, v, modifier)
       --               if lootContainer then
       --                   corpse:addItemEx(item)
       --               end
       --           else
       --               corpse:addItemEx(item, 1)
       --           end
       --       end
       --   end
       --end
     
       -- Insert Loot into corpse, rewritten as per above.
       itemIndex = {}
       for _, v in pairs(self:getLoot()) do
           itemList = self:createLootItem(v, modifier)
           if itemList then
               for _, item in ipairs(itemList) do
                   table.insert(itemIndex, item)
               end
           end
       end
       for i = #itemIndex, 1, -1 do
           if itemIndex[i]:isContainer() then
               local lootContainer = self:createLootContainer(itemIndex[i], v, modifier)
               if lootContainer then
                   corpse:addItemEx(itemIndex[i])
               end
               else
                   corpse:addItemEx(itemIndex[i], 1)
           end
       end
 
     
       -- Apply rarity chance to corpse contents
       find_loot_Container(corpse:getPosition())
     
       -- Print loot list to Server Log
       if owner then
           local loot_msg = "Loot of " .. self:getNameDescription() .. ": " .. corpse:getContentDescription()

           if owner:getParty() then
               owner:getParty():broadcastLoot(loot_msg)
               owner:getParty():broadcastChannelLoot(loot_msg)
           else
               owner:sendTextMessage(MESSAGE_INFO_DESCR, loot_msg)
               -- Loot channel un-used on my server.
               -- owner:channelSay(nil, TALKTYPE_CHANNEL_O, loot_msg, 10)
           end
       end
   else
       local loot_msg = "Loot of " .. self:getNameDescription() .. ": nothing (due to low stamina)"

       if owner:getParty() then
           owner:getParty():broadcastLoot(loot_msg)
           owner:getParty():broadcastChannelLoot(loot_msg)
       else
           owner:sendTextMessage(MESSAGE_INFO_DESCR, loot_msg)
       end
   end

   corpse:decay()
end

function MonsterType.createLootItem(self, lootBlock, modifier)
   local lootTable = {}
   local itemCount = 0

   local randvalue = math.random(0, 100000) / (configManager.getNumber(configKeys.RATE_LOOT) * modifier);
   if randvalue < lootBlock.chance then
       if (ItemType(lootBlock.itemId):isStackable()) then
           itemCount = randvalue % lootBlock.maxCount + 1;
       else
           itemCount = 1;
       end
   end

   while (itemCount > 0) do
       local n = math.min(itemCount, 100)
       itemCount = itemCount - n

       local item = Game.createItem(lootBlock.itemId, n)

       if item then
           if lootBlock.subType ~= -1 then
               item:transform(lootBlock.itemId, lootBlock.subType)
           end

           if lootBlock.actionId ~= -1 then
               item:setActionId(lootBlock.actionId)
           end

           if lootBlock.text and lootBlock.text ~= "" then
               item:setAttribute(ITEM_ATTRIBUTE_TEXT, lootBlock.text)
           end

           table.insert(lootTable, item)
       end
   end

   return #lootTable == 0 and nil or lootTable
end

function MonsterType.createLootContainer(self, parent, lootBlock, modifier)
   if #lootBlock.childLoot == 0 then
       return true
   end

   for _, v in pairs(lootBlock.childLoot) do
       if parent:getSize() < parent:getCapacity() then
           local itemList = self:createLootItem(v, modifier)
           if itemList then
               for _, item in ipairs(itemList) do
                   if item:isContainer() then
                       local lootContainer = self:createLootContainer(item, v, modifier)
                       if lootContainer then
                           parent:addItemEx(item)
                       end
                   else
                       parent:addItem(item, 1)
                   end
               end
           end
       end
   end

   return #parent > 0
end

I can't find a way to increase the loot chance of a rare/epic/legendary item.
 
Last edited:
seems that this line is about the chance
Code:
if math.random(1, 100000) <= tiers[tier].chance[stat] then
you need check the tables with the tiers..
 
Alright, if I would do:

Code:
if math.random(1, 10000) <= tiers[tier].chance[stat] then

I would have 10% chance to loot that item?
 
Last edited:
if math.random(1, 100000) <= 1000 then <- 1% chance
if math.random(1, 100000) <= 10000 then <- 10% chance
100 000 = 100%
50 000 = 50%
10 000 = 10%
1000 = 1%
 
I still have a question remaining:

If I have:

Code:
-- Tiers
local tiers = {
   [1] = {
       prefix = 'rare',
       chance = {
           [1] = 100000, -- chance for basic stat
           [2] = 5000 -- chance for second stat
       }
   },

   [2] = {
       prefix = 'epic',
       chance = {
           [1] = 3333,
           [2] = 25000
       }
   },

   [3] = {
       prefix = 'legendary',
       chance = {
           [1] = 1000,
           [2] = 100000 -- Legendary rolls always have two affixes.
       }
   },
}

and:

Code:
if math.random(1, 100000) <= tiers[tier].chance[stat] then

I'm not always getting a rare item, is it first randomly choosing between rare, epic and legendary?

but if I do:

Code:
if math.random(1, 100000) <= 10000 then

I always get rare, epic or legendary.

I think I see:

Code:
local tier = math.random(1, #tiers)
if #tiers[tier].chance > 0 then

So it's first checking between rare, epic and legendary

Anyone can tell me?
 
Last edited by a moderator:
I still have a question remaining:

If I have:

Code:
-- Tiers
local tiers = {
   [1] = {
       prefix = 'rare',
       chance = {
           [1] = 100000, -- chance for basic stat
           [2] = 5000 -- chance for second stat
       }
   },

   [2] = {
       prefix = 'epic',
       chance = {
           [1] = 3333,
           [2] = 25000
       }
   },

   [3] = {
       prefix = 'legendary',
       chance = {
           [1] = 1000,
           [2] = 100000 -- Legendary rolls always have two affixes.
       }
   },
}

and:

Code:
if math.random(1, 100000) <= tiers[tier].chance[stat] then

I'm not always getting a rare item, is it first randomly choosing between rare, epic and legendary?

but if I do:

Code:
if math.random(1, 100000) <= 10000 then

I always get rare, epic or legendary.

I think I see:

Code:
local tier = math.random(1, #tiers)
if #tiers[tier].chance > 0 then

So it's first checking between rare, epic and legendary

Anyone can tell me?


We can help you when you start following the rules.
 
Back
Top