Module:MoneyMakingGuide: Difference between revisions

From Melvor Idle
m (Fix icons?)
(Fix currency function calls)
 
(47 intermediate revisions by one other user not shown)
Line 6: Line 6:
local itemdb = require('Module:Items')
local itemdb = require('Module:Items')
local icons = require('Module:Icons')
local icons = require('Module:Icons')
local StringBuilder = require('Module:StringBuilder')


-- Constants
-- Constants
Line 20: Line 21:
}
}


--- Formats a wikicode string to be bold and red
local SpecialItems = {
local function formatError(errorMessage)
['GP'] = 'Gold Pieces',
local eror = mw.html.create('span')
['SC'] = 'Slayer Coins',
:wikitext("'''")
}
:css('color', 'red')
 
:wikitext(errorMessage)
local SpecialIcons = {
:wikitext("'''")
['Gold Pieces'] = icons._Currency('melvorD:GP'),
:done()
['Slayer Coins'] = icons._Currency('melvorD:SlayerCoins'),
}
return tostring(error)
 
end
--- Possible fix to allow wikicode (especially tables) to be passed to the 'explanation'
--- parameter, and get parsed by the wiki parser on return.
-- Not used currently, because it might not be necessary at all.
local function processMarkup(str)
--return frame:preprocess(str)
end


--- Formats a given string to TitleCase.
--- Formats a given string to TitleCase.
-- @param name (string) String to format.
-- @param name (string) String to format.
-- @return (string) Formatted string, or the input if it's not a string.
-- @return (string) Formatted string, or the input if it's not a string.
local function formatName(name)
local function formatItemName(name)
-- Special case to correctly format GP name.
if SpecialItems[name] then
        return SpecialItems[name]
    end
 
-- Got to solve too many edge case swith this (like of and IV)
-- Better just make the user use the correct name instead.
--if type(name) == 'string' then
-- return shared.titleCase(name)
--end
return name
end
 
local function formatSkillName(name)
if type(name) == 'string' then
if type(name) == 'string' then
return shared.titleCase(name)
return shared.titleCase(name)
Line 41: Line 62:
return name
return name
end
local function getErrorDiv(message)
return mw.html.create('div')
:css('color', 'red')
:wikitext(message)
:done()
end
local function getItemIcon(iconName)
if SpecialIcons[iconName] then
return SpecialIcons[iconName]
end
return icons.Icon({iconName, type='item', notext=true})
end
local function getSkillExpIcon(skillName, expValue)
if expValue == nil then error("expValue is nil") end
local icon = icons.Icon({skillName, type='skill', notext=true})
expValue = num.formatnum(expValue)
return icon .. ' ' .. expValue
end
local function parseProfitMargin(minProfit, maxProfit)
-- Manually set maxProfit to numeric 0, only if it's nil.
if maxProfit == nil then maxProfit = 0 end
local min = tonumber(minProfit)
local max = tonumber(maxProfit)
if max == nil then
error("maxProfit is not a valid number.")
end
local sb = StringBuilder:new()
if min then
sb  :append(icons._Currency('melvorD:GP', num.round2(min)))
:append(' to ')
end
sb  :append(icons._Currency('melvorD:GP', num.round2(max)))
:append(' per hour')
return sb:toString()
end
end


