Module:TradeTable: Difference between revisions
From Downtime Wiki
MC>Im Wired In m Add debug logs |
m 1 revision imported |
(No difference)
|
Latest revision as of 04:45, 25 April 2025
Documentation for this module may be created at Module:TradeTable/doc
local p = {}
local namespace = mw.title.getCurrentTitle().nsText
local sprite = require('Module:SpriteFile')
local rowspans = {} -- Count of rowspans for each level
local rowspans_slot = {} -- count of rowspans for each slot
local outside_html -- html outside of table
local html -- html table
local lastLevel = ''
local lastSlot
local profession = ''
local refUsed = false
local level_trades = {} -- How many different trades are available in the pool per level
local slot_trades = {} -- How many different trades are available in the pool per slot (bedrock)
local given_note_list = {} -- A table of note names -> note text
local wanted_note_list = {} -- A table of note names -> note text
local colors = {'White', 'Light Gray', 'Gray', 'Black', 'Brown', 'Red', 'Orange', 'Yellow', 'Lime', 'Green', 'Cyan', 'Light Blue', 'Blue', 'Purple', 'Magenta', 'Pink'}
-- Maps alternate input param names to a standardized name
function normalizeInput(line)
return {
level = line["level"] or line["lvl"],
trade_slot = line["slot"] or '-',
wanted_item = line["want"] or line["want1"] or 'Emerald',
wanted_quant = line["wantQuant"] or line["wantQuant1"] or '1',
wanted_sprite = line["wantSprite"] or line["wantSprite1"],
wanted_item_2 = line["want2"],
wanted_sprite_2 = line["wantSprite2"],
wanted_quant_2 = line["wantQuant2"] or '1',
wanted_note = line["wantNote"],
wanted_note_text = line["wantNoteText"],
price_multiplier = line["multi"],
given_item = line["give"] or 'Emerald',
given_sprite = line["giveSprite"],
given_quant = line["giveQuant"] or '1',
given_note = line["giveNote"],
given_note_text = line["giveNoteText"],
max_trades = line["maxTrades"],
villager_xp_gain = line["xpGain"],
weight = line["weight"] or 1
}
end
-- Takes all args and puts them into a json string
function p.serialize(frame)
return mw.text.jsonEncode(frame:getParent().args)
end
-- Undo the serialization for parsing
function deserialize(args)
local e
local out = {}
for k, v in pairs(args) do
if type(k) == "number" then -- only deserialize unnamed params
e, out[k] = pcall( mw.text.jsonDecode, v )
if not e then
out[k] = { '', 'unknown', v..' <strong class="error">Lua error: '..out[k].. ' Input:'.. v ..'</strong>' }
else
out[k] = normalizeInput(out[k])
end
end
end
return out
end
function printHeader(headerText)
outside_html = mw.html.create('div')
html = outside_html:tag('table')
:addClass('wikitable')
:cssText('text-align:center')
if headerText ~= '' then
html:tag('th')
:attr('colspan', 9)
:attr('data-description', headerText)
:wikitext(headerText)
end
local tr = html:tag('tr')
tr:tag('th'):wikitext('Level'):attr('rowspan', 2)
tr:tag('th'):wikitext("''[[Bedrock Edition]]''"):attr('colspan', 2)
tr:tag('th'):wikitext("''[[Java Edition]]''")
tr:tag('th'):wikitext('Item wanted'):attr('rowspan', 2)
:tag('th'):wikitext('Item given'):attr('rowspan', 2)
:tag('th'):wikitext('Trades in<br>stock'):attr('rowspan', 2)
:tag('th'):wikitext('Price multiplier'):attr('rowspan', 2)
:tag('th'):wikitext('Villager XP'):attr('rowspan', 2)
tr = tr:tag('tr')
tr:tag('th'):wikitext('Slot')
tr:tag('th'):wikitext('Probability')
tr:tag('th'):wikitext('Probability')
end
function p.formatItemOutput(item, quant, item2, quant2, _sprite, _sprite2)
local text, qtyText = '', ''
local item_display_text = item:gsub(' %(item%)',''):gsub('Any color','')
_sprite = _sprite or item
if quant ~= '1' then
quant = quant:gsub('-', '–')
qtyText = quant..' × '
end
if item:find('Enchanted') then
text = text..qtyText..'[[File:'.._sprite..' (item).gif|16x16px|link='..item..']] [['..item..']]'
elseif item:find('Any color') then
-- Show all color sprites
local base_item = _sprite:gsub('Any color', '')
for i, color in pairs(colors) do
text = text..sprite.link({text='',id=color..' '..base_item,name='InvSprite',keepcase=1})
if i == 8 then
text = text..'<br>'
end
end
text = text..'<br>'..qtyText..'[['..item_display_text..'|'..item..']]'
else
text = text..qtyText..sprite.link({text=item_display_text,id=_sprite,link=item,name="InvSprite", keepcase=1})
end
if quant2 and item2 then
text = text .. '<br>+ ' .. p.formatItemOutput(item2, quant2, nil, nil, _sprite2)
end
return text
end
-- from WP
function choose(n, k)
if k < 0 or k > n then
return 0
end
if k == 0 or k == n then
return 1
end
k = math.min(k, n-k) -- symmetry
local c = 1
for i = 0, k-1 do
c = c * (n-i) / (k - i)
end
return c
end
function formatProbability(line, use_slot)
local weight = line.weight
local group_weight = level_trades[line.level]
local draws = 2
if use_slot and line.trade_slot then
if line.trade_slot == '-' then
return '-'
end
draws = 1
group_weight = slot_trades[line.trade_slot]
end
if group_weight - weight == 0 then
return 100
end
local probability = (1 - choose(group_weight - weight, draws) / choose(group_weight, draws)) * 100
return math.floor(probability+0.5)
end
function printLine(line)
local tr = html:tag('tr')
local cssText = ''
if line.level ~= lastLevel then
-- Don't do thick border for first level
if lastLevel ~= '' then
cssText = 'border-top-width:2px'
end
tr:tag('th')
:attr('rowspan', rowspans[line.level])
:cssText(cssText)
:wikitext((line.level:gsub("^%l", string.upper))) --uppercase first letter for nice display
end
lastLevel = line.level
if line.trade_slot == '-' or line.trade_slot ~= lastSlot then
tr:tag('th')
:attr('rowspan', rowspans_slot[line.trade_slot])
:cssText(cssText)
:wikitext(line.trade_slot)
end
lastSlot = line.trade_slot
local bedrock_probability = formatProbability(line, true)
if line.trade_slot ~= '-' then
bedrock_probability = bedrock_probability .. '%'
end
tr:tag('td'):cssText(cssText):wikitext(bedrock_probability) -- Bedrock probability
tr:tag('td'):cssText(cssText):wikitext(formatProbability(line, false), '%') -- Java probability
local wanted = tr:tag('td'):cssText(cssText):wikitext(p.formatItemOutput(line.wanted_item, line.wanted_quant, line.wanted_item_2, line.wanted_quant_2, line.wanted_sprite, line.wanted_sprite_2))
if line.wanted_note_text ~= nil or line.wanted_note ~= nil then
wanted:wikitext(mw.getCurrentFrame():extensionTag{ name='ref', content=line.wanted_note_text, args = {name=line.wanted_note, group='t'}})
refUsed = true -- Used to set references at footer
end
local given = tr:tag('td'):cssText(cssText):wikitext(p.formatItemOutput(line.given_item, line.given_quant, nil, nil, line.given_sprite)):wikitext()
if line.given_note_text ~= nil or line.given_note ~= nil then
given:wikitext(mw.getCurrentFrame():extensionTag{ name='ref', content=line.given_note_text, args = {name=line.given_note, group='t'}})
refUsed = true -- Used to set references at footer
end
tr:tag('td'):cssText(cssText):wikitext(line.max_trades)
local price_multiplier_text = line.price_multiplier
if line.price_multiplier == "0.05" then
price_multiplier_text = "Low"
elseif line.price_multiplier == "0.2" then
price_multiplier_text = "High"
end
tr:tag('td'):cssText(cssText):wikitext(price_multiplier_text)
tr:tag('td'):cssText(cssText):wikitext(line.villager_xp_gain)
end
function storeLine(line)
-- Put data into SMW
-- The only top level params that are needed is stuff that would be used as selectors
-- everything else is put into a json blob for flexibility
if not (namespace == '' or namespace == 'Minecraft_Wiki') then
return -- only store data if we are in main or Minecraft_Wiki namespaces
end
local subname = 'TRADE'..'_'..line.level..'_'..line.wanted_item..'_'..line.given_item
subname = subname:gsub(' ', '_')
local smw_json = {
['profession'] = profession,
['level'] = line.level,
['java_probability'] = formatProbability(line, false),
['bedrock_probability'] = formatProbability(line, true),
['given_item'] = line.given_item,
['given_quant'] = line.given_quant,
['given_sprite'] = line.given_sprite,
['wanted_item'] = line.wanted_item,
['wanted_quant'] = line.wanted_quant,
['wanted_sprite'] = line.wanted_sprite,
['wanted_item_2'] = line.wanted_item_2,
['wanted_quant_2'] = line.wanted_quant_2,
['wanted_sprite_2'] = line.wanted_sprite_2,
['given_note'] = line.given_note,
['given_note_text'] = given_note_list[line.given_note] or line.given_note_text,
['wanted_note'] = line.wanted_note,
['wanted_note_text'] = wanted_note_list[line.wanted_note] or line.wanted_note_text
}
local smw_sub = { -- the actual SMW sub-object
['Given item'] = line.given_item,
['Wanted item'] = {line.wanted_item, line.wanted_item_2},
['Trade JSON'] = mw.text.jsonEncode(smw_json)
}
local result = mw.smw.subobject(smw_sub, subname)
mw.logObject(result, subname)
if result == true then
return ''
else
return 'Error saving smw data: ' .. result.error
end
end
function p.main(frame)
return p._main(frame:getParent().args)
end
function p._main(args)
local headerText = args.title or ''
local ignoreUsage = args.ignoreUsage or nil
local lines = deserialize(args)
-- First loop counts the rowspans for column 1
for i,line in ipairs(lines) do
level_trades[line.level] = (level_trades[line.level] or 0) + line.weight
rowspans[line.level] = (rowspans[line.level] or 0) + 1
rowspans_slot[line.trade_slot] = (rowspans_slot[line.trade_slot] or 0) + 1
slot_trades[line.trade_slot] = (slot_trades[line.trade_slot] or 0) + line.weight
--Save the note text so it can be used by SMW to display the notes on other pages
if line.given_note and line.given_note_text and given_note_list[line.given_note] == nil then
given_note_list[line.given_note] = line.given_note_text
end
if line.wanted_note and line.wanted_note_text and wanted_note_list[line.wanted_note] == nil then
wanted_note_list[line.wanted_note] = line.wanted_note_text
end
end
profession = args.profession or headerText
printHeader(headerText)
rowspans_slot['-'] = 1 -- Special case missing slot to not combine
local smw_info = {}
for i, line in ipairs(lines) do
printLine(line)
if not ignoreUsage then
local result = storeLine(line)
if result ~= '' then
table.insert(smw_info, result)
end
end
end
-- Add a warning popup with any errors saving smw data
if #smw_info > 0 then
outside_html:wikitext(mw.smw.info(table.concat(smw_info, '\n'), 'warning'))
end
if refUsed then
outside_html:wikitext(mw.getCurrentFrame():extensionTag{ name='references', args = {group='t'}})
end
return outside_html
end
return p