Module:TradeTable: Difference between revisions
Jump to navigation
Jump to search
imported>Merge merge.py |
imported>Merge merge.py |
||
(No difference)
| |||
Latest revision as of 10:55, 25 September 2026
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 show_java
local show_mult_and_xp
local profession = {'Armorer', 'Butcher', 'Battlewill Trainer', 'Cartographer', 'Cleric', 'Farmer', 'Fisherman', 'Fletcher', 'Leatherworker', 'Librarian', 'Lumberjack', 'Magic Trainer', 'Mason', 'Merchant', 'Miner', 'Shepherd', 'Toolsmith', 'Weaponsmith'}
local refUsed = false
local level_trades = {} -- How many different trades are available in the pool per level
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
local function normalizeInput(line)
return {
level = line["level"] or line["lvl"],
trade_slot = line["slot"] or '—',
slot_draws = line["slotDraws"] 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,
override_probability_je = line['overrideProbability'] or line['overrideProbabilityJE'] or nil,
override_probability_be = line['overrideProbability'] or line['overrideProbabilityBE'] or nil
}
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
local 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
local function printHeader(headerText)
outside_html = mw.html.create('div')
html = outside_html:tag('table')
:addClass('wikitable')
:cssText('text-align:center')
local colspan = 6
if show_java then
colspan = colspan + 1
end
if headerText ~= '' then
html:tag('th')
:attr('colspan', colspan)
:attr('data-description', headerText)
:wikitext(sprite.link({profession, name='EntitySprite', headerText}))
end
local tr = html:tag('tr')
tr:tag('th'):wikitext('Level'):attr('rowspan', 2)
if show_java then
tr:tag('th'):wikitext("''[[Java Edition]]''")
end
tr:tag('th'):wikitext('Villager wants'):attr('rowspan', 2)
:tag('th'):wikitext('Player receives'):attr('rowspan', 2)
:tag('th'):wikitext('Trades in<br>stock'):attr('rowspan', 2)
if show_mult_and_xp then
tr:tag('th'):wikitext('Villager<br>experience'):attr('rowspan', 2)
:tag('th'):wikitext('Price<br>multiplier'):attr('rowspan', 2)
end
tr = tr:tag('tr')
if show_java then
tr:tag('th'):wikitext('Probability')
end
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
local 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
local function formatProbability(line, use_slot)
local weight = line.weight
local group_weight = level_trades[line.level]
local draws = 2
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
local 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
end
lastLevel = line.level
if show_java then
local java_probability = formatProbability(line, false) .. '%'
tr:tag('td'):cssText(cssText):wikitext(line.override_probability_je or java_probability) -- Java probability
end
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)
if show_mult_and_xp then
tr:tag('td'):cssText(cssText):wikitext(line.villager_xp_gain or '—')
local price_multiplier_text = line.price_multiplier
tr:tag('td'):cssText(cssText):wikitext(price_multiplier_text or '—')
end
end
local function storeLine(line)
-- Put data into Bucket
-- 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 bucket_json = {
['profession'] = profession,
['level'] = line.level,
['java_probability'] = line.override_probability_je or formatProbability(line, false) .. '%',
['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,
['price_multiplier'] = line.price_multiplier,
['max_trades'] = line.max_trades,
['villager_xp_gain'] = line.villager_xp_gain
}
local bucket_data = { -- the actual bucket data
['profession'] = profession,
['given_item'] = line.given_item,
['wanted_item'] = {line.wanted_item, line.wanted_item_2},
['json'] = mw.text.jsonEncode(bucket_json)
}
bucket('trade').sub(line.level).put(bucket_data)
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)
local edition = args.edition
show_java = (edition == nil or edition == '' or edition == 'both' or edition == 'java')
-- 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 Bucket 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
rowspans_slot['—'] = 1 -- Special case missing slot to not combine
for i, line in ipairs(lines) do
printLine(line)
if not ignoreUsage then
storeLine(line)
end
end
if refUsed then
outside_html:wikitext(mw.getCurrentFrame():extensionTag{ name='references', args = {group='t'}})
end
return outside_html
end
return p