Outward Wiki
No edit summary
m (Changed protection level for "Module:DropTable" ([Edit=Allow only autoconfirmed users] (indefinite) [Move=Allow only autoconfirmed users] (indefinite)))
 
(42 intermediate revisions by 2 users not shown)
Line 2: Line 2:
   
 
function p.main(frame)
 
function p.main(frame)
if frame == mw.getCurrentFrame() then
+
if frame == mw.getCurrentFrame() then
 
args = require('Module:ProcessArgs').merge(true)
 
args = require('Module:ProcessArgs').merge(true)
 
else
 
else
Line 10: Line 10:
 
-- If the user defined "args.ShowRecipes" we will show Recipe Items in the table, otherwise we wont.
 
-- If the user defined "args.ShowRecipes" we will show Recipe Items in the table, otherwise we wont.
 
showRecipes = args.ShowRecipes or nil
 
showRecipes = args.ShowRecipes or nil
  +
  +
dlc = getDLCArgs(frame) or ''
   
 
-- create the base html holder
 
-- create the base html holder
Line 16: Line 18:
 
-- display the title of the drop table
 
-- display the title of the drop table
 
html:tag('b'):wikitext(args.Title):tag('br'):done()
 
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
 
-- calculate the average number of rolls, if applicable
Line 100: Line 115:
 
local tr = table:tag('tr'):attr({ valign = 'middle' })
 
local tr = table:tag('tr'):attr({ valign = 'middle' })
 
 
 
local image = ''
-- check if the item has an image. if not, its assumed to be a recipe item (display the Recipe Scroll)
 
  +
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
local image = frame:callParserFunction{ name = '#ifexist', args =
 
 
image = string.format('[[File:Recipe Scroll.png|link=%s|20px]]', item)
{
 
string.format('File:%s.png', item),
+
else
string.format('[[File:%s.png|link=%s|20px]]', item, item),
+
image = string.format('[[File:%s.png|link=%s|20px]]', item, item)
  +
end
string.format('[[File:Recipe Scroll.png|link=%s|20px]]', item)
 
  +
}}
 
local qtyText = (minqty == maxqty and minqty) or (minqty .. '-' .. maxqty) -- a lua ternary operator. Means 'if minqty equals maxqty, then display minqty, else display "minqty - maxqty"'
+
local qtyText = (minqty == maxqty and minqty) or (minqty .. '-' .. maxqty)
   
 
-- format and display the actual output for the first cell
 
-- format and display the actual output for the first cell
Line 115: Line 130:
 
-- if random table, display the random cell
 
-- if random table, display the random cell
 
if (args.Random ~= nil) then
 
if (args.Random ~= nil) then
local color = string.format('background-color: #%s', p.getRarity(chance))
+
local color = p.getRarity(chance)
local td = tr:tag('td'):cssText(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))
+
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 .. '%')
 
td:wikitext(chance .. '%')
 
 
Line 125: Line 140:
 
end
 
end
   
  +
if not isTemplate(frame) then
-- add to cargo 'ItemSources' table
 
frame:callParserFunction{ name = '#cargo_store', args = {
+
frame:callParserFunction{ name = '#cargo_store', args =
'_table = ItemSource',
+
{
itemName = item,
+
'_table = ItemSource',
minQty = minqty,
+
itemName = item,
maxQty = trueMaxQty, -- listing with total potential max quantity
+
minQty = minqty,
chance = trueChance, -- listing with average chance of any drop at all
+
maxQty = trueMaxQty, -- listing with total potential max quantity
type = args.Type or ''
+
chance = trueChance, -- listing with average chance of any drop at all
  +
type = args.Type or '',
  +
location = locArgs,
  +
DLC = dlc
  +
}
 
}
 
}
}
+
end
 
end
 
end
   
Line 140: Line 159:
   
 
if (avgRolls > 1) then
 
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')
+
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
 
end
   
 
return html
 
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('{{NAMESPACE}}')
  +
return namespace == 'Template'
  +
end
  +
  +
function getRootParent(iframe)
  +
if iframe:getParent() == nil then
  +
return iframe
  +
else
  +
return getRootParent(iframe:getParent())
  +
end
 
end
 
end
   
Line 149: Line 195:
 
local color = ''
 
local color = ''
 
if chance == 100 then
 
if chance == 100 then
color = '005453' -- Guaranteed (blue)
+
color = 'rarity-always' -- Guaranteed (blue)
 
elseif chance > 24 then
 
elseif chance > 24 then
color = '082904' -- Common (green)
+
color = 'rarity-common' -- Common (green)
 
elseif chance > 11 then
 
elseif chance > 11 then
color = '545400' -- Uncommon (yellow)
+
color = 'rarity-uncommon' -- Uncommon (yellow)
 
elseif chance > 4 then
 
elseif chance > 4 then
color = '542400' -- Rare (orange)
+
color = 'rarity-rare' -- Rare (orange)
 
else
 
else
color = '260C09' -- Very rare (dark red)
+
color = 'rarity-veryrare' -- Very rare (dark red)
 
end
 
end
 
return color
 
return color
Line 165: Line 211:
 
local mult = 10^(numDecimalPlaces or 0)
 
local mult = 10^(numDecimalPlaces or 0)
 
return math.floor(num * mult + 0.5) / mult
 
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
 
end
   

Latest revision as of 20:05, 1 July 2021

Template-info Documentation

Lua module used by DropTable template.


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 <b><u>%s</u></b> 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('[[File:Recipe Scroll.png|link=%s|20px]]', item) 
            else
                image = string.format('[[File:%s.png|link=%s|20px]]', 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('{{NAMESPACE}}')  
    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