Module:Mmgtable/displaysandbox: Difference between revisions
Jump to navigation
Jump to search
Content added Content deleted
No edit summary Tag: Reverted |
No edit summary Tag: Manual revert |
||
Line 1: | Line 1: | ||
-- <nowiki> |
|||
local timefunc = require('Module:Time') |
|||
local exg = require('Module:Exchange')._price |
|||
local p = {} |
local p = {} |
||
--imports |
|||
local gePrice = require('Module:Exchange')._price |
|||
local yn = require('Module:Yesno') |
|||
local round = require('Module:Number')._round |
|||
local _coins = require('Module:Currency')._amount |
|||
local vdf = mw.ext.VariablesLua.vardefine |
|||
local lang = mw.getContentLanguage() |
local lang = mw.getContentLanguage() |
||
local title = mw.title.getCurrentTitle() |
|||
function gep(x) |
|||
local onmain = require('Module:Mainonly').on_main |
|||
return exg(x, 1, nil, nil, 0) |
|||
function expr(x) |
|||
local e_g, e = pcall(mw.ext.ParserFunctions.expr, x) |
|||
if e_g then |
|||
return e |
|||
end |
|||
return nil |
|||
end |
end |
||
function sigfig(x, p) |
|||
local MEMBERS_ICON = { |
|||
[false] = "[[File:Free-to-play icon.png|20px|center|link=Free-to-play]]", |
|||
local x = math.abs(x) |
|||
[true] = "[[File:Member icon.png|20px|center|link=Members]]" |
|||
if x == 0 then |
|||
} |
|||
return 0 |
|||
function round1k(x, f) |
|||
if not tonumber(x) then |
|||
return x |
|||
end |
end |
||
local |
local _x = math.abs(x) |
||
_x = 1000 * math.floor(_x / 1000 + 0.5) |
|||
if x < 0 then |
|||
_x = _x * -1 |
|||
end |
|||
if f then |
|||
return lang:formatNum(_x) |
|||
end |
|||
return _x |
|||
end |
end |
||
function |
function round1dp(x, f) |
||
if not tonumber(x) then |
|||
return x |
|||
local _x |
|||
end |
|||
if x < 0.1 and x > -0.1 then |
|||
local _x = math.abs(x) |
|||
_x = math.floor(_x * 10 + 0.5) / 10 |
|||
elseif x >= 100 or x <= -100 then |
|||
if x < 0 then |
|||
_x = round(x, 0) |
|||
_x = _x * -1 |
|||
else |
|||
_x = round(x, 2) |
|||
end |
end |
||
if f then |
if f then |
||
Line 41: | Line 42: | ||
return _x |
return _x |
||
end |
end |
||
-- Config constants, change as needed |
|||
MAX_INPUTS = 75 |
|||
MAX_OUTPUTS = 75 |
|||
MAX_XP = 75 |
|||
function |
function calc_value(args, kph) |
||
local total = 0 |
|||
return p._mmgtable(mw.getCurrentFrame(), args) |
|||
for i,v in ipairs(args) do |
|||
local val |
|||
if v.pricetype == 'value' then |
|||
val = v.qty * v.value |
|||
elseif v.pricetype == 'gemw' then |
|||
val = gep(v.name) * v.qty |
|||
end |
|||
if kph>0 and not v.isph then |
|||
val = val * kph |
|||
end |
|||
total = total + val |
|||
end |
|||
return total |
|||
end |
end |
||
function |
function make_row(fullpagename, raw_data, ismulti) |
||
local |
local data = mw.text.jsonDecode(mw.text.decode(raw_data)) |
||
local tr = mw.html.create('tr') |
|||
return p._mmgtable(frame, args) |
|||
local pagename, pagelink |
|||
pagelink = fullpagename..'|'..string.match(fullpagename, '/(.*)') |
|||
if ismulti and data.version then |
|||
pagename = '[['..pagelink..' ('..data.version..')]]' |
|||
else |
|||
pagename = '[['..pagelink..']]' |
|||
end |
|||
local rowcat = data.category |
|||
if data.skillcategory then |
|||
rowcat = rowcat .. '/' .. data.skillcategory |
|||
end |
|||
local val, c_class |
|||
local inputval = calc_value(data.inputs, data.prices.default_kph or 0) |
|||
local outputval = calc_value(data.outputs, data.prices.default_kph or 0) |
|||
val = outputval - inputval |
|||
if val > 0 then |
|||
c_class = 'coins-pos' |
|||
elseif val < 0 then |
|||
c_class = 'coins-neg' |
|||
else |
|||
c_class = '' |
|||
end |
|||
local intensity = (data.intensity == 'High') and 3 or (data.intensity == 'Moderate') and 2 or (data.intensity == 'Low') and 1 or 0 |
|||
tr :addClass('mmg-list-table-row') |
|||
:tag('td') |
|||
:wikitext(pagename) |
|||
:css('max-width', '300px') |
|||
:css('word-wrap', 'break-word') |
|||
:done() |
|||
:tag('td') |
|||
:addClass('mmg-list-table-profit-cell') |
|||
:tag('span') |
|||
:addClass('coins') |
|||
:addClass(c_class) |
|||
:wikitext(round1k(val, true)) |
|||
:done() |
|||
:done() |
|||
:tag('td') |
|||
:addClass('mmg-list-table-kph-cell') |
|||
:wikitext(data.prices.default_kph) |
|||
:css('max-width', '125px') |
|||
:css('min-width', '125px') |
|||
:done() |
|||
:tag('td') |
|||
:addClass('plainlist') |
|||
:newline() |
|||
:wikitext(data.skill) |
|||
:css('max-width', '400px') |
|||
:css('word-wrap', 'break-word') |
|||
:done() |
|||
:tag('td') |
|||
:wikitext(rowcat) |
|||
:done() |
|||
:tag('td') |
|||
:wikitext(data.intensity) |
|||
:attr('data-sort-value', intensity) |
|||
:done() |
|||
:tag('td') |
|||
:wikitext(MEMBERS_ICON[data.members]) |
|||
:done() |
|||
return val, data.category, tr |
|||
end |
end |
||
function p.main(frame) |
|||
-- Create an MMG table. |
|||
local args = frame:getParent().args |
|||
-- Frame is the frame the module was invoked from. |
|||
local query = {'[[MMG JSON::+]]', '?MMG JSON', '?=#', limit=10000} |
|||
-- Args are the template arguments used when creating the table. |
|||
if args[1] then |
|||
function p._mmgtable(frame, args) |
|||
table.insert(query, 2, '[[Category:'..args[1]..']]') |
|||
local parsedInput = handleInputs(args) |
|||
end |
|||
local parsedOutput = handleOutputs(args) |
|||
local |
local data = mw.smw.ask(query) |
||
local is_per_kill = yn(args.isperkill) |
|||
local ret = mw.html.create('') |
|||
local t = mw.html.create('table') |
|||
mw.logObject(parsedInput) |
|||
mw.logObject(parsedOutput) |
|||
mw.logObject(parsedXP) |
|||
t :addClass('wikitable sortable sticky-header align-right-2 align-center-5 align-center-6 align-center-7 mmg-list-table') |
|||
:tag('tr') |
|||
:cssText('width:100%;text-align:center;') |
|||
:tag(' |
:tag('th') |
||
:wikitext( |
:wikitext('Method') |
||
:css('word-wrap', 'break-word') |
|||
:css('max-width', '300px') |
|||
:done() |
:done() |
||
:tag(' |
:tag('th') |
||
: |
:wikitext('Hourly Profit') |
||
:css('max-width', '100px') |
|||
:done() |
|||
:tag('td') |
|||
:attr('rowspan', '9') |
|||
:wikitext(args['Image'] or '{{{Image}}}') |
|||
:done() |
|||
:done() |
:done() |
||
:tag(' |
:tag('th') |
||
: |
:wikitext('Actions per hour') |
||
:addClass('unsortable') |
|||
:done() |
|||
:done() |
:done() |
||
:tag(' |
:tag('th') |
||
: |
:wikitext('Skills') |
||
:css('word-wrap', 'break-word') |
|||
:css('max-width', '400px') |
|||
:newline() |
|||
:addClass('unsortable') |
|||
:wikitext(args['Skill'] or 'None') -- Can leave blank if no reqs. |
|||
:done() |
|||
:done() |
:done() |
||
:tag(' |
:tag('th') |
||
: |
:wikitext('Category') |
||
:wikitext('Items') |
|||
:done() |
|||
:done() |
:done() |
||
:tag(' |
:tag('th') |
||
: |
:wikitext('Intensity') |
||
:css('width', '65px') |
|||
:newline() |
|||
:wikitext(args['Item'] or 'None') -- Can leave blank if no reqs. |
|||
:done() |
|||
:done() |
:done() |
||
:tag(' |
:tag('th') |
||
: |
:wikitext('Members') |
||
:css('width', '65px') |
|||
:done() |
|||
:done() |
|||
:tag('tr') |
|||
:tag('td') |
|||
:addClass('plainlist') |
|||
:newline() |
|||
:wikitext(args['Quest'] or 'None') -- Can leave blank if no reqs |
|||
:done() |
|||
:done() |
|||
:tag('tr') |
|||
:tag('th') |
|||
:wikitext('Other') |
|||
:done() |
|||
:done() |
|||
:tag('tr') |
|||
:tag('td') |
|||
:addClass('plainlist') |
|||
:newline() |
|||
:wikitext(args['Other'] or 'None') -- Can leave blank if no reqs |
|||
:done() |
|||
:done() |
|||
:tag('tr') |
|||
:tag('th') |
|||
:attr('colspan', '2') |
|||
:wikitext('Results') |
|||
:done() |
|||
:done() |
|||
:tag('tr') |
|||
:tag('th') |
|||
:wikitext('Profit') |
|||
:done() |
|||
:tag('th') |
|||
:wikitext('Experience gained') |
|||
:done() |
|||
:done() |
:done() |
||
local |
local methods = {} |
||
for i,v in ipairs(data) do |
|||
local profitTD = xpTR:tag('td') |
|||
if(string.find(v[1], 'Money making guide/')) then |
|||
profitTD:addClass('mmg-varieswithkph') |
|||
if type(v['MMG JSON']) == 'table' then |
|||
:attr({['data-mmg-cost-ph'] = parsedOutput.valueph-parsedInput.valueph, ['data-mmg-cost-pk'] = parsedOutput.valuepk-parsedInput.valuepk}) |
|||
for j,u in ipairs(v['MMG JSON']) do |
|||
:wikitext(_coins(autoround(parsedOutput.value - parsedInput.value), 'coins')) |
|||
table.insert(methods, { make_row(v[1], u, true) }) |
|||
end |
|||
local xpTD = xpTR:tag('td') |
|||
else |
|||
if args['Other Benefits'] then |
|||
table.insert(methods, { make_row(v[1], v['MMG JSON'], false) }) |
|||
xpTD:newline():wikitext(args['Other Benefits']):addClass('plainlist') |
|||
end |
|||
elseif #parsedXP.spans > 0 then |
|||
for i,v in ipairs(parsedXP.spans) do |
|||
xpTD:node(v) |
|||
end |
end |
||
else |
|||
xpTD = tbl:wikitext('None') |
|||
end |
end |
||
table.sort(methods, function(a,b) return a[1]>b[1] end) |
|||
for i,v in ipairs(methods) do |
|||
local putsTRH = tbl:tag('tr') |
|||
if v[1] > 0 then |
|||
local inputTH = putsTRH:tag('th') |
|||
t:newline():node(v[3]) |
|||
inputTH:wikitext('Inputs') |
|||
if parsedInput.value ~= 0 then |
|||
inputTH:wikitext(' (') |
|||
:tag('span') |
|||
:addClass('mmg-varieswithkph') |
|||
:attr({['data-mmg-cost-ph'] = parsedInput.valueph, ['data-mmg-cost-pk'] = parsedInput.valuepk}) |
|||
:wikitext(_coins(autoround(parsedInput.value), 'coins')) |
|||
:done() |
|||
:wikitext(')') |
|||
end |
|||
local outputTH = putsTRH:tag('th') |
|||
outputTH:wikitext('Outputs') |
|||
if parsedOutput.value ~= 0 then |
|||
outputTH:wikitext(' (') |
|||
:tag('span') |
|||
:addClass('mmg-varieswithkph') |
|||
:attr({['data-mmg-cost-ph'] = parsedOutput.valueph, ['data-mmg-cost-pk'] = parsedOutput.valuepk}) |
|||
:wikitext(_coins(autoround(parsedOutput.value), 'coins')) |
|||
:done() |
|||
:wikitext(')') |
|||
end |
|||
local inputTR = tbl:tag('tr') |
|||
local inputTD = inputTR:tag('td') |
|||
local outputTD = inputTR:tag('td') |
|||
for i,v in ipairs(parsedInput.spans) do |
|||
inputTD:node(v) |
|||
end |
|||
for i,v in ipairs(parsedOutput.spans) do |
|||
outputTD:node(v) |
|||
end |
|||
local kph_text = args['kph name'] or 'Kills per hour' |
|||
if is_per_kill then |
|||
tbl:addClass('mmg-isperkill') |
|||
:attr('data-default-kph', args.kph) |
|||
:attr('data-default-kph-name', kph_text) |
|||
end |
|||
if not(yn(args.noexports)) then |
|||
if is_per_kill then |
|||
vdf('kph', string.format('<span class="mmg-variable mmg-kph">%s</span>', args.kph)) |
|||
vdf('default_kph', args.kph) |
|||
vdf('inputPH', string.format('<span class="mmg-variable mmg-input-ph" data-mmg-cost-ph="%s">%s</span>', parsedInput.valueph, _coins(autoround(parsedInput.valueph), 'nocoins'))) |
|||
vdf('inputPK', string.format('<span class="mmg-variable mmg-input-pk" data-mmg-cost-pk="%s">%s</span>', parsedInput.valuepk, _coins(autoround(parsedInput.valuepk), 'nocoins'))) |
|||
vdf('input', string.format('<span class="mmg-variable mmg-input" data-mmg-cost-ph="%s", data-mmg-cost-pk="%s">%s</span>', parsedInput.valueph, parsedInput.valuepk, _coins(autoround(parsedInput.value), 'nocoins'))) |
|||
vdf('outputPH', string.format('<span class="mmg-variable mmg-output-ph" data-mmg-cost-ph="%s">%s</span>', parsedOutput.valueph, _coins(autoround(parsedOutput.valueph), 'nocoins'))) |
|||
vdf('outputPK', string.format('<span class="mmg-variable mmg-output-pk" data-mmg-cost-pk="%s">%s</span>', parsedOutput.valuepk, _coins(autoround(parsedOutput.valuepk), 'nocoins'))) |
|||
vdf('output', string.format('<span class="mmg-variable mmg-varieswithkph mmg-output" data-mmg-cost-ph="%s", data-mmg-cost-pk="%s">%s</span>', parsedOutput.valueph, parsedOutput.valuepk, _coins(autoround(parsedOutput.value), 'nocoins'))) |
|||
vdf('profitPH', string.format('<span class="mmg-variable mmg-profit-ph" data-mmg-cost-ph="%s">%s</span>', parsedOutput.valueph-parsedInput.valueph, _coins(autoround(parsedOutput.valueph-parsedInput.valueph), 'nocoins'))) |
|||
vdf('profitPK', string.format('<span class="mmg-variable mmg-profit-pk" data-mmg-cost-pk="%s">%s</span>', parsedOutput.valuepk-parsedInput.valuepk, _coins(autoround(parsedOutput.valuepk-parsedInput.valuepk), 'nocoins'))) |
|||
vdf('profit', string.format('<span class="mmg-variable mmg-varieswithkph mmg-profit" data-mmg-cost-ph="%s", data-mmg-cost-pk="%s">%s</span>', parsedOutput.valueph-parsedInput.valueph, parsedOutput.valuepk-parsedInput.valuepk, _coins(autoround(parsedOutput.value-parsedInput.value), 'nocoins'))) |
|||
else |
|||
vdf('input', string.format('<span class="mmg-input">%s</span>', parsedInput.value, _coins(autoround(parsedInput.value), 'nocoins'))) |
|||
vdf('output', string.format('<span class="mmg-input">%s</span>', parsedOutput.value, _coins(autoround(parsedOutput.value), 'nocoins'))) |
|||
vdf('profit', string.format('<span class="mmg-input">%s</span>', parsedOutput.value-parsedInput.value, _coins(autoround(parsedOutput.value-parsedInput.value), 'nocoins'))) |
|||
vdf('input_raw', parsedInput.value) |
|||
vdf('output_raw', parsedOutput.value) |
|||
vdf('profit_raw', parsedOutput.value-parsedInput.value) |
|||
end |
end |
||
end |
end |
||
return t |
|||
frame:callParserFunction('DISPLAYTITLE', title.subpageText) |
|||
frame:callParserFunction('DEFAULTSORT', title.subpageText) |
|||
local cats = '[[Category:Money making guides]]' |
|||
if args['Members'] == nil then |
|||
cats = cats .. '[[Category:Money making guides without a membership status]]' |
|||
elseif not(yn(args['Members'])) then |
|||
cats = cats .. '[[Category:MMG/F2P]]' |
|||
end |
|||
local final_profit = parsedOutput.value - parsedInput.value |
|||
local set_smw = true |
|||
if final_profit <= 100000 and yn(args['Members']) then |
|||
set_smw = false |
|||
cats = cats .. '[[Category:Obsolete money making guides]]' |
|||
elseif ((final_profit <= 20000) and (parsedInput.value>0) ) then |
|||
set_smw = false |
|||
cats = cats .. '[[Category:Obsolete money making guides]]' |
|||
elseif final_profit <= 15000 then |
|||
set_smw = false |
|||
cats = cats .. '[[Category:Obsolete money making guides]]' |
|||
elseif yn(args.Exclude) then |
|||
set_smw = false |
|||
cats = cats .. '[[Category:Obsolete money making guides]]' |
|||
end |
|||
if args["Category"] then |
|||
local mmgcat = ({ |
|||
["Combat/Low"] = "[[Category:MMG/Combat]]", |
|||
["Combat/Mid"] = "[[Category:MMG/Combat]]", |
|||
["Combat/High"] = "[[Category:MMG/Combat]]", |
|||
["Combat"] = "[[Category:MMG/Combat]]", |
|||
["Skilling"] = "[[Category:MMG/Skilling]]", |
|||
["Processing"] = "[[Category:MMG/Processing]]", |
|||
["Recurring"] = "[[Category:MMG/Recurring]]", |
|||
["Collecting"] = "[[Category:MMG/Collecting]]" |
|||
})[args["Category"]] |
|||
if mmgcat == nil then |
|||
cats = cats .. '[[Category:Money making guides with an invalid category]]' |
|||
else |
|||
cats = cats .. mmgcat |
|||
end |
|||
else |
|||
cats = cats .. '[[Category:Money making guides without a category]]' |
|||
end |
|||
if not onmain() then |
|||
cats = '' |
|||
end |
|||
if set_smw then |
|||
local smw_data = { |
|||
members = yn(args.Members or 'yes', true), |
|||
skill = args.Skill, |
|||
activity = args.Activity, |
|||
category = args.Category, |
|||
skillcategory = args.SkillCategory, |
|||
intensity = args.Intensity, |
|||
isperkill = is_per_kill, |
|||
version = args.Version, |
|||
inputs = parsedInput.list, |
|||
outputs = parsedOutput.list |
|||
} |
|||
if is_per_kill then |
|||
smw_data.prices = { |
|||
input_perhour=parsedInput.valueph, |
|||
input_perkill=parsedInput.valuepk, |
|||
output_perhour=parsedOutput.valueph, |
|||
output_perkill=parsedOutput.valuepk, |
|||
default_kph=tonumber(args.kph) or 1, |
|||
kph_text=kph_text, |
|||
default_value=parsedOutput.value - parsedInput.value, |
|||
} |
|||
else |
|||
smw_data.prices = { |
|||
input=parsedInput.value, |
|||
output=parsedOutput.value, |
|||
value=parsedOutput.value - parsedInput.value |
|||
} |
|||
end |
|||
smw_data = mw.text.killMarkers(mw.text.nowiki(mw.text.jsonEncode(smw_data))) |
|||
mw.smw.set({ |
|||
['MMG value']=parsedOutput.value - parsedInput.value, |
|||
['MMG JSON']=smw_data |
|||
}) |
|||
end |
|||
return ret, cats |
|||
end |
end |
||
-- Calculate the profit and do nothing else. |
|||
function p.profit(frame) |
|||
local frame = frame or mw.getCurrentFrame() |
|||
local args = frame:getParent().args -- Template args, NOT #invoke args |
|||
return handleOutputs(args).value - handleInputs(args).value |
|||
end |
|||
-- Implements handleInputs and handleOutputs |
|||
-- See those functions for further details |
|||
function handleIteratedArgs(args, prefix, max_iters) |
|||
local frame = mw.getCurrentFrame() |
|||
local items = {} |
|||
local total_item_value = 0 |
|||
local textlines = {} |
|||
local is_per_kill = yn(args.isperkill) |
|||
local defaultKPH = tonumber(args.kph) or 1 |
|||
local value_per_kill = 0 |
|||
local value_per_hour = 0 |
|||
for i=1,max_iters,1 do |
|||
if not args[prefix..i] then break end |
|||
local pri = prefix..i |
|||
local span = mw.html.create('span') |
|||
span:addClass('mmg-itemline mmg-'..prefix:lower()) |
|||
local name = args[pri] |
|||
local qty_param = args[pri..'num'] |
|||
local actual_qty = nil |
|||
local value_param = args[pri..'value'] |
|||
local actual_value = nil |
|||
local is_per_hour = not is_per_kill |
|||
if is_per_kill and yn(args[pri..'isph']) then |
|||
is_per_hour = true |
|||
end |
|||
-- Keep track of sanity check states - we want to handle them gracefully later. |
|||
local invalid_qty_present = false |
|||
local invalid_value_present = false |
|||
local failed_ge_lookup = false |
|||
local pricetype = '' |
|||
if qty_param then |
|||
actual_qty = tonumber(qty_param) or expr(qty_param) |
|||
invalid_qty_present = not actual_qty |
|||
actual_qty = actual_qty or 1 |
|||
-- If the given quantity doesn't look like a number, we'll default to 1 |
|||
-- but we should probably alert the user |
|||
-- since they might want to fix that |
|||
else |
|||
-- Default value of 1 |
|||
actual_qty = 1 |
|||
end |
|||
if value_param then |
|||
-- Again, if it was specified, it should be a number |
|||
-- If it isn't, we pretend it wasn't specified |
|||
-- but we alert the user because it's probably not what they want |
|||
actual_value = tonumber(value_param) or expr(value_param) |
|||
invalid_value_present = not actual_value |
|||
pricetype = 'value' |
|||
end |
|||
-- If we got the value earlier, skip this part |
|||
if not actual_value then |
|||
-- Here we try to find an exchange price |
|||
-- If we get here, and we can't get an exchange price |
|||
-- we default to 0. |
|||
-- This is almost certainly not what the user wants, |
|||
-- so we warn them about it. |
|||
local success, price = pcall(gePrice, name) |
|||
actual_value = success and tonumber(price) -- This is awful but still pleasant somehow |
|||
failed_ge_lookup = not actual_value |
|||
actual_value = actual_value or 0 |
|||
pricetype = 'gemw' |
|||
end |
|||
local this_item_value, this_item_qty, attrName |
|||
local attrVal = actual_qty * actual_value |
|||
if is_per_kill and not is_per_hour then |
|||
span:addClass('mmg-varieswithkph') |
|||
this_item_qty = actual_qty * defaultKPH |
|||
this_item_value = attrVal * defaultKPH |
|||
value_per_kill = value_per_kill + attrVal |
|||
attrName = 'data-mmg-cost-pk' |
|||
else |
|||
this_item_qty = actual_qty |
|||
this_item_value = attrVal |
|||
value_per_hour = value_per_hour + attrVal |
|||
attrName = 'data-mmg-cost-ph' |
|||
end |
|||
total_item_value = total_item_value + this_item_value |
|||
span:tag('span'):addClass('mmg-quantity'):attr('data-mmg-qty', actual_qty):wikitext(autoround(this_item_qty, true)) |
|||
if invalid_qty_present then |
|||
span:node(warning('Could not interpret \''..qty_param..'\' as a number, defaulting to 1')) |
|||
end |
|||
span:wikitext(string.format(' × [[File:%s.png|link=%s]] [[%s]] (', name, name, name)) |
|||
span:tag('span'):addClass('mmg-cost'):attr(attrName, attrVal):wikitext(_coins(autoround(this_item_value), 'nocoins')) |
|||
span:wikitext(')') |
|||
if invalid_value_present then |
|||
span:node(warning('Could not interpret \''..value_param..'\' as a number, ignoring.')) |
|||
end |
|||
if failed_ge_lookup then |
|||
span:node(warning('Could not find exchange price for item \''..name..'\', please double-check the spelling')) |
|||
span:wikitext('[[Category:Money making guides with a failed GE lookup]]') |
|||
end |
|||
if args[pri..'note'] then |
|||
span:tag('span'):addClass('mmg-note'):wikitext(' ',args[pri..'note']) |
|||
end |
|||
table.insert(textlines, span) |
|||
table.insert(items, {name = name, qty = actual_qty, value = actual_value, isph = is_per_hour, pricetype=pricetype}) |
|||
end |
|||
return {value = total_item_value, valuepk = value_per_kill, valueph = value_per_hour, spans = textlines, list = items} |
|||
end |
|||
function make_rec_row(fullpagename, raw_data, ismulti) |
|||
-- args are the args supplied to the template, (or a subset of them contining all input arguments) |
|||
local data = mw.text.jsonDecode(mw.text.decode(raw_data)) |
|||
-- Returns a table. The table has three keys: 'value', 'text', and 'as_table' |
|||
local tr = mw.html.create('tr') |
|||
---- 'value' contains the total value of the inputs specified by args |
|||
local pagename, pagelink |
|||
---- 'text' contains a formatted string based on the inputs specified by args. This can be directly plugged into the HTML table. |
|||
pagelink = fullpagename..'|'..string.match(fullpagename, '/(.*)') |
|||
---- 'list' contains all the inputs as a Lua list. Each input is represented by a table with the following keys |
|||
if ismulti and data.version then |
|||
------ 'name' being the name of the item |
|||
pagename = '[['..pagelink..' ('..data.version..')]]' |
|||
------ 'value' being the value of the item in question |
|||
else |
|||
------ 'qty' being the quantity specified for the item |
|||
pagename = '[['..pagelink..']]' |
|||
function handleInputs(args) |
|||
return handleIteratedArgs(args, 'Input', MAX_INPUTS) |
|||
end |
|||
-- args are the args supplied to the template, (or a subset of them contining all output arguments) |
|||
-- Returns a table. The table has two keys: 'value', and 'text' |
|||
---- 'value' contains the total value of the outputs specified by args |
|||
---- 'text' contains a formatted string based on the outputs specified by args. This can be directly plugged into the HTML table. |
|||
---- 'list' contains all the outputs as a Lua list. Each output is represented by a table with the following keys |
|||
------ 'name' being the name of the item |
|||
------ 'value' being the value of the item in question |
|||
------ 'qty' being the quantity specified for the item |
|||
function handleOutputs(args) |
|||
return handleIteratedArgs(args, 'Output', MAX_OUTPUTS) |
|||
end |
|||
function handleXP(args) |
|||
local frame = mw.getCurrentFrame() |
|||
local items = {} |
|||
local textlines = {} |
|||
local is_per_kill = yn(args.isperkill) |
|||
local defaultKPH = tonumber(args.kph) or 1 |
|||
for i=1,MAX_XP,1 do |
|||
if not args['Experience'..i] then break end |
|||
local pri = 'Experience'..i |
|||
local span = mw.html.create('span') |
|||
span:addClass('mmg-xpline') |
|||
local skill = args[pri] |
|||
local qty_param = args[pri..'num'] |
|||
local actual_qty = tonumber(qty_param) or expr(qty_param) or 0 |
|||
local is_per_hour = not is_per_kill |
|||
if is_per_kill and yn(args[pri..'isph']) then |
|||
is_per_hour = true |
|||
end |
|||
local this_item_value, attrName |
|||
if is_per_kill and not is_per_hour then |
|||
span:addClass('mmg-varieswithkph') |
|||
this_item_value = actual_qty * defaultKPH |
|||
attrName = 'data-mmg-xp-pk' |
|||
else |
|||
this_item_value = actual_qty |
|||
attrName = 'data-mmg-xp-ph' |
|||
end |
|||
-- TODO modulise SCP |
|||
span:attr(attrName, actual_qty) |
|||
:wikitext(frame:expandTemplate{title='SCP', args = { skill, autoround(this_item_value, true) }}) |
|||
table.insert(textlines, span) |
|||
table.insert(items, { skill = skill, xp = actual_qty, isph = is_per_hour }) |
|||
end |
end |
||
local val = calc_value(data.outputs, 0) - calc_value(data.inputs, 0) |
|||
return { spans = textlines, items = items } |
|||
local c_class |
|||
end |
|||
if val > 0 then |
|||
c_class = 'coins-pos' |
|||
-- Creates a neat little warning message |
|||
elseif val < 0 then |
|||
function warning(msg) |
|||
c_class = 'coins-neg' |
|||
return mw.html.create('span') |
|||
:addClass('mmg-warning') |
|||
:css({ |
|||
['border-bottom'] = '1px red dotted', |
|||
color = 'red', |
|||
cursor = 'help' |
|||
}) |
|||
:attr('title', msg) |
|||
:wikitext('*') |
|||
end |
|||
function p.recurringTable(frame) |
|||
return p._recurringTable(frame, frame:getParent().args) |
|||
end |
|||
function p._recurringTable(frame, args) |
|||
local parsedInput = handleInputs(args) |
|||
local parsedOutput = handleOutputs(args) |
|||
local timeAsString = nil |
|||
local num_good, numMinutes = pcall(mw.ext.ParserFunctions.expr, args['Activity Time']) |
|||
numMinutes = tonumber(numMinutes) |
|||
if not num_good or not numMinutes then |
|||
numMinutes = 1 |
|||
end |
|||
if numMinutes < 1 then |
|||
local seconds = numMinutes * 60 |
|||
timeAsString = seconds..' '..lang:plural(seconds, 'second', 'seconds') |
|||
else |
else |
||
c_class = '' |
|||
timeAsString = numMinutes..' '..lang:plural(numMinutes, 'minute', 'minutes') |
|||
end |
end |
||
tr :tag('td') |
|||
local tbl = mw.html.create('table') |
|||
:wikitext(pagename) |
|||
local inputTR = mw.html.create('tr') |
|||
local inputTD = inputTR:tag('td') |
|||
local outputTD = inputTR:tag('td') |
|||
for i,v in ipairs(parsedInput.spans) do |
|||
inputTD:node(v) |
|||
end |
|||
for i,v in ipairs(parsedOutput.spans) do |
|||
outputTD:node(v) |
|||
end |
|||
-- This is one statement, defining a single variable. It goes on for 120 lines. I don't know how I feel about this tbh. |
|||
-- I mean how the heck would you even document this, for a start? |
|||
tbl:addClass('wikitable') -- I guess it's pretty self-documenting if you know HTML |
|||
:cssText('width:100%;text-align:center;') -- But like... |
|||
:tag('tr') |
|||
:tag('caption') |
|||
:wikitext(args['Activity']) |
|||
:done() |
|||
:done() |
:done() |
||
:tag(' |
:tag('td') |
||
: |
:attr('data-sort-value', val) |
||
:tag('span') |
|||
: |
:addClass('coins') |
||
: |
:addClass(c_class) |
||
: |
:wikitext(round1k(val, true)) |
||
:wikitext(args['Image']) |
|||
:done() |
:done() |
||
:done() |
:done() |
||
:tag(' |
:tag('td') |
||
:wikitext(timefunc._m_to_c(tostring(data.time))) |
|||
:tag('td') |
|||
:wikitext(_coins(round(parsedOutput.value - parsedInput.value, 0), 'coins'), ' per instance') |
|||
:done() |
|||
:done() |
:done() |
||
:tag(' |
:tag('td') |
||
:attr('data-sort-value', val*60/data.time) |
|||
:tag('th') |
|||
:tag('span') |
|||
:addClass('coins') |
|||
:addClass(c_class) |
|||
:wikitext(round1k(val*60/data.time, true)) |
|||
:done() |
:done() |
||
:done() |
:done() |
||
:tag(' |
:tag('td') |
||
:attr('data-sort-value', timefunc._w_to_c(tostring(data.recurrence))) |
|||
:tag('td') |
|||
:wikitext(timefunc._w_to_c(tostring(data.recurrence))) |
|||
:done() |
|||
:done() |
:done() |
||
:tag(' |
:tag('td') |
||
: |
:addClass('plainlist') |
||
:newline() |
|||
:wikitext('Minimum recurrence time') |
|||
: |
:wikitext(data.skill) |
||
:done() |
:done() |
||
:tag(' |
:tag('td') |
||
: |
:wikitext(data.category) |
||
:wikitext(args['Recurrence Time']) |
|||
:done() |
|||
:done() |
:done() |
||
:tag(' |
:tag('td') |
||
:wikitext(MEMBERS_ICON[data.members]) |
|||
:tag('th') |
|||
:wikitext('Effective profit') |
|||
:done() |
|||
:done() |
:done() |
||
return val, tr |
|||
:tag('tr') |
|||
end |
|||
:tag('td') |
|||
:wikitext(_coins(tostring(parsedOutput.value - parsedInput.value)..' * 60 / ('..args['Activity Time']..') round 0', 'coins') .. ' per hour') -- A bit messy but it should do the job. Assuming Activity Time is a well-behaved number, of course. |
|||
function p.rec(frame) |
|||
:done() |
|||
local data = mw.smw.ask({'[[MMG recurring JSON::+]]', '[[MMG value::+]]', '?MMG recurring JSON', '?=#', limit=10000}) |
|||
local t = mw.html.create('table') |
|||
t :addClass('wikitable sortable sticky-header align-right-2 align-right-3 align-right-4 align-right-5 align-center-7 align-center-8') |
|||
:tag('caption') |
|||
:wikitext('[[Money making guide|All guides]] • [[Money making guide/Collecting|Collecting]] • [[Money making guide/Combat|Combat]] • [[Money making guide/Processing|Processing]] • [[Money making guide/Skilling|Skilling]] • [[Money making guide/Recurring|Recurring]] • [[Money making guide/Free-to-play|Free-to-play]]') |
|||
:done() |
:done() |
||
:tag('tr') |
:tag('tr') |
||
:tag('th') |
:tag('th') |
||
:wikitext(' |
:wikitext('Method') |
||
:done() |
:done() |
||
:tag('th') |
:tag('th') |
||
:wikitext(' |
:wikitext('Profit') |
||
:done() |
:done() |
||
:done() |
|||
:tag('tr') |
|||
:tag('td') |
|||
:addClass('plainlist') |
|||
:newline() |
|||
:wikitext(args['Skill'] or 'None') |
|||
:done() |
|||
:tag('td') |
|||
:addClass('plainlist') |
|||
:newline() |
|||
:wikitext(args['Quest'] or 'None') |
|||
:done() |
|||
:done() |
|||
:tag('tr') |
|||
:tag('th') |
:tag('th') |
||
:wikitext(' |
:wikitext('Time') |
||
:done() |
:done() |
||
:tag('th') |
:tag('th') |
||
:wikitext(' |
:wikitext('Effective<br>profit') |
||
:done() |
:done() |
||
:done() |
|||
:tag('tr') |
|||
:tag('td') |
|||
:addClass('plainlist') |
|||
:newline() |
|||
:wikitext(args['Item'] or 'None') |
|||
:done() |
|||
:tag('td') |
|||
:addClass('plainlist') |
|||
:newline() |
|||
:wikitext(args['Other'] or 'None') |
|||
:done() |
|||
:done() |
|||
:tag('tr') |
|||
:tag('th') |
:tag('th') |
||
:wikitext(' |
:wikitext('Recurrence<br>time') |
||
:done() |
:done() |
||
:tag('th') |
:tag('th') |
||
:wikitext(' |
:wikitext('Skills') |
||
:done() |
:done() |
||
:done() |
|||
:tag('tr') |
|||
:tag('td') |
|||
:wikitext(args['Other Benefits'] or 'None') |
|||
:done() |
|||
:tag('td') |
|||
:wikitext(args['Location'] or 'Anywhere') -- Sensible enough as a default |
|||
:done() |
|||
:done() |
|||
:tag('tr') |
|||
:tag('th') |
:tag('th') |
||
:wikitext(' |
:wikitext('Category') |
||
:wikitext(' (', _coins(round(parsedInput.value, 0), 'coins'), ')') |
|||
:done() |
:done() |
||
:tag('th') |
:tag('th') |
||
:wikitext(' |
:wikitext('Members') |
||
:css('width', '65px') |
|||
:wikitext(' (', _coins(round(parsedOutput.value, 0), 'coins'), ')') |
|||
:done() |
:done() |
||
local methods = {} |
|||
:node(inputTR) |
|||
for i,v in ipairs(data) do |
|||
:done() |
|||
if type(v['MMG recurring JSON']) == 'table' then |
|||
for j,u in ipairs(v['MMG recurring JSON']) do |
|||
frame:callParserFunction('DISPLAYTITLE', title.subpageText) |
|||
table.insert(methods, { make_rec_row(v[1], u, true) }) |
|||
frame:callParserFunction('DEFAULTSORT', title.subpageText) |
|||
end |
|||
else |
|||
local cats = '[[Category:Money making guides]][[Category:MMG/Recurring]]' |
|||
table.insert(methods, { make_rec_row(v[1], v['MMG recurring JSON'], false) }) |
|||
end |
|||
local final_profit = parsedOutput.value - parsedInput.value |
|||
local set_smw = true |
|||
if final_profit <= 0 then |
|||
set_smw = false |
|||
cats = cats .. '[[Category:Obsolete money making guides]]' |
|||
end |
end |
||
table.sort(methods, function(a,b) return a[1]>b[1] end) |
|||
for i,v in ipairs(methods) do |
|||
if set_smw then |
|||
t:newline() |
|||
local smw_data = { |
|||
t:node(v[2]) |
|||
members = yn(args.Members or 'yes', true), |
|||
skill = args.Skill, |
|||
activity = args.Activity, |
|||
category = args.Category, |
|||
version = args.Version, |
|||
time = numMinutes, |
|||
recurrence = args['Recurrence Time'], |
|||
prices = { |
|||
input=parsedInput.value, |
|||
output=parsedOutput.value, |
|||
value=parsedOutput.value - parsedInput.value |
|||
}, |
|||
inputs = parsedInput.list, |
|||
outputs = parsedOutput.list |
|||
} |
|||
smw_data = mw.text.killMarkers(mw.text.nowiki(mw.text.jsonEncode(smw_data))) |
|||
mw.smw.set({ |
|||
['MMG value']=parsedOutput.value - parsedInput.value, |
|||
['MMG recurring JSON']=smw_data |
|||
}) |
|||
end |
end |
||
t:newline() |
|||
return |
return t |
||
end |
end |
||
return p |
return p |
||
-- </nowiki> |
Latest revision as of 11:23, 17 October 2024
Documentation for this module may be created at Module:Mmgtable/displaysandbox/doc
-- <nowiki>
local timefunc = require('Module:Time')
local exg = require('Module:Exchange')._price
local p = {}
local lang = mw.getContentLanguage()
function gep(x)
return exg(x, 1, nil, nil, 0)
end
local MEMBERS_ICON = {
[false] = "[[File:Free-to-play icon.png|20px|center|link=Free-to-play]]",
[true] = "[[File:Member icon.png|20px|center|link=Members]]"
}
function round1k(x, f)
if not tonumber(x) then
return x
end
local _x = math.abs(x)
_x = 1000 * math.floor(_x / 1000 + 0.5)
if x < 0 then
_x = _x * -1
end
if f then
return lang:formatNum(_x)
end
return _x
end
function round1dp(x, f)
if not tonumber(x) then
return x
end
local _x = math.abs(x)
_x = math.floor(_x * 10 + 0.5) / 10
if x < 0 then
_x = _x * -1
end
if f then
return lang:formatNum(_x)
end
return _x
end
function calc_value(args, kph)
local total = 0
for i,v in ipairs(args) do
local val
if v.pricetype == 'value' then
val = v.qty * v.value
elseif v.pricetype == 'gemw' then
val = gep(v.name) * v.qty
end
if kph>0 and not v.isph then
val = val * kph
end
total = total + val
end
return total
end
function make_row(fullpagename, raw_data, ismulti)
local data = mw.text.jsonDecode(mw.text.decode(raw_data))
local tr = mw.html.create('tr')
local pagename, pagelink
pagelink = fullpagename..'|'..string.match(fullpagename, '/(.*)')
if ismulti and data.version then
pagename = '[['..pagelink..' ('..data.version..')]]'
else
pagename = '[['..pagelink..']]'
end
local rowcat = data.category
if data.skillcategory then
rowcat = rowcat .. '/' .. data.skillcategory
end
local val, c_class
local inputval = calc_value(data.inputs, data.prices.default_kph or 0)
local outputval = calc_value(data.outputs, data.prices.default_kph or 0)
val = outputval - inputval
if val > 0 then
c_class = 'coins-pos'
elseif val < 0 then
c_class = 'coins-neg'
else
c_class = ''
end
local intensity = (data.intensity == 'High') and 3 or (data.intensity == 'Moderate') and 2 or (data.intensity == 'Low') and 1 or 0
tr :addClass('mmg-list-table-row')
:tag('td')
:wikitext(pagename)
:css('max-width', '300px')
:css('word-wrap', 'break-word')
:done()
:tag('td')
:addClass('mmg-list-table-profit-cell')
:tag('span')
:addClass('coins')
:addClass(c_class)
:wikitext(round1k(val, true))
:done()
:done()
:tag('td')
:addClass('mmg-list-table-kph-cell')
:wikitext(data.prices.default_kph)
:css('max-width', '125px')
:css('min-width', '125px')
:done()
:tag('td')
:addClass('plainlist')
:newline()
:wikitext(data.skill)
:css('max-width', '400px')
:css('word-wrap', 'break-word')
:done()
:tag('td')
:wikitext(rowcat)
:done()
:tag('td')
:wikitext(data.intensity)
:attr('data-sort-value', intensity)
:done()
:tag('td')
:wikitext(MEMBERS_ICON[data.members])
:done()
return val, data.category, tr
end
function p.main(frame)
local args = frame:getParent().args
local query = {'[[MMG JSON::+]]', '?MMG JSON', '?=#', limit=10000}
if args[1] then
table.insert(query, 2, '[[Category:'..args[1]..']]')
end
local data = mw.smw.ask(query)
local t = mw.html.create('table')
t :addClass('wikitable sortable sticky-header align-right-2 align-center-5 align-center-6 align-center-7 mmg-list-table')
:tag('tr')
:tag('th')
:wikitext('Method')
:css('word-wrap', 'break-word')
:css('max-width', '300px')
:done()
:tag('th')
:wikitext('Hourly Profit')
:css('max-width', '100px')
:done()
:tag('th')
:wikitext('Actions per hour')
:addClass('unsortable')
:done()
:tag('th')
:wikitext('Skills')
:css('word-wrap', 'break-word')
:css('max-width', '400px')
:addClass('unsortable')
:done()
:tag('th')
:wikitext('Category')
:done()
:tag('th')
:wikitext('Intensity')
:css('width', '65px')
:done()
:tag('th')
:wikitext('Members')
:css('width', '65px')
:done()
local methods = {}
for i,v in ipairs(data) do
if(string.find(v[1], 'Money making guide/')) then
if type(v['MMG JSON']) == 'table' then
for j,u in ipairs(v['MMG JSON']) do
table.insert(methods, { make_row(v[1], u, true) })
end
else
table.insert(methods, { make_row(v[1], v['MMG JSON'], false) })
end
end
end
table.sort(methods, function(a,b) return a[1]>b[1] end)
for i,v in ipairs(methods) do
if v[1] > 0 then
t:newline():node(v[3])
end
end
return t
end
function make_rec_row(fullpagename, raw_data, ismulti)
local data = mw.text.jsonDecode(mw.text.decode(raw_data))
local tr = mw.html.create('tr')
local pagename, pagelink
pagelink = fullpagename..'|'..string.match(fullpagename, '/(.*)')
if ismulti and data.version then
pagename = '[['..pagelink..' ('..data.version..')]]'
else
pagename = '[['..pagelink..']]'
end
local val = calc_value(data.outputs, 0) - calc_value(data.inputs, 0)
local c_class
if val > 0 then
c_class = 'coins-pos'
elseif val < 0 then
c_class = 'coins-neg'
else
c_class = ''
end
tr :tag('td')
:wikitext(pagename)
:done()
:tag('td')
:attr('data-sort-value', val)
:tag('span')
:addClass('coins')
:addClass(c_class)
:wikitext(round1k(val, true))
:done()
:done()
:tag('td')
:wikitext(timefunc._m_to_c(tostring(data.time)))
:done()
:tag('td')
:attr('data-sort-value', val*60/data.time)
:tag('span')
:addClass('coins')
:addClass(c_class)
:wikitext(round1k(val*60/data.time, true))
:done()
:done()
:tag('td')
:attr('data-sort-value', timefunc._w_to_c(tostring(data.recurrence)))
:wikitext(timefunc._w_to_c(tostring(data.recurrence)))
:done()
:tag('td')
:addClass('plainlist')
:newline()
:wikitext(data.skill)
:done()
:tag('td')
:wikitext(data.category)
:done()
:tag('td')
:wikitext(MEMBERS_ICON[data.members])
:done()
return val, tr
end
function p.rec(frame)
local data = mw.smw.ask({'[[MMG recurring JSON::+]]', '[[MMG value::+]]', '?MMG recurring JSON', '?=#', limit=10000})
local t = mw.html.create('table')
t :addClass('wikitable sortable sticky-header align-right-2 align-right-3 align-right-4 align-right-5 align-center-7 align-center-8')
:tag('caption')
:wikitext('[[Money making guide|All guides]] • [[Money making guide/Collecting|Collecting]] • [[Money making guide/Combat|Combat]] • [[Money making guide/Processing|Processing]] • [[Money making guide/Skilling|Skilling]] • [[Money making guide/Recurring|Recurring]] • [[Money making guide/Free-to-play|Free-to-play]]')
:done()
:tag('tr')
:tag('th')
:wikitext('Method')
:done()
:tag('th')
:wikitext('Profit')
:done()
:tag('th')
:wikitext('Time')
:done()
:tag('th')
:wikitext('Effective<br>profit')
:done()
:tag('th')
:wikitext('Recurrence<br>time')
:done()
:tag('th')
:wikitext('Skills')
:done()
:tag('th')
:wikitext('Category')
:done()
:tag('th')
:wikitext('Members')
:css('width', '65px')
:done()
local methods = {}
for i,v in ipairs(data) do
if type(v['MMG recurring JSON']) == 'table' then
for j,u in ipairs(v['MMG recurring JSON']) do
table.insert(methods, { make_rec_row(v[1], u, true) })
end
else
table.insert(methods, { make_rec_row(v[1], v['MMG recurring JSON'], false) })
end
end
table.sort(methods, function(a,b) return a[1]>b[1] end)
for i,v in ipairs(methods) do
t:newline()
t:node(v[2])
end
t:newline()
return t
end
return p
-- </nowiki>