Module:Sandbox/User:Alsang: Difference between revisions
Jump to navigation
Jump to search
Content added Content deleted
(set up for passive LW, with both recipes and activities) |
No edit summary |
||
Line 1: | Line 1: | ||
require('strict') |
|||
require('Module:Mw.html extension') |
require('Module:Mw.html extension') |
||
local recipe = require('Module:Infobox Recipe') -- to make use of its extensive material searching function |
|||
local param = require( 'Module:Paramtest' ) |
|||
local currency = require('Module:Currency') |
|||
local discount = require('Module:MerchantHideDiscount') |
|||
local lang = mw.getContentLanguage() |
|||
local p = {} |
local p = {} |
||
function sort_entries(entry1, entry2) |
|||
-- non dynamic module, no inputs |
|||
-- Sort the variants based on the sum of the profession levels |
|||
function p.main() |
|||
local entry1_value = (entry1['Profession Level A'] or 0) + (entry1['Profession Level B'] or 0) |
|||
-- returns only directly needed parameter needed for the row, |
|||
local entry2_value = (entry2['Profession Level A'] or 0) + (entry2['Profession Level B'] or 0) |
|||
-- other parameters are determined by subqueries of chained pages |
|||
return entry1_value < entry2_value |
|||
end |
|||
-- this is in testing, uncomment out one line to test that type of query |
|||
-- requires pages to be made and have recipes on them or it tends to get an error |
|||
function get_table_info(args, get_images) |
|||
-- query types that include non-recipe passive activities behave weird and need to be fixed |
|||
local variant_name = args.variant or mw.title.getCurrentTitle().fullText |
|||
local query = { |
local query = { |
||
'[[Variant of::'..variant_name..']]', |
|||
--'[[Uses facility::One Handed Range (bonewright)||One Handed Melee Workbench (bonewright)||Two Handed Range Workbench (bonewright)||Two Handed Melee Workbench (bonewright)||Shield Vice (bonewright)]]', -- bonewright active |
|||
'?Profession A #', -- Adding a # to this query makes it return plaintext instead of a link |
|||
--'[[Uses facility::Knickknacks workbench]] OR [[Variant of::Divination||Sabertooth Cat Skeleton||Wooly Mammoth Skeleton||Triceratops Skeleton||T-Rex Skeleton||Wendigo Skeleton||Bonemeal Cement]]', -- bonewright passive |
|||
'?Profession B #', |
|||
--'[[Uses facility::One Handed Range (stonemason)||One Handed Melee Workbench (stonemason)||Two Handed Range Workbench (stonemason)||Two Handed Melee Workbench (stonemason)||Shield Vice (stonemason)]]', -- stonemason active |
|||
'?Profession Level A', |
|||
--'[[Uses facility::Stoneware bench]] OR [[Variant of::Statue Sculpture||Ornamental Scuplture||Column Sculpture||Bust Sculpture]]', -- stonemason passive |
|||
'?Profession Level B', |
|||
--'[[Uses facility::Goblin Forge||Gnome Forge (skill node)]]', -- blacksmith active |
|||
'?Profession Level A High', |
|||
--'[[Uses facility::Jewelry Bench]]', -- blacksmith passive, no non-recipe |
|||
'?Profession Level B High', |
|||
'[[-Sold item.Sold by::Leather Goods]] OR [[Variant of::Upholstery]]', -- leatherworker passive, but the Upholstery activities dont have pages yet |
|||
'?Variant |
'?Variant name', |
||
get_images and '?Image#64px;x64px = Image' or nil, |
|||
'?Profession Level A = lvl', |
|||
limit = args.limit or 500, |
|||
'?Recipe JSON = recipeJSON', |
|||
'?Activity XP = XP', |
|||
'?Activity duration = duration', |
|||
'?Value = sell', |
|||
'?Activity coins = coins', |
|||
'sort = Profession Level A', |
|||
'limit = 500' |
|||
} |
} |
||
local |
local smw_data = mw.smw.ask(query) |
||
if not smw_data then |
|||
return nil |
|||
-- criterion for if an entry is a recipe, or a passive activity |
|||
for _, item in ipairs(results) do |
|||
--item.passiveActivity = false -- can delete this entire FOR statement unless there are non-recipe passive activities on the table |
|||
item.passiveActivity = (item.variant=='Upholstery') -- leatherworker has not got upholstery implemented yet, do not use |
|||
end |
end |
||
table.sort(smw_data, sort_entries) |
|||
return smw_data |
|||
results = p.formatResults(results) |
|||
return p.displayTable(results) |
|||
--for debugging |
|||
--return '<pre>'..mw.text.jsonEncode(results, mw.text.JSON_PRETTY)..'</pre>' |
|||
end |
end |
||
function p.variants_header(args) |
|||
-- makes the html for the cells containing currency directly |
|||
local smw_data = get_table_info(args) |
|||
-- Replaces nil with an "unknown" cell |
|||
local contents |
|||
local function currency_cell(amount) |
|||
if |
if smw_data then |
||
local formatted = {} |
|||
return mw.html.create('td') |
|||
for _, entry in ipairs(smw_data) do |
|||
:addClass('table-bg-gray') |
|||
local variant_name = entry['Variant name'] |
|||
:css{ ['text-align'] = 'center' } |
|||
if variant_name then |
|||
:attr{ colspan = '10' } |
|||
local page = entry[1] |
|||
:wikitext("''unknown''") |
|||
local pipe = page:find('|') |
|||
:done() |
|||
if pipe then |
|||
end |
|||
page = page:sub(3, pipe-1) |
|||
return currency._cell(amount, { html = 'yes' }) |
|||
end |
|||
-- do calculations and determine strings to go in cells |
|||
function p.formatResults(results) |
|||
local discountList = discount.main() |
|||
-- iterate through products |
|||
for _, item in ipairs(results) do |
|||
-- recipe in a workable format |
|||
if item.passiveActivity then |
|||
-- if it is a passive activity that is not a recipe |
|||
-- some fields are easy to work out |
|||
item.outputQuantity = 1 |
|||
item.materials = {} |
|||
item.intermediates = {} |
|||
item.buy = 0 |
|||
item.sell = item.coins |
|||
else |
|||
local unpackJSON = mw.text.jsonDecode(item.recipeJSON) |
|||
item.outputQuantity = unpackJSON.output[1].quantity |
|||
-- call the module:infobox recipe to extensively search for |
|||
-- all raw materials (to add to prices) |
|||
-- all intermediate materials (for intermediate XP and duration) |
|||
local Materials = recipe._getTrueRawMaterials(unpackJSON.materials) |
|||
if next(Materials) ~= nil then |
|||
item.materials = Materials.rawMaterials |
|||
item.intermediates = Materials.intermediateMaterials |
|||
end |
|||
-- iterate through materials, adding buy price to running total (individuals not needed) |
|||
item.buy = 0 |
|||
item.buyDiscount = 0 |
|||
for _, material in ipairs(item.materials) do |
|||
--shamelessley lifted from Module:Products |
|||
local shopPriceQuery = '[[:+]][[Sold item::' .. material.name .. ']]|?Shop buy price|mainlabel=' .. material.name |
|||
local shopPriceResult = mw.smw.ask(shopPriceQuery) or {} |
|||
local shopPrice = 0 |
|||
if shopPriceResult[1] and shopPriceResult[1]["Shop buy price"] then |
|||
shopPrice = tonumber(shopPriceResult[1]["Shop buy price"]) or 0 |
|||
else |
else |
||
page = page:sub(3, -3) |
|||
end |
end |
||
if page:sub(1, 1 + #variant_name + #' (grade ') == ':' .. variant_name .. ' (grade ' and page:sub(-1) == ')' then |
|||
item.buy = item.buy and item.buy + shopPrice * material.quantity |
|||
local grade_number = page:sub(2 + #variant_name + #' (grade ', -2) |
|||
mw.log('"' .. grade_number .. '"') |
|||
--with the merchant discount, buy price is sell price |
|||
variant_name = 'Grade ' .. grade_number |
|||
local shopPriceQuery = '[[:+]][[Sold item::' .. material.name .. ']]|?Shop sell price|mainlabel=' .. material.name |
|||
local shopPriceResult = mw.smw.ask(shopPriceQuery) or {} |
|||
local shopPrice = 0 |
|||
if shopPriceResult[1] and shopPriceResult[1]["Shop sell price"] then |
|||
shopPrice = tonumber(shopPriceResult[1]["Shop sell price"]) or 0 |
|||
else |
|||
item.buyDiscount = nil |
|||
end |
end |
||
table.insert(formatted, ('[[%s|%s]]'):format(page, variant_name)) |
|||
item.buyDiscount = item.buyDiscount and item.buyDiscount + shopPrice * material.quantity |
|||
else |
|||
table.insert(formatted, entry[1]) |
|||
end |
end |
||
-- iterate through intermediaries to add to XP and duration parameters |
|||
for _, intermediate in ipairs(item.intermediates) do |
|||
-- look up activity XP and add it to running total |
|||
local XPQuery = '[[Recipe output::' .. intermediate.name .. ']]|?Activity XP|mainlabel=' .. intermediate.name |
|||
local XPResult = mw.smw.ask(XPQuery) or {} |
|||
local XPIncrease = 0 |
|||
if XPResult[1] and XPResult[1]["Activity XP"] then |
|||
XPIncrease = tonumber(XPResult[1]["Activity XP"]) or 0 |
|||
else |
|||
XPIncrease = nil |
|||
end |
|||
-- need to look up both the amount of the item used in this recipe and created in its own recipe to know how much of this xp to use |
|||
local QTYused = intermediate.quantity |
|||
local QTYQuery = '[[Recipe output::' .. intermediate.name .. ']]|?Recipe JSON|mainlabel=' .. intermediate.name |
|||
local QTYResult = mw.smw.ask(QTYQuery) or {} |
|||
local QTYmade = 0 |
|||
if QTYResult[1] and QTYResult[1]["Recipe JSON"] then |
|||
QTYmade = mw.text.jsonDecode(QTYResult[1]["Recipe JSON"]) or {} |
|||
end |
|||
QTYmade = QTYmade.output[1].quantity |
|||
item.XP = item.XP and XPIncrease and item.XP + XPIncrease * QTYused / QTYmade; |
|||
-- look up activity duration and add it to running total |
|||
local durationQuery = '[[Recipe output::' .. intermediate.name .. ']]|?Activity duration|mainlabel=' .. intermediate.name |
|||
local durationResult = mw.smw.ask(durationQuery) or {} |
|||
local durationIncrease = 0 |
|||
if durationResult[1] and durationResult[1]["Activity duration"] then |
|||
durationIncrease = tonumber(durationResult[1]["Activity duration"]) or 0 |
|||
else |
|||
durationIncrease = nil |
|||
end |
|||
item.duration = item.duration and durationIncrease and item.duration + durationIncrease * intermediate.quantity |
|||
end |
|||
end |
end |
||
contents = table.concat(formatted, ' • ') |
|||
else |
|||
-- direct values |
|||
contents = '[[Category:Empty variant list]]' |
|||
item.sell = item.sell and item.outputQuantity and item.sell * item.outputQuantity |
|||
item.profit = item.sell and item.buy and item.sell - item.buy |
|||
item.profitDiscount = item.sell and item.buyDiscount and item.sell - item.buyDiscount |
|||
item.profitPerXP = item.profit and item.XP and math.floor(item.profit / item.XP * 100) / 100 |
|||
item.profitPerXPDiscount = item.profitDiscount and item.XP and math.floor(item.profitDiscount / item.XP * 100) / 100 |
|||
local hideName = string.gsub(item.name,'Leather','Hide') |
|||
item.discountLevel = discountList[hideName] |
|||
-- a lot of downtime in leatherworker |
|||
-- 1min 40s to go from hide stall to tannery, run round the tannery, run to leather stall, then back to hide stall |
|||
local batchSize = 24 |
|||
local downtime = 100 |
|||
item.duration = item.duration and item.duration + downtime/batchSize |
|||
item.productPerHour = item.duration and 1 / item.duration * 3600 |
|||
-- properties per hour |
|||
item.XPPerHour = item.XP and item.productPerHour and math.floor(item.XP * item.productPerHour) |
|||
item.profitPerHour = item.profit and item.productPerHour and math.floor(item.profit * item.productPerHour) |
|||
item.profitPerHourDiscount = item.profitDiscount and item.productPerHour and math.floor(item.profitDiscount * item.productPerHour) |
|||
end |
end |
||
return mw.html.create('div') |
|||
:addClass('variants-header') |
|||
:wikitext(contents) |
|||
:done() |
|||
end |
|||
function p.main(frame) |
|||
return results |
|||
local args = frame:getParent().args |
|||
args.variant = args[1] or args.variant |
|||
return p.variants_header(args) |
|||
end |
end |
||
function p.variants_table(args) |
|||
-- make the table |
|||
local smw_data = get_table_info(args, true) |
|||
function p.displayTable(results) |
|||
if not smw_data then |
|||
local out = mw.html.create('table') |
|||
return ':No variants found.' |
|||
:addClass('wikitable sortable') |
|||
end |
|||
local profession_b_flag = false |
|||
for _, entry in ipairs(smw_data) do |
|||
if entry['Profession B'] or entry['Profession Level B'] then |
|||
profession_b_flag = true |
|||
end |
|||
break |
|||
end |
|||
local table = mw.html.create('table') |
|||
:addClass('sortable wikitable') |
|||
:tag('tr') |
:tag('tr') |
||
:tag('th') |
:tag('th') |
||
:attr{ colspan = '2' } |
|||
:wikitext('[[File:Leatherworker small icon.png|15px]] Level') |
|||
:wikitext('Item') |
|||
:done() |
:done() |
||
:tag('th') |
:tag('th') |
||
: |
:wikitext('Level') |
||
:attrIf(profession_b_flag, 'colspan', 2) |
|||
:wikitext('Product') |
|||
:done() |
:done() |
||
:tag('th') |
|||
:wikitext('Materials') |
|||
:done() |
|||
:tag('th') |
|||
:attr{ colspan = '10' } |
|||
:wikitext('Buy Value') |
|||
:done() |
|||
:tag('th') |
|||
:attr{ colspan = '10' } |
|||
:wikitext('Sell Value') |
|||
:done() |
|||
:tag('th') |
|||
:attr{ colspan = '10' } |
|||
:wikitext('Profit') |
|||
:done() |
|||
:tag('th') |
|||
:attr{ colspan = '10' } |
|||
:wikitext('Profit/hr') |
|||
:done() |
|||
-- :tag('th') |
|||
-- :wikitext('[[File:Merchant_small_icon.png|18px]] level<br>for discount') |
|||
-- :done() |
|||
-- :tag('th') |
|||
-- :attr{ colspan = '10' } |
|||
-- :wikitext('Profit/hr with [[File:Merchant_small_icon.png|18px]] discount') |
|||
-- :done() |
|||
:tag('th') |
|||
:wikitext('XP') |
|||
:done() |
|||
:tag('th') |
|||
:wikitext('XP/hr') |
|||
:done() |
|||
:tag('th') |
|||
:attr{ colspan = '10' } |
|||
:wikitext('Coins/XP') |
|||
:done() |
|||
-- :tag('th') |
|||
-- :attr{ colspan = '10' } |
|||
-- :wikitext('Coins/XP with [[File:Merchant_small_icon.png|18px]] discount') |
|||
-- :done() |
|||
:done() |
:done() |
||
local function formatProfessionLevel(profession, level, high) |
|||
if not (profession or level) then |
|||
return nil |
|||
end |
|||
profession = profession or 'Unknown profession' |
|||
local level_text = tostring(level or '?') |
|||
if high then |
|||
level_text = ('%s➨%d'):format(level_text, high) |
|||
end |
|||
return ('%s [[File:%s small icon.png|21x21px|link=%s]]'):format(level_text, profession, profession) |
|||
end |
|||
for _, entry in ipairs(smw_data) do |
|||
local unknown_value_cell = mw.html.create('td') |
|||
local profession_level_a = entry['Profession Level A'] or '?' |
|||
:addClass('table-bg-gray') |
|||
local profession_level_b = entry['Profession Level B'] or '?' |
|||
:css{ ['text-align'] = 'center' } |
|||
local profession_a = entry['Profession A'] or '' |
|||
:wikitext("''unknown''") |
|||
local profession_b = entry['Profession B'] or '' |
|||
table |
|||
for i, item in ipairs(results) do |
|||
:tag('tr') |
|||
:IF(item.lvl) |
|||
:tag('td') |
:tag('td') |
||
:css{ [' |
:css{ ['border-right'] = 'none' } |
||
:wikitext( |
:wikitext(entry.Image) |
||
:done() |
:done() |
||
:ELSE() |
|||
:node(unknown_value_cell) |
|||
:END() |
|||
:tag('td') |
|||
:css{ ['border-right'] = '0', ['padding-right'] = '0', ['text-align'] = 'right' } |
|||
:attr{ ['data-sort-value'] = item.name } |
|||
:wikitext(item.outputQuantity .. ' ×') |
|||
:done() |
|||
:tag('td') |
|||
:addClass('plinkt-image no-border') |
|||
:css{ ['border-left'] = '0', ['padding-left'] = '0' } |
|||
:wikitext('[[File:' .. item.name .. '.png|link=' .. item.name .. '|30px]]') |
|||
:done() |
|||
:tag('td') |
|||
:addClass('plinkt-link no-border') |
|||
:wikitext('[[' .. item.name .. ']]') |
|||
:done() |
|||
:IF(not(item.passiveActivity)) |
|||
local materialCell = row:tag('td') |
|||
for i, _ in ipairs(item.materials) do |
|||
materialCell:wikitext(item.materials[i].quantity .. '× [[File:' .. item.materials[i].name .. '.png|30px|link=' .. item.materials[i].name .. ']]' .. '[[' .. item.materials[i].name .. ']]<br>') |
|||
end |
|||
row |
|||
:node(currency_cell(item.buy)) |
|||
:node(currency_cell(item.sell)) |
|||
:ELSE() |
|||
:tag('td') |
|||
:addClass('table-na') |
|||
:attr{ colspan = '21' } |
|||
:wikitext('N/A') |
|||
:done() |
|||
:END() |
|||
:node(currency_cell(item.profit)) |
|||
:node(currency_cell(item.profitPerHour)) |
|||
-- :tag('td') |
|||
-- :css{ ['text-align'] = 'center' } |
|||
-- :wikitext(item.discountLevel) |
|||
-- :done() |
|||
-- :node(currency_cell(item.profitPerHourDiscount)) |
|||
:IF(item.XP) |
|||
:tag('td') |
:tag('td') |
||
:css{ ['border-left'] = 'none' } |
|||
:wikitext(item.XP and lang:formatNum(tonumber(item.XP))) |
|||
:wikitext(entry[1]) |
|||
:done() |
:done() |
||
:ELSE() |
|||
:node(unknown_value_cell) |
|||
:END() |
|||
:IF(item.XPPerHour) |
|||
:tag('td') |
:tag('td') |
||
:css{ ['text-align'] = 'right' } |
|||
:wikitext(item.XPPerHour and lang:formatNum(tonumber(item.XPPerHour))) |
|||
:wikitext(formatProfessionLevel(entry['Profession A'], entry['Profession Level A'], entry['Profession Level A High'])) |
|||
:done() |
|||
: |
:IF(profession_b_flag) |
||
: |
:tag('td') |
||
:IF(entry['Profession B']) |
|||
:END() |
|||
:css{ ['text-align'] = 'right' } |
|||
:wikitext(formatProfessionLevel(entry['Profession B'], entry['Profession Level B'], entry['Profession Level B High'])) |
|||
:done() |
|||
:END() |
|||
:done() |
|||
end |
|||
return table |
|||
:node(currency_cell(item.profitPerXP)) |
|||
end |
|||
-- :node(currency_cell(item.profitPerXPDiscount)) |
|||
:done() |
|||
end |
|||
function p.main_table(frame) |
|||
return out |
|||
local args = frame:getParent().args |
|||
args.variant = args[1] or args.variant |
|||
return p.variants_table(args) |
|||
end |
end |
||
Revision as of 16:11, 4 December 2024
Module documentation
This documentation is transcluded from Module:Sandbox/User:Alsang/doc. [edit] [history] [purge]
This module does not have any documentation. Please consider adding documentation at Module:Sandbox/User:Alsang/doc. [edit]
Module:Sandbox/User:Alsang's function main is invoked by Template:Sandbox/User:Alsang.
Module:Sandbox/User:Alsang requires Module:Mw.html extension.
require('Module:Mw.html extension')
local p = {}
function sort_entries(entry1, entry2)
-- Sort the variants based on the sum of the profession levels
local entry1_value = (entry1['Profession Level A'] or 0) + (entry1['Profession Level B'] or 0)
local entry2_value = (entry2['Profession Level A'] or 0) + (entry2['Profession Level B'] or 0)
return entry1_value < entry2_value
end
function get_table_info(args, get_images)
local variant_name = args.variant or mw.title.getCurrentTitle().fullText
local query = {
'[[Variant of::'..variant_name..']]',
'?Profession A #', -- Adding a # to this query makes it return plaintext instead of a link
'?Profession B #',
'?Profession Level A',
'?Profession Level B',
'?Profession Level A High',
'?Profession Level B High',
'?Variant name',
get_images and '?Image#64px;x64px = Image' or nil,
limit = args.limit or 500,
}
local smw_data = mw.smw.ask(query)
if not smw_data then
return nil
end
table.sort(smw_data, sort_entries)
return smw_data
end
function p.variants_header(args)
local smw_data = get_table_info(args)
local contents
if smw_data then
local formatted = {}
for _, entry in ipairs(smw_data) do
local variant_name = entry['Variant name']
if variant_name then
local page = entry[1]
local pipe = page:find('|')
if pipe then
page = page:sub(3, pipe-1)
else
page = page:sub(3, -3)
end
if page:sub(1, 1 + #variant_name + #' (grade ') == ':' .. variant_name .. ' (grade ' and page:sub(-1) == ')' then
local grade_number = page:sub(2 + #variant_name + #' (grade ', -2)
mw.log('"' .. grade_number .. '"')
variant_name = 'Grade ' .. grade_number
end
table.insert(formatted, ('[[%s|%s]]'):format(page, variant_name))
else
table.insert(formatted, entry[1])
end
end
contents = table.concat(formatted, ' • ')
else
contents = '[[Category:Empty variant list]]'
end
return mw.html.create('div')
:addClass('variants-header')
:wikitext(contents)
:done()
end
function p.main(frame)
local args = frame:getParent().args
args.variant = args[1] or args.variant
return p.variants_header(args)
end
function p.variants_table(args)
local smw_data = get_table_info(args, true)
if not smw_data then
return ':No variants found.'
end
local profession_b_flag = false
for _, entry in ipairs(smw_data) do
if entry['Profession B'] or entry['Profession Level B'] then
profession_b_flag = true
end
break
end
local table = mw.html.create('table')
:addClass('sortable wikitable')
:tag('tr')
:tag('th')
:attr{ colspan = '2' }
:wikitext('Item')
:done()
:tag('th')
:wikitext('Level')
:attrIf(profession_b_flag, 'colspan', 2)
:done()
:done()
local function formatProfessionLevel(profession, level, high)
if not (profession or level) then
return nil
end
profession = profession or 'Unknown profession'
local level_text = tostring(level or '?')
if high then
level_text = ('%s➨%d'):format(level_text, high)
end
return ('%s [[File:%s small icon.png|21x21px|link=%s]]'):format(level_text, profession, profession)
end
for _, entry in ipairs(smw_data) do
local profession_level_a = entry['Profession Level A'] or '?'
local profession_level_b = entry['Profession Level B'] or '?'
local profession_a = entry['Profession A'] or ''
local profession_b = entry['Profession B'] or ''
table
:tag('tr')
:tag('td')
:css{ ['border-right'] = 'none' }
:wikitext(entry.Image)
:done()
:tag('td')
:css{ ['border-left'] = 'none' }
:wikitext(entry[1])
:done()
:tag('td')
:css{ ['text-align'] = 'right' }
:wikitext(formatProfessionLevel(entry['Profession A'], entry['Profession Level A'], entry['Profession Level A High']))
:IF(profession_b_flag)
:tag('td')
:IF(entry['Profession B'])
:css{ ['text-align'] = 'right' }
:wikitext(formatProfessionLevel(entry['Profession B'], entry['Profession Level B'], entry['Profession Level B High']))
:done()
:END()
:done()
end
return table
end
function p.main_table(frame)
local args = frame:getParent().args
args.variant = args[1] or args.variant
return p.variants_table(args)
end
return p