Line 49: Line 117:
local function parseItemInOut(args, prefix)
local function parseItemInOut(args, prefix)
local items = {}
local items = {}
local totalValue = 0
for i = 1, MaxDynamicArgs do
for i = 1, MaxDynamicArgs do
local numPrefix = prefix .. i
local numPrefix = prefix .. i
Line 57: Line 127:
end
end
local pName = formatName(args[numPrefix])
local pName = formatItemName(args[numPrefix])
local pAmount = args[numPrefix .. AmountSuffix]
local pAmount = args[numPrefix .. AmountSuffix]
local pValue = args[numPrefix .. ValueSuffix]
local pValue = args[numPrefix .. ValueSuffix]
Line 72: Line 142:
end
end
-- Default to 0 to add to totals.
local totVal = pValue or 0
local totAmt = pAmount or 0
totalValue = totalValue + (totVal * totAmt)
table.insert(items, {
table.insert(items, {
prmNumber = i,
prmNumber = i,
Line 79: Line 154:
end
end
return items
return {
TotalValue = totalValue,
Items = items  
}
end
end


Line 95: Line 173:
end
end
local pSkill = formatName(args[skillPrefix])
local pSkill = formatSkillName(args[skillPrefix])
local pExp = args[skillPrefix .. AmountSuffix]
local pExp = tonumber(args[skillPrefix .. AmountSuffix])


if paramtest.has_content(pExp) then
pExp = tonumber(pExp)
end
table.insert(skills, {
table.insert(skills, {
prmNumber = i,
prmNumber = i,
Line 108: Line 182:
end
end
-- Return nil if there is no experience earned.
-- Lets the parent table know to enter 'None' instead
if #skills == 0 then
        return nil
    end
return skills
return skills
end
end
Line 114: Line 193:
-- @param items (table) A table containing items
-- @param items (table) A table containing items
-- @return (string) The HTML representation of the item table section.
-- @return (string) The HTML representation of the item table section.
local function buildItemTable(items)
local function buildItemTable(itemSet)
if itemSet == nil or itemSet.Items == nil or #itemSet.Items == 0 then
return nil
end
 
-- Create table with header for items
local tbl = mw.html.create('table')
tbl:addClass('wikitable sortable text-align-center')
:attr('style', 'font-size: 1em; width: calc(100% + 2px); margin: -1px')
:tag('tr')
:tag('th')
:addClass('unsortable')
:tag('th'):wikitext('Item')
:tag('th'):wikitext('Quantity')
:tag('th'):wikitext('Value')
:done()
-- Create individual rows for items
for _, i in pairs(itemSet.Items) do
local row = mw.html.create('tr')
local imgCell = mw.html.create('td')
:wikitext(getItemIcon(i.name))
:css('height', '28px')
:done()
local itemCell = mw.html.create('td')
:css('text-align','left')
:wikitext(string.format('[[%s]]', i.name))
:done()
local qtyCell = mw.html.create('td')
if i.amount == nil then
qtyCell:node(getErrorDiv("Unable to parse quantity for item: " .. i.name))
else
local qty = num.autoround(i.amount)
qtyCell:wikitext(num.formatnum(qty))
end
qtyCell:done()
local valCell = mw.html.create('td')
if i.value == nil then
valCell:node(getErrorDiv("Unable to get value for item: " .. i.name))
else
local tot = i.value * (i.amount or 0)
valCell
:css('text-align','right')
:wikitext(icons._Currency('melvorD:GP', num.round2(tot)))
:attr("data-sort-value", tot)
end
valCell:done()
-- Add all cells to row, and row to table.
row:node(imgCell):node(itemCell):node(qtyCell):node(valCell):done()
tbl:node(row)
end
return tbl:done()
end
end


Line 120: Line 256:
-- @param items (table) A table containing skills and experience values.
-- @param items (table) A table containing skills and experience values.
-- @return (string) The HTML representation of the item table section.
-- @return (string) The HTML representation of the item table section.
local function buildExpTable(skills)
local function buildExpLayout(skills)
local layoutLines = {}
for _, skillLine in pairs(skills) do
local hasInvalidExp = false
local span = nil
if skillLine.exp == nil or skillLine.exp <= 0 then
hasInvalidExp = true
end
local skillExp = num.round2(skillLine.exp or 0)
local skillIcon = getSkillExpIcon(skillLine.name, skillExp)
 
if hasInvalidExp then
span = getErrorDiv("Skill in parameter " .. skillLine.prmNumber .. " has an invalid experience value.")
else
span = mw.html.create('div')
:wikitext(skillIcon)
:done()
end
 
table.insert(layoutLines, span)
end
return layoutLines
end
end


local function buildMMGTable(args)
function p._buildMMGTable(args)
if args['guideName'] == nil then
error("Money Making Guide must have a valid guideName parameter.")
end
-- Parse arguments.
-- Parse arguments.
local pSkills = parseExp(args)
local pSkills = parseExp(args)
local pInputs = parseItemInOut(args, 'input')
local pInputs = parseItemInOut(args, 'input')
local pOutputs = parseItemInOut(args, 'output')
local pOutputs = parseItemInOut(args, 'output')
local dlcIcons = p.getDLCIcons(dlc)
local dlcIcons = p.getDLCIcons(args['dlc'], ' ')
local minProfit = args['minimumProfit']
local maxProfit = pOutputs.TotalValue - pInputs.TotalValue
 
local tbl = mw.html.create()
local tbl = mw.html.create()


tbl:tag("table")
local html = tbl:tag("table")
     :addClass("wikitable")
     :addClass("wikitable")
     :attr('style', 'width: 100%; text-align: center;')
     :attr('style', 'width: 100%; text-align: center;')
Line 139: Line 304:
         :tag("td")
         :tag("td")
             :attr("colspan", 2)
             :attr("colspan", 2)
             :wikitext(table.concat(dlcIcons))
            :css('font-weight', 'bold')
             :wikitext(args['guideName'])
             :wikitext(table.concat(dlcIcons) .. ' ')
             :wikitext(args['guideName'] or '{{{guideName}}}')
    :tag("tr")
        :tag("td")
            :attr("colspan", 2)
            :wikitext(parseProfitMargin(minProfit, maxProfit))
     :tag("tr")
     :tag("tr")
         :tag("th")
         :tag("th")
             :attr("colspan", 2)
             :attr("colspan", 2)
             :wikitext("Requirements")
             :wikitext("Requirements")
     :tag("tr")
     :tag('tr')
         :tag("th")
         :tag('th')
             :wikitext("Skills")
             :wikitext('Skills')
         :tag("th")
         :tag('th')
             :wikitext("Recommended")
             :wikitext('Other')
        :done()
     :tag("tr")
     :tag("tr")
         :tag("td")
         :tag("td")
            :wikitext("<SkillReqs>")
        :addClass('mmg-no-list')
        :newline()
        :wikitext(paramtest.default_to(args['skills'], 'None'))  
     :tag("td")
     :tag("td")
        :attr("rowspan", 3)
            :addClass('mmg-no-list')
        :wikitext("<RecommendedReqs>")
        :newline()
     :tag("tr")
        :wikitext(paramtest.default_to(args['other'], 'None'))  
         :tag("th")
     :tag('tr')
             :wikitext("Items")
         :tag('th')
     :tag("tr")
             :wikitext('Items')
         :tag("td")
        :tag('th')
             :wikitext("<ItemReqs>")
            :wikitext('Recommended')
     :tag('tr')
        :tag('td')
        :addClass('mmg-no-list')
        :newline()
            :wikitext(paramtest.default_to(args['items'], 'None'))  
         :tag('td')
            :addClass('mmg-no-list')
        :newline()
             :wikitext(paramtest.default_to(args['recommended'], 'None'))  
     :tag("tr")
     :tag("tr")
         :tag("th")
         :tag("th")
Line 173: Line 355:
     :tag("tr")
     :tag("tr")
         :tag("td")
         :tag("td")
            :wikitext("<Total gp gained>")
        if args['profit'] then
        html:wikitext(icons._Currency('melvorD:GP', num.round2(args['profit'])))
        else
        html:wikitext(icons._Currency('melvorD:GP', num.round2(maxProfit)))
        end
        html = html
         :done()
         :done()
         :tag("td")
         :tag("td")
            :wikitext("<Exp values>")
        if pSkills ~= nil then
        for _, v in ipairs(buildExpLayout(pSkills)) do
        html:node(v)
        end
        else
        html:wikitext("None")
        end
            html = html
     :tag("tr")
     :tag("tr")
         :tag("th")
         :tag("th")
             :wikitext("Inputs")
             :wikitext("Inputs")
        :done()
            if pInputs.TotalValue ~= 0 then
            html:wikitext(" (" .. icons._Currency('melvorD:GP', num.round2(pInputs.TotalValue)) .. ")")
            end
            html = html
         :tag("th")
         :tag("th")
             :wikitext("Outputs")
             :wikitext("Outputs")
            if pOutputs.TotalValue ~= 0 then
            html:wikitext(" (" .. icons._Currency('melvorD:GP', num.round2(pOutputs.TotalValue)) .. ")")
            end
            html = html
     :tag("tr")
     :tag("tr")
         :tag("td")
         :tag("td")
        local inputsTable = buildItemTable(pInputs)
        if inputsTable ~= nil then
        html:css('padding', '0')
        :css('vertical-align', 'top')
        :node(inputsTable)
        else
        html:wikitext("None")
        end
        html = html
         :tag("td")
         :tag("td")
        local outputsTable = buildItemTable(pOutputs)
        if outputsTable ~= nil then
        html:css('padding', '0')
        :css('vertical-align', 'top')
        :node(outputsTable)
        else
        html:wikitext("None")
        end
        html = html
:done()
:done()
return tbl:done()
:newline()
end


return tostring(tbl)
function p.buildMMGTable(frame)
local args = frame:getParent().args
return p._buildMMGTable(args)
end
end


Line 196: Line 423:
if type(dlcParam) ~= 'string' then return {} end
if type(dlcParam) ~= 'string' then return {} end


local input = shared.splitString(dlcParam, ',', true)
local dlcs = {}
local dlcs = {}
for _, arg in pairs(shared.splitString(dlcArgs, ',')) do
for _, arg in pairs(input) do
for dlc, icon in pairs(DLCParams) do
local argLower = arg:lower()
if shared.compareString(dlc, arg, true) then
table.insert(dlcs, DLCParams[argLower])
table.insert(dlcs, icon)
end
end
end
end


Line 209: Line 434:


function p.main(frame)
function p.main(frame)
local args = frame:getParent().args
error("Call a specific function rather than main.")
return p._main(args)
end
 
function p._main(args)
if args['guideName'] == nil then
error("Money Making Guide must have a valid guideName parameter.")
end
 
return buildMMGTable(args)
end
end


function p.test()
function p.test()
local args = {
local args = {
guideName = "",
guideName       ='Mining Pure Crystal',
interval = "",
category        ='Non-combat',
skills = "",
dlc              ='aod, toth',
items = "",
skills          =[[
other = "",
*{{SkillReq|Mining|85}}
skillExp1 = "Magic",
*{{SkillReq|Herblore|53}}]],
skillExp1amount = "1000",
items            =[=[
skillExp2 = "Mining",
*{{ItemIcon|Mining Gloves}}
skillExp2amount = "420",
*{{ItemIcon|Perfect Swing Potion IV}}]=],
input1 = "nature rune",
other            =[=[
input1amount = "5",
*{{ItemIcon|Pure Crystal|notext=true}}{{Icon|Mastery|notext=true}} Level 99
input1value = "69",
*{{UpgradeIcon|Dragon Pickaxe}}
input2 = "Fire Rune",
*[[Mining#Mastery Pool Checkpoints|95% Mining Mastery Pool Checkpoint]]]=],
input2amount = "20",
recommended      =[=[
output1 = "gp",
[[Money_Making/Mining_Pure_Crystal#Improves_GP_Rate|Bonusses that improve profit]]]=],
output1amount = "1050",
category = "",
dlc = "",
intensity = "",
explanation = ""
}
}
local result = parseItemInOut(args, 'input')
local t = p._buildMMGTable(args)
for k, v in pairs(result) do
for a ,b in pairs(v) do
mw.log(a .. '  =  '.. tostring(b))
end
end
mw.log(t)
end
end
-- function p.test()
-- local args = {
-- guideName = "",
-- interval = "",
-- skills = "",
-- items = "",
-- other = "",
-- recommended = "",
-- skillExp1 = "Maagic",
-- skillExp1amount = "-1000",
-- skillExp2 = "Mining",
-- skillExp2amount = "420",
-- input1 = "nature rune",
-- input1amount = "5",
-- input1value = "69",
-- input2 = "Fire Rune",
-- input2amount = "20",
-- output1 = "gp",
-- output1amount = "1050",
-- category = "",
-- dlc = "aod, toth",
-- intensity = "",
-- explanation = ""
-- }
--
-- mw.log(p._buildMMGTable(args))
-- end


return p
return p

Latest revision as of 14:52, 22 June 2024

Documentation for this module may be created at Module:MoneyMakingGuide/doc

local p = {}

local shared = require('Module:Shared')
local num = require('Module:Number')
local paramtest = require('Module:Shared/Paramtest')
local itemdb = require('Module:Items')
local icons = require('Module:Icons')
local StringBuilder = require('Module:StringBuilder')

-- Constants
local MaxDynamicArgs = 20
local AmountSuffix = 'amount'
local ValueSuffix = 'value'
local SkillPrefix = 'skillExp'

-- Determines the order of Icons
local DLCParams = { 
	toth  = icons.TotH(), 
	aod = icons.AoD(), 
	ita = icons.ItA()
}

local SpecialItems = {
	['GP'] = 'Gold Pieces',
	['SC'] = 'Slayer Coins',
}

local SpecialIcons = {
	['Gold Pieces'] = icons._Currency('melvorD:GP'),
	['Slayer Coins'] = icons._Currency('melvorD:SlayerCoins'),
}

--- Possible fix to allow wikicode (especially tables) to be passed to the 'explanation'
--- parameter, and get parsed by the wiki parser on return.
-- Not used currently, because it might not be necessary at all.
local function processMarkup(str)
	--return frame:preprocess(str)
end	

--- Formats a given string to TitleCase.
-- @param name (string) String to format.
-- @return (string) Formatted string, or the input if it's not a string.
local function formatItemName(name)
	-- Special case to correctly format GP name.
	if SpecialItems[name] then
        return SpecialItems[name]
    end

	-- Got to solve too many edge case swith this (like of and IV)
	-- Better just make the user use the correct name instead.
	--if type(name) == 'string' then
	--	return shared.titleCase(name)
	--end
	
	return name
end

local function formatSkillName(name)
	if type(name) == 'string' then
		return shared.titleCase(name)
	end
	
	return name
end

local function getErrorDiv(message)
	return mw.html.create('div')
		:css('color', 'red')
		:wikitext(message)
		:done()
end

local function getItemIcon(iconName)
	if SpecialIcons[iconName] then
		return SpecialIcons[iconName]
	end

	return icons.Icon({iconName, type='item', notext=true})
end

local function getSkillExpIcon(skillName, expValue)
	if expValue == nil then error("expValue is nil") end
	
	local icon = icons.Icon({skillName, type='skill', notext=true})
	expValue = num.formatnum(expValue)
	
	return icon .. ' ' .. expValue
end

local function parseProfitMargin(minProfit, maxProfit)
	-- Manually set maxProfit to numeric 0, only if it's nil.
	if maxProfit == nil then maxProfit = 0 end
	local min = tonumber(minProfit)
	local max = tonumber(maxProfit)
	
	if max == nil then
		error("maxProfit is not a valid number.") 
	end
	
	local sb = StringBuilder:new()
	
	if min then
		sb  :append(icons._Currency('melvorD:GP', num.round2(min)))
			:append(' to ')
	end
	
	sb  :append(icons._Currency('melvorD:GP', num.round2(max)))
		:append(' per hour')
		
	return sb:toString()
end

--- Parses the input and output items from the provided arguments.
-- @param args (table) Passed args table from the caller.
-- @param prefix (string) The prefix that determines input or output item parsing.
-- @return (table) A table containing parsed item information.
local function parseItemInOut(args, prefix)
	local items = {}
	local totalValue = 0
	
	for i = 1, MaxDynamicArgs do
		local numPrefix = prefix .. i

		-- Stop parsing. Could cause problems if user input skips indexes.
		if paramtest.is_empty(args[numPrefix]) then
			break
		end
			
		local pName = formatItemName(args[numPrefix])
		local pAmount = args[numPrefix .. AmountSuffix]
		local pValue = args[numPrefix .. ValueSuffix]

		-- Values *should* always exit this function with a non nil value.
		if paramtest.has_content(pAmount) then
			pAmount = tonumber(pAmount)
		end
		
		if not paramtest.has_content(pValue) or tonumber(pValue) == nil then
			pValue = itemdb.getItemValue(pName)
		else
			pValue = tonumber(pValue)
		end
		
		-- Default to 0 to add to totals.
		local totVal = pValue or 0
		local totAmt = pAmount or 0
		totalValue = totalValue + (totVal * totAmt)

		table.insert(items, {
			prmNumber = i,
			name = pName, 
			amount = pAmount, 
			value = pValue})
	end
	
	return {
		TotalValue = totalValue, 
		Items = items 
	}
end

--- Parses the skill experience from the provided arguments.
-- @param args (table) Passed args table from the caller.
-- @return (table) A table containing parsed skill experience information.
local function parseExp(args)
	local skills = {}
	for i = 1, MaxDynamicArgs do
		local skillPrefix = 'skillExp' .. i
		
		-- Stop parsing. Could cause problems if user input skips indexes.
		if paramtest.is_empty(args[skillPrefix]) then
			break
		end
			
		local pSkill = formatSkillName(args[skillPrefix])
		local pExp = tonumber(args[skillPrefix .. AmountSuffix])

		table.insert(skills, {
			prmNumber = i,
			name = pSkill, 
			exp = pExp})
	end
	
	-- Return nil if there is no experience earned.
	-- Lets the parent table know to enter 'None' instead
	if #skills == 0 then
        return nil
    end
	return skills
end

--- Builds the section of the mmg table that shows the input and output items.
-- @param items (table) A table containing items
-- @return (string) The HTML representation of the item table section.
local function buildItemTable(itemSet)
	if itemSet == nil or itemSet.Items == nil or #itemSet.Items == 0 then
		return nil
	end

	-- Create table with header for items
	local tbl = mw.html.create('table')
	tbl:addClass('wikitable sortable text-align-center')
		:attr('style', 'font-size: 1em; width: calc(100% + 2px); margin: -1px')
		:tag('tr')
			:tag('th')
				:addClass('unsortable')
			:tag('th'):wikitext('Item')
			:tag('th'):wikitext('Quantity')
			:tag('th'):wikitext('Value')
			:done()
	
	-- Create individual rows for items
	for _, i in pairs(itemSet.Items) do
		local row = mw.html.create('tr')
		
		local imgCell = mw.html.create('td')
			:wikitext(getItemIcon(i.name))
			:css('height', '28px')
			:done()
			
		local itemCell = mw.html.create('td')
			:css('text-align','left')
			:wikitext(string.format('[[%s]]', i.name))
			:done()
			
		local qtyCell = mw.html.create('td')
		if i.amount == nil then
			qtyCell:node(getErrorDiv("Unable to parse quantity for item: " .. i.name))
		else
			local qty = num.autoround(i.amount)
			qtyCell:wikitext(num.formatnum(qty))
		end
		qtyCell:done()
		
		local valCell = mw.html.create('td')
		if i.value == nil then
			valCell:node(getErrorDiv("Unable to get value for item: " .. i.name))
		else
			local tot = i.value * (i.amount or 0)
			valCell
				:css('text-align','right')
				:wikitext(icons._Currency('melvorD:GP', num.round2(tot)))
				:attr("data-sort-value", tot)
		end
		valCell:done()
		
		-- Add all cells to row, and row to table.
		row:node(imgCell):node(itemCell):node(qtyCell):node(valCell):done()
		tbl:node(row)
	end
	
	return tbl:done()
end

--- Builds the section of the mmg table that shows the skill experience gained.
-- @param items (table) A table containing skills and experience values.
-- @return (string) The HTML representation of the item table section.
local function buildExpLayout(skills)
	local layoutLines = {}
	for _, skillLine in pairs(skills) do
		local hasInvalidExp = false
		local span = nil
	
		if skillLine.exp == nil or skillLine.exp <= 0 then
			hasInvalidExp = true
		end
		
		local skillExp = num.round2(skillLine.exp or 0)
		local skillIcon = getSkillExpIcon(skillLine.name, skillExp)

		if hasInvalidExp then
			span = getErrorDiv("Skill in parameter " .. skillLine.prmNumber .. " has an invalid experience value.")
		else
			span = mw.html.create('div')
				:wikitext(skillIcon)
				:done()
		end

		table.insert(layoutLines, span)
	end
	
	return layoutLines
end

function p._buildMMGTable(args)
	if args['guideName'] == nil then
		error("Money Making Guide must have a valid guideName parameter.")
	end
	
	-- Parse arguments.
	local pSkills = parseExp(args)
	local pInputs = parseItemInOut(args, 'input')
	local pOutputs = parseItemInOut(args, 'output')
	local dlcIcons = p.getDLCIcons(args['dlc'], ' ')
	local minProfit = args['minimumProfit']
	local maxProfit = pOutputs.TotalValue - pInputs.TotalValue

	local tbl = mw.html.create()

	local html = tbl:tag("table")
    :addClass("wikitable")
    :attr('style', 'width: 100%; text-align: center;')
    :tag("tr")
        :tag("td")
            :attr("colspan", 2)
            :css('font-weight', 'bold')
            :wikitext(table.concat(dlcIcons) .. ' ')
            :wikitext(args['guideName'] or '{{{guideName}}}')
    :tag("tr")
        :tag("td")
            :attr("colspan", 2)
            :wikitext(parseProfitMargin(minProfit, maxProfit))
    :tag("tr")
        :tag("th")
            :attr("colspan", 2)
            :wikitext("Requirements")
    :tag('tr')
        :tag('th')
            :wikitext('Skills')
        :tag('th')
            :wikitext('Other')
        :done()
    :tag("tr")
        :tag("td")
        	:addClass('mmg-no-list')
        	:newline()
        	:wikitext(paramtest.default_to(args['skills'], 'None')) 
    :tag("td")
            :addClass('mmg-no-list')
        	:newline()
        	:wikitext(paramtest.default_to(args['other'], 'None')) 
    :tag('tr')
        :tag('th')
            :wikitext('Items')
        :tag('th')
            :wikitext('Recommended')
    :tag('tr')
        :tag('td')
        	:addClass('mmg-no-list')
        	:newline()
            :wikitext(paramtest.default_to(args['items'], 'None')) 
        :tag('td')
            :addClass('mmg-no-list')
        	:newline()
            :wikitext(paramtest.default_to(args['recommended'], 'None')) 
    :tag("tr")
        :tag("th")
            :attr("colspan", 2)
            :wikitext("Results")
    :tag("tr")
        :tag("th")
            :wikitext("Profit")
        :tag("th")
            :wikitext("Experience gained")
    :tag("tr")
        :tag("td")
        	if args['profit'] then
        		html:wikitext(icons._Currency('melvorD:GP', num.round2(args['profit'])))
        	else
        		html:wikitext(icons._Currency('melvorD:GP', num.round2(maxProfit)))
        	end
        	html = html
        :done()
        :tag("td")
        	if pSkills ~= nil then
        		for _, v in ipairs(buildExpLayout(pSkills)) do
        			html:node(v)
        		end
        	else
        		html:wikitext("None")
        	end
            html = html
    :tag("tr")
        :tag("th")
            :wikitext("Inputs")
            if pInputs.TotalValue ~= 0 then
            	html:wikitext(" (" .. icons._Currency('melvorD:GP', num.round2(pInputs.TotalValue)) .. ")")
            end
            html = html
        :tag("th")
            :wikitext("Outputs")
            if pOutputs.TotalValue ~= 0 then
            	html:wikitext(" (" .. icons._Currency('melvorD:GP', num.round2(pOutputs.TotalValue)) .. ")")
            end
            html = html
    :tag("tr")
        :tag("td")
        	local inputsTable = buildItemTable(pInputs)
        	if inputsTable ~= nil then
        		html:css('padding', '0')
        			:css('vertical-align', 'top')
        			:node(inputsTable)
        	else
        		html:wikitext("None")
        	end
        	html = html
        :tag("td")
        	local outputsTable = buildItemTable(pOutputs)
        	if outputsTable ~= nil then
        		html:css('padding', '0')
        			:css('vertical-align', 'top')
        			:node(outputsTable)
        	else
        		html:wikitext("None")
        	end
        	html = html
		:done()
	:done()
	
	return tbl:done()
		:newline()
end

function p.buildMMGTable(frame)
	local args = frame:getParent().args
	return p._buildMMGTable(args)
end

--- Returns the parsed result of the dlcParam.
-- @param dlcParam (string) A string separated by , listing the DLCs required for the MMG
-- @return (table) A table containing the items of provided DLCs
function p.getDLCIcons(dlcParam)
	if type(dlcParam) ~= 'string' then return {} end

	local input = shared.splitString(dlcParam, ',', true)
	local dlcs = {}
	for _, arg in pairs(input) do
		local argLower = arg:lower()
		table.insert(dlcs, DLCParams[argLower])
	end

	return dlcs
end

function p.main(frame)
	error("Call a specific function rather than main.")
end

function p.test()
	local args = {
		guideName        ='Mining Pure Crystal',
category         ='Non-combat',
dlc              ='aod, toth',
skills           =[[
*{{SkillReq|Mining|85}}
*{{SkillReq|Herblore|53}}]],
items            =[=[
*{{ItemIcon|Mining Gloves}}
*{{ItemIcon|Perfect Swing Potion IV}}]=],
other            =[=[
*{{ItemIcon|Pure Crystal|notext=true}}{{Icon|Mastery|notext=true}} Level 99
*{{UpgradeIcon|Dragon Pickaxe}}
*[[Mining#Mastery Pool Checkpoints|95% Mining Mastery Pool Checkpoint]]]=],
recommended      =[=[
[[Money_Making/Mining_Pure_Crystal#Improves_GP_Rate|Bonusses that improve profit]]]=],
	}
	
	local t = p._buildMMGTable(args)
	
	mw.log(t)
end

-- function p.test()
-- 	local args = {
-- 		guideName = "",
-- 		interval = "",
-- 		skills = "",
-- 		items = "",
-- 		other = "",
-- 		recommended = "",
-- 		skillExp1 = "Maagic",
-- 		skillExp1amount = "-1000",
-- 		skillExp2 = "Mining",
-- 		skillExp2amount = "420",
-- 		input1 = "nature rune",
-- 		input1amount = "5",
-- 		input1value = "69",
-- 		input2 = "Fire Rune",
-- 		input2amount = "20",
-- 		output1 = "gp",
-- 		output1amount = "1050",
-- 		category = "",
-- 		dlc = "aod, toth",
-- 		intensity = "",
-- 		explanation = ""
-- 	}
-- 	
-- 	mw.log(p._buildMMGTable(args))
-- end

return p