Module:MoneyMakingGuide: Difference between revisions

From Melvor Idle
(Allow "None" to appear when no skills are entered.)
(Fix styling issues with lists)
 
(2 intermediate revisions by the same user not shown)
Line 162: Line 162:
local pSkill = formatSkillName(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 315: Line 311:
     :tag("tr")
     :tag("tr")
         :tag("td")
         :tag("td")
        :wikitext(paramtest.default_to(args['skills'], 'None'))  
        :addClass('mmg-no-list')
        :newline()
        :wikitext(paramtest.default_to(args['skills'], 'None'))  
     :tag("td")
     :tag("td")
        :wikitext(paramtest.default_to(args['other'], 'None'))  
            :addClass('mmg-no-list')
        :newline()
        :wikitext(paramtest.default_to(args['other'], 'None'))  
     :tag('tr')
     :tag('tr')
         :tag('th')
         :tag('th')
Line 325: Line 325:
     :tag('tr')
     :tag('tr')
         :tag('td')
         :tag('td')
        :addClass('mmg-no-list')
        :newline()
             :wikitext(paramtest.default_to(args['items'], 'None'))  
             :wikitext(paramtest.default_to(args['items'], 'None'))  
         :tag('td')
         :tag('td')
            :addClass('mmg-no-list')
        :newline()
             :wikitext(paramtest.default_to(args['recommended'], 'None'))  
             :wikitext(paramtest.default_to(args['recommended'], 'None'))  
     :tag("tr")
     :tag("tr")
Line 419: Line 423:
function p.main(frame)
function p.main(frame)
error("Call a specific function rather than main.")
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
end



Latest revision as of 23:24, 3 April 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()
}

--- 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 shared.compareString(name, 'Gold Pieces', true) or shared.compareString(name, 'GP', true) then
		return 'Gold Pieces'
	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 iconName == 'Gold Pieces' then
		return icons.GP()
	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)
	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.GP(num.round2(min / 1000)))
			:append('k/hr Minimum ~ ')
	end
	
	sb  :append(icons.GP(num.round2(max / 1000)))
		:append('k/hr Maximum')
		
	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.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.GP(num.round2(args['profit'])))
        	else
        		html:wikitext(icons.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.GP(num.round2(pInputs.TotalValue)) .. ")")
            end
            html = html
        :tag("th")
            :wikitext("Outputs")
            if pOutputs.TotalValue ~= 0 then
            	html:wikitext(" (" .. icons.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