Module:DropTable

local p = {}

function p.main(frame) if frame == mw.getCurrentFrame then args = require('Module:ProcessArgs').merge(true) else frame = mw.getCurrentFrame end

-- If the user defined "args.ShowRecipes" we will show Recipe Items in the table, otherwise we wont. showRecipes = args.ShowRecipes or nil

dlc = getDLCArgs(frame) or ''

-- create the base html holder local html = mw.html.create

-- display the title of the drop table html:tag('b'):wikitext(args.Title):tag('br'):done

locArgs = args.Location or args.Locations or frame:getParent.args.Location or frame:getParent.args.Locations or '' if (args.Type == 'LootContainer') then local locations = '' local locTbl = split(locArgs, ',') for i, v in ipairs(locTbl) do           if (i == #locTbl and i > 1) then locations = locations .. ' and ' elseif locations ~= '' then locations = locations .. ', ' end locations = locations ..  .. v ..  end html:tag('ul') :tag('li'):wikitext(frame:preprocess('Can be found on containers at ' .. locations)) end

-- calculate the average number of rolls, if applicable avgRolls = 1 if (args.Random ~= nil) then local rollsText = '' if (args.MinRolls == args.MaxRolls) then avgRolls = tonumber(args.MinRolls) -- min rolls and max rolls are same value, so only display one number. rollsText = args.MinRolls else avgRolls = (tonumber(args.MinRolls) + tonumber(args.MaxRolls)) / 2 -- calculate the average num of rolls rollsText = string.format('%s to %s', args.MinRolls, args.MaxRolls) -- display "x to y" rolls end html:wikitext(frame:preprocess(string.format('This drop table will be rolled  %s  times.', rollsText))) end

-- create the actual table local table = html:tag('table'):addClass('wikitable sortable'):attr({        align = 'center', border = '1', cellspacing = '0', cellpadding = '2'     }):cssText('border-collapse: collapse; border: 1px solid darkgray; empty-cells: show; text-align: center')

-- header row local headerRow = table:tag('tr') headerRow:tag('th'):wikitext('Items')

if (args.Random ~= nil) then table:addClass('randomDropTable') local suffix = avgRolls > 1 and " *" or "" headerRow:tag('th'):wikitext('Chance' .. suffix) else table:addClass('guaranteedDropTable') end -- iterate all unnamed arguments of the frame by using ipairs(args) and storing into a table -- this allows us to define an unlimited number of drop table entries, without having to worry about how many there are. local ip = {} for k, v in ipairs(args) do       ip[k] = v    end

-- little helper to display "an average" on the hover-text when needed local pretext = '' if (args.Random ~= nil and args.MinRolls ~= args.MaxRolls) then pretext = 'an average ' end

-- display the "empty drop" entry if (args.NoDrop ~= nil and tonumber(args.NoDrop) > 0) then -- display the chance per roll, and the alt-text for it       local td = table:tag('tr'):tag('td'):wikitext('Empty drop'):tag('td'):attr('data-sort-value', 101):tag('span'):attr('title', 'Empty drop chance per roll'):wikitext(args.NoDrop .. '%') end

-- iterate over our ipairs(args) table, counting up by 4. this is because each drop table entry takes 4 unnamed arguments -- 1: Name, 2: minQty, 3: maxQty, 4: Chance -- note: even guaranteed tables need to have chance defined, otherwise this becomes too messy. just set any value (even nil) for i = 1, #ip, 4 do       local item = ip[i] local minqty = ip[i + 1] local maxqty = ip[i + 2]

-- figure out if we should actually display this (for showrecipes) local show = true if (showRecipes == nil and (string.find(item, 'Cooking:', 1, true) ~= nil or string.find(item, 'Crafting:') ~= nil or string.find(item, 'Alchemy:') ~= nil) or string.find(item, 'Recipe:') ~= nil) then show = false end

-- calculate the cumulative chance and potential max quantity (based on drop table max rolls and average rolls) local chance = 100 local trueMaxQty = maxqty local trueChance = 100 local trueChanceInt = 100 if (args.Random ~= nil) then chance = tonumber(ip[i + 3]) -- this is a random table, so get the actual chance value

trueMaxQty = maxqty * args.MaxRolls -- total potential max quantity trueChance = 100 - ((((100 - chance) / 100) ^ avgRolls) * 100) -- the chance of any drop at all, based on average num of rolls trueChance = round(trueChance, 2) trueChanceInt = round(trueChance, 0) -- for displayed value (round to 0 places) end

-- if actually displaying this entry if (show == true) then local tr = table:tag('tr'):attr({ valign = 'middle' }) local image = '' if (string.find(item, 'Recipe:', 1, true) ~= nil or string.find(item, 'Cooking:', 1, true) ~= nil or string.find(item, 'Alchemy:', 1, true) ~= nil or string.find(item, 'Crafting:', 1, true) ~= nil) then image = string.format('', item) else image = string.format('', item, item) end

local qtyText = (minqty == maxqty and minqty) or (minqty .. '-' .. maxqty)

-- format and display the actual output for the first cell local output = image .. ' ' .. item .. ' (' .. qtyText .. ')' tr:tag('td'):cssText('padding-left: 0px; padding-top: 0px; padding-bottom: 0px; text-align: left'):wikitext(frame:preprocess(output))

-- if random table, display the random cell if (args.Random ~= nil) then local color = p.getRarity(chance) local td = tr:tag('td'):addClass(color):attr('data-sort-value', tonumber(chance)):tag('span'):attr('title', string.format('Each roll, you have a %s%% chance of receiving %s %s', chance, qtyText, item)) td:wikitext(chance .. '%') if (avgRolls > 1) then td:tag('sup'):tag('small'):tag('span'):attr('title', string.format('Total %s%% chance of receiving at least %s %s, from %s%s rolls', tostring(round(trueChance, 2)), minqty, item, pretext, round(avgRolls, 1))):wikitext(' (' .. trueChanceInt .. '%)')               end end end

if not isTemplate(frame) then frame:callParserFunction{ name = '#cargo_store', args = {                   '_table = ItemSource', itemName = item, minQty = minqty, maxQty = trueMaxQty, -- listing with total potential max quantity chance = trueChance, -- listing with average chance of any drop at all type = args.Type or '', location = locArgs, DLC = dlc }           }        end end

table:done

if (avgRolls > 1) then html:tag('i'):tag('small'):wikitext('* The second number indicates the total chance of receiving the drop at least once, from ' .. pretext .. tostring(round(avgRolls, 1)) .. ' rolls.'):tag('br') end

return html end

function getDLCArgs(frame) if notempty(frame.args.DLC) then return frame.args.DLC elseif frame:getParent ~= nil then return getDLCArgs(frame:getParent) else return '' end end

function notempty(string) return string ~= nil and string ~= '' end

function isTemplate(iframe) local namespace = getRootParent(iframe):preprocess('') return namespace == 'Template' end

function getRootParent(iframe) if iframe:getParent == nil then return iframe else return getRootParent(iframe:getParent) end end

function p.getRarity(chance) local color = '' if chance == 100 then color = 'rarity-always'    -- Guaranteed (blue) elseif chance > 24 then color = 'rarity-common'    -- Common (green) elseif chance > 11 then color = 'rarity-uncommon'  -- Uncommon (yellow) elseif chance > 4 then color = 'rarity-rare'      -- Rare (orange) else color = 'rarity-veryrare'  -- Very rare (dark red) end return color end

function round(num, numDecimalPlaces) local mult = 10^(numDecimalPlaces or 0) return math.floor(num * mult + 0.5) / mult end

function split(inputstr, sep) if sep == nil then sep = "%s" end local t={} for str in string.gmatch(inputstr, "([^"..sep.."]+)") do           table.insert(t, str) end return t end

return p