Module:DropTable
Jump to navigation
Jump to search
This module unfortunately does not work.
"Lua error at line 825: attempt to call global 'setBucketData' (a nil value)."
local i18n = {
imageSprite = {
item = 'Invicon',
mob = 'EntitySprite'
},
headerThing = {
item = 'Item',
mob = 'Mob'
},
headerWithoutLooting = 'Default',
headerLooting1 = 'Looting I',
headerLooting2 = 'Looting II',
headerLooting3 = 'Looting III',
headerOverview = 'Quantity / Chance / Average',
headerDropAmount = 'Amount',
headerDistribution = 'Probability',
headerAverage = 'Average',
headerTotal = {
item = 'Killed',
mob = 'Drops'
},
headerExpectation = {
item = 'Expected Drops',
mob = 'Expected Number of Kills'
},
tabOverviewDecimal = 'Decimal',
tabOverviewFraction = 'Fraction',
tabDistribution = 'Distribution',
tabCalculator = 'Expectation',
titlejava = "''[[Java Edition]]'':",
titlebedrock = "''[[Bedrock Edition]]'':",
notePlayerOrPet = 'Only when killed by a [[player]] or a tamed [[wolf]].',
notePlayerOnly = 'Only when killed by a [[player]].',
noteBurnOrFireAspect = 'Only when on fire or killed with a weapon enchanted with [[Fire Aspect]].',
noteBurnOnly = 'Only when on fire.',
noteNotBurnOrFireAspect = "Only when ''not'' on fire and ''not'' killed with a weapon enchanted with [[Fire Aspect]].",
noteNotBurnOnly = "Only when ''not'' on fire.",
refgroup = 'FN', -- [[MediaWiki:Cite link label group-FN]]
style = 'DropTable/styles.css', -- [[Template:DropTable/styles.css]]
bucket = 'droptable', -- [[Bucket:droptable]]
}
------------------
local Frac = {}
-- Defined types:
-- * frac: { numerator = numerator, denominator = denominator }, used on Frac.*( ..., fraction, ... )
local function GCD( x, y ) -- get Greatest Common Divisor (GCD)
-- Test
-- gcd( 0, 0 ) = undef
if type( x ) ~= 'number' or type( y ) ~= 'number' or ( x == 0 and y == 0 ) then
return nil
end
x = math.abs( x )
y = math.abs( y )
-- Improve precision
local multiplier = 1
while x % 1 ~= 0 or y % 1 ~= 0 do
x = x * 10
y = y * 10
multiplier = multiplier * 10
end
local function euclidean( x, y )
return y == 0 and x or euclidean( y, x % y )
end
return euclidean( x, y ) / multiplier
end
local function LCM( x, y ) -- get Least Common Multiple (LCM)
-- Test
local GCD = GCD( x, y )
if not GCD then
return nil
end
return x * y / GCD
end
-- Start of Frac: Fraction Library
local fracMetatable = {
__index = Frac,
__add = function ( fraction1, fraction2 ) return Frac.add( fraction1, fraction2 ) end,
__sub = function ( fraction1, fraction2 ) return Frac.sub( fraction1, fraction2 ) end,
__mul = function ( fraction1, fraction2 ) return Frac.mul( fraction1, fraction2 ) end,
__div = function ( fraction1, fraction2 ) return Frac.div( fraction1, fraction2 ) end,
__pow = function ( fraction, n ) return Frac.pow( fraction, n ) end,
__unm = function ( fraction ) return Frac.neg( fraction ) end,
__eq = function ( fraction1, fraction2 ) return Frac.cmp( fraction1, fraction2 ) == 0 end,
__lt = function ( fraction1, fraction2 ) return Frac.cmp( fraction1, fraction2 ) < 0 end,
__le = function ( fraction1, fraction2 ) return Frac.cmp( fraction1, fraction2 ) <= 0 end,
__tostring = function ( fraction ) return Frac.display( fraction ) end,
}
-- Tests
function Frac.isFrac( fraction ) -- Check if it is a valid fraction
return not ( type( fraction ) ~= 'table' or not fraction.numerator or not fraction.denominator or fraction.denominator <= 0 or fraction.numerator % 1 ~= 0 or fraction.denominator % 1 ~= 0 )
end
-- Create a fraction directly
function Frac.new( numerator, denominator )
if type( numerator ) ~= 'number' or denominator and ( type( denominator ) ~= 'number' or denominator == 0 ) then
return nil
end
local fraction = denominator and ( denominator < 0 and { numerator = - numerator, denominator = - denominator } or { numerator = numerator, denominator = denominator } ) or Frac.fromDec( numerator )
if Frac.isFrac( fraction ) == false then
return nil
end
setmetatable( fraction, fracMetatable )
return fraction
end
function Frac.fromDec( decimal ) -- Convert decimal to fraction
if type( decimal ) ~= 'number' then
return nil
end
local numerator = decimal
local denominator = 1
while #tostring(numerator % 1) ~= 1 do
numerator = numerator * 10
denominator = denominator * 10
end
return Frac.reduce( Frac.new( math.floor(numerator + 0.5), denominator ) )
end
function Frac.toDec( fraction ) -- Convert fraction to decimal
if Frac.isFrac( fraction ) == false then
return nil
end
return fraction.numerator / fraction.denominator
end
-- Basic calculations
function Frac.neg( fraction ) -- Take the negative fraction
if Frac.isFrac( fraction ) == false then
return nil
end
return Frac.new( - fraction.numerator, fraction.denominator )
end
function Frac.inv( fraction ) -- Take the inversed fraction
if Frac.isFrac( fraction ) == false then
return nil
end
if fraction.numerator == 0 then
return nil
end
return ( fraction.numerator < 0 ) and Frac.new( - fraction.denominator, - fraction.numerator ) or Frac.new( fraction.denominator, fraction.numerator )
end
function Frac.mul( fraction1, fraction2 ) -- Fractions multiplication
fraction1 = Frac.isFrac( fraction1 ) and fraction1 or Frac.new( fraction1 )
fraction2 = Frac.isFrac( fraction2 ) and fraction2 or Frac.new( fraction2 )
if Frac.isFrac( fraction1 ) == false or Frac.isFrac( fraction2 ) == false then
return nil
end
return Frac.new( fraction1.numerator * fraction2.numerator, fraction1.denominator * fraction2.denominator )
end
function Frac.div( fraction1, fraction2 ) -- Fractions division
fraction2 = Frac.isFrac( fraction2 ) and fraction2 or Frac.new( fraction2 )
return Frac.mul( fraction1, Frac.inv( fraction2 ) )
end
function Frac.pow( fraction, n ) -- Fractions exponentiation
if Frac.isFrac( fraction ) == false or n % 1 ~= 0 then
return nil
end
return ( n < 0 ) and Frac.new( fraction.denominator ^ ( - n ), fraction.numerator ^ ( -n ) ) or Frac.new( fraction.numerator ^ n, fraction.denominator ^ n )
end
-- Advanced calculation
function Frac.expand( fraction, newDenominator ) -- Fractions expansion
if Frac.isFrac( fraction ) == false or type( newDenominator ) ~= 'number' or ( type( newDenominator ) == 'number' and newDenominator % 1 ~= 0 ) or newDenominator <= 0 then
return nil
end
local newNumerator = fraction.numerator * newDenominator / fraction.denominator
if newNumerator % 1 ~= 0 then
return nil
end
return Frac.new( newNumerator, newDenominator )
end
function Frac.reduce( fraction ) -- Fractions reduction (to simplest)
if Frac.isFrac( fraction ) == false then
return nil
end
return Frac.expand( fraction, fraction.denominator / GCD( fraction.denominator, fraction.numerator ) )
end
function Frac.common( fractionList ) -- Get fractions with the common denominator
if type( fractionList ) ~= 'table' then
return nil
end
local candidates = {}
local denominators = {}
for _, fraction in pairs( fractionList ) do
if Frac.isFrac( fraction ) == false then
return nil
else
table.insert( candidates, fraction )
table.insert( denominators, fraction.denominator )
end
end
if #candidates == 1 then
return candidates
end
local lcm = 1
for _, denominator in pairs( denominators ) do
lcm = LCM( denominator, lcm )
end
local results = {}
for _, fraction in pairs( candidates ) do
table.insert( results, Frac.expand( fraction, lcm ) )
end
return results
end
-- Basic calculations, with dependencies
function Frac.add( fraction1, fraction2 ) -- Fractions addition
fraction1 = Frac.isFrac( fraction1 ) and fraction1 or Frac.new( fraction1 )
fraction2 = Frac.isFrac( fraction2 ) and fraction2 or Frac.new( fraction2 )
if Frac.isFrac( fraction1 ) == false or Frac.isFrac( fraction2 ) == false then
return nil
end
local fractions = Frac.common{ fraction1, fraction2 }
return Frac.new( fractions[ 1 ].numerator + fractions[ 2 ].numerator, fractions[ 1 ].denominator )
end
function Frac.sub( fraction1, fraction2 ) -- Fractions subtraction
fraction2 = Frac.isFrac( fraction2 ) and fraction2 or Frac.new( fraction2 )
return Frac.add( fraction1, Frac.neg( fraction2 ) )
end
function Frac.cmp( fraction1, fraction2 ) -- Compare fractions
return Frac.sub( fraction1, fraction2 ).numerator
end
-- Formattings to human readable
function Frac.display( fraction, isReduced, isMixed ) -- Convert fractions to human-readable style
if Frac.isFrac( fraction ) == false then
return ''
end
if fraction.numerator == 0 then
return '0'
end
local prefix = ''
if fraction.numerator < 0 then
prefix = prefix .. '-'
fraction = Frac.neg( fraction )
end
if isReduced then
fraction = Frac.reduce( fraction )
end
local numerator = fraction.numerator
local denominator = fraction.denominator
if denominator == 1 then
return prefix .. tostring( numerator )
end
if numerator == denominator then
return prefix .. '1'
end
if isMixed and numerator > denominator then
prefix = prefix .. math.floor( numerator / denominator )
numerator = numerator % denominator
if numerator == 0 then
return prefix
end
end
return string.format( '%s<sup>%s</sup>⁄<sub>%s</sub>', prefix, numerator, denominator )
end
------------------
local Distr = {}
Distr.mt = {}
function Distr.mt.__index( t, k )
if type( k ) == 'number' then return Frac.new( 0 ) end
return Distr[ k ]
end
function Distr.mt.__newindex( t, k, v )
v = Frac.isFrac( v ) and v or Frac.new( v )
if type( k ) ~= 'number' or v == nil then error() end
if v == Frac.new( 0 ) then return end
return rawset( t, k, v )
end
function Distr.new( t )
t = t or {}
local distribution = {}
setmetatable( distribution, Distr.mt )
for k, v in pairs( t ) do
distribution[ k ] = v
end
return distribution
end
function Distr.shift( distribution, s )
local newDistribution = Distr.new()
for i, v in pairs( distribution ) do
newDistribution[ i + s ] = v
end
return newDistribution
end
function Distr.limitRange( distribution, min, max )
local newDistribution = Distr.new()
for i, v in pairs( distribution ) do
if max and i >= max then
newDistribution[ max ] = newDistribution[ max ] + v
elseif min and i <= min then
newDistribution[ min ] = newDistribution[ min ] + v
else
newDistribution[ i ] = v
end
end
return newDistribution
end
function Distr.add( distribution1, distribution2 )
local newDistribution = distribution1:new()
for i, v in pairs( distribution2 ) do
newDistribution[ i ] = newDistribution[ i ] + v
end
return newDistribution
end
function Distr.multiply( distribution, x )
local newDistribution = Distr.new()
for i, v in pairs( distribution ) do
newDistribution[ i ] = v * x
end
return newDistribution
end
function Distr.expectation( distribution )
local expectation = Frac.new( 0 )
for i, v in pairs( distribution ) do
expectation = expectation + v * i
end
return expectation
end
function Distr.times( distribution, n )
local newDistribution = Distr.new({[0] = 1})
for i = 1, n do
newDistribution = newDistribution:addIncrease( distribution )
end
return newDistribution
end
function Distr.addIncrease( distribution1, distribution2 )
local newDistribution = Distr.new()
for i, v in pairs( distribution2 ) do
newDistribution = newDistribution:add( distribution1:shift( i ):multiply( v ) )
end
return newDistribution
end
function Distr.rolls( distribution1, distribution2 )
local newDistribution = Distr.new()
for i, v in pairs( distribution2 ) do
newDistribution = newDistribution:add( distribution1:times( i ):multiply( v ) )
end
return newDistribution
end
function Distr.getUniform( min, max )
local value = Frac.new( 1, max - min + 1 )
local distribution = Distr.new()
for i = min, max do
distribution[ i ] = value
end
return distribution
end
function Distr.getLootingIncrease( level, min, max )
min = min or 0
max = max or min + 1
min = min * level
max = max * level
if max - min < 2 then
return Distr.getUniform( min, max )
end
local distribution = Distr.new()
local value = Frac.new( 1, max - min )
for i = min, max do
distribution[ i ] = value
end
distribution[ min ] = value / 2
distribution[ max ] = value / 2
return distribution
end
function Distr.addLootingIncrease( distribution1, distribution2, edition )
local base = distribution1:limitRange(0, nil)
local backup = base[ 0 ]
if edition == 'bedrock' then
base[ 0 ] = 0 -- MCPE-35307: Looting doesn't work when the mob drops nothing
end
local newDistribution = base:addIncrease(distribution2)
if edition == 'bedrock' then
newDistribution[ 0 ] = newDistribution[ 0 ] + backup
end
return newDistribution
end
------------------
local p = {}
local function calculateDistributions( args, edition )
local function parseRange(str)
local n = tonumber(str)
if n then return {min = n, max = n} end
local min, max = str:match('^%s*(%-?%d+)%s*%-%s*(%-?%d+)%s*$')
min = tonumber(min)
max = tonumber(max)
if min and max and min <= max then return {min = min, max = max} end
error(string.format("'%s' isn't a range!", str))
end
local function parseProbability(str)
local n = Frac.fromDec(tonumber(str)) or Frac.div(tonumber(str:match('(.*)%%%s*$')), 100)
if n then return n end
local numerator, denominator = str:match('^%s*(%-?%d+)%s*/%s*(%-?%d+)%s*$')
numerator = tonumber(numerator)
denominator = tonumber(denominator)
local frac = Frac.new(numerator, denominator)
if frac then return frac end
error(string.format("'%s' isn't a probability!", str))
end
local quantity = parseRange(args.quantity or 1)
local lootingquantity = parseRange(args.lootingquantity or 0)
local dropchance = parseProbability(args.dropchance or 1)
local lootingchance = parseProbability(args.lootingchance or 0)
local multiplychance = parseProbability(args.multiplychance or 1)
local rolls = parseRange(args.rolls or 1)
local limit = tonumber(args.limit)
if edition == 'bedrock' then
limit = nil
end
local independent = {}
for _, sub in ipairs(args.independent or {}) do
table.insert(independent, calculateDistributions(sub, edition))
end
local mutuallyexclusive = {}
for _, sub in ipairs(args.mutuallyexclusive or {}) do
table.insert(mutuallyexclusive, calculateDistributions(sub, edition))
end
local distributions = {}
for level = 0, 3 do
local lootingIncrease = Distr.getLootingIncrease(level, lootingquantity.min, lootingquantity.max)
local chance = (dropchance + lootingchance * level) * multiplychance
local distribution = Distr.getUniform(quantity.min, quantity.max)
:addLootingIncrease(lootingIncrease, edition)
:rolls(Distr.new{[0] = 1 - chance, [1] = chance})
:limitRange(0, limit)
:rolls(Distr.getUniform(rolls.min, rolls.max))
for _, sub in ipairs(independent) do
distribution = distribution:addIncrease(sub[level])
end
for _, sub in ipairs(mutuallyexclusive) do
local nodropchance1 = distribution[0]
local nodropchance2 = sub[level][0]
distribution = distribution:add(sub[level])
distribution[0] = nodropchance1 + nodropchance2 - Frac.new(1)
end
distributions[level] = distribution
end
return distributions
end
local function getImageAndLink(args, mode)
local link = args.link or args.name
local image = require([[Module:SpriteFile]]).sprite{
args.image or args.name,
name = args.spritetype or i18n.imageSprite[mode],
link = link,
size = 32,
align = 'middle',
keepcase = mode == 'item'
}
local displayname = args.displayname or args.name
local linktext = string.format('[[%s|%s]]', link, displayname)
return image, linktext
end
local function getDropData(args, edition)
local data = {}
for level, distribution in pairs(calculateDistributions(args, edition)) do
local quantity = {}
for k, v in pairs(distribution) do
if v ~= Frac.new(0) then table.insert(quantity, k) end
end
table.sort(quantity)
local min = quantity[1]
local max = quantity[#quantity]
local from = min
local to
local quantitytext = ''
local function range()
quantitytext = quantitytext .. (from == min and '' or ' / ')
.. (from == to and to or (from .. '–' .. to))
end
for _, num in ipairs(quantity) do
if to and to ~= num - 1 then
range()
from = num
end
to = num
end
range()
data[level] = {
distribution = distribution,
average = distribution:expectation():reduce(),
dropchance = (1 - distribution[0]):reduce(),
min = min,
max = max,
quantitytext = quantitytext
}
end
return data
end
local function linesToDatas(lines, notes, edition)
local datas = {}
for _, line in ipairs(lines) do
local args = mw.text.jsonDecode(line)
local data = { name = args.name }
data.image, data.linktext = getImageAndLink(args, 'item')
if edition ~= 'bedrock' and args.edition ~= 'bedrock' then
data.java = getDropData(args, 'java')
end
if edition ~= 'java' and args.edition ~= 'java' then
data.bedrock = getDropData(args, 'bedrock')
end
local notenames = args.notes and mw.text.split(args.notes, ',') or {}
data.notes = {}
for _, name in ipairs(notenames) do
table.insert(data.notes, notes[name])
end
table.insert(datas, data)
end
return datas
end
local function createOverviewTable(format, datas, noteTexts, mode, edition)
local wikitable = mw.html.create('table'):addClass('wikitable')
:tag('tr')
:tag('th'):attr('colspan', '2'):attr('rowspan', '2'):wikitext(i18n.headerThing[mode]):done()
:tag('th'):attr('colspan', '12'):wikitext(i18n.headerOverview):done()
:done()
:tag('tr')
:tag('th'):attr('colspan', '3'):wikitext(i18n.headerWithoutLooting):done()
:tag('th'):attr('colspan', '3'):wikitext(i18n.headerLooting1):done()
:tag('th'):attr('colspan', '3'):wikitext(i18n.headerLooting2):done()
:tag('th'):attr('colspan', '3'):wikitext(i18n.headerLooting3):done()
:done()
for i, data in ipairs(datas) do
if data[edition] then
local row = mw.html.create('tr')
:tag('td'):wikitext(data.image):done()
:tag('td'):wikitext(data.linktext .. noteTexts[i]):done()
for level = 0, 3 do
local info = data[edition][level]
row:tag('td'):css('text-wrap-mode', 'nowrap'):wikitext(info.quantitytext):done()
if format == 'decimal' then
row:tag('td'):wikitext(string.format('%.2f%%', Frac.toDec(info.dropchance) * 100)):done()
row:tag('td'):wikitext(string.format('%.2f', Frac.toDec(info.average))):done()
else
row:tag('td'):wikitext(Frac.display(info.dropchance)):done()
row:tag('td'):wikitext(Frac.display(info.average)):done()
end
end
wikitable:node(row)
end
end
return tostring(wikitable)
end
local function createDistributionsTable(datas, noteTexts, mode, edition)
local wikitable = mw.html.create('table'):addClass('wikitable')
:tag('tr')
:tag('th'):attr('colspan', '2'):attr('rowspan', '2'):wikitext(i18n.headerThing[mode]):done()
:tag('th'):attr('rowspan', '2'):wikitext(i18n.headerDropAmount):done()
:tag('th'):attr('colspan', '4'):wikitext(i18n.headerDistribution):done()
:done()
:tag('tr')
:tag('th'):wikitext(i18n.headerWithoutLooting):done()
:tag('th'):wikitext(i18n.headerLooting1):done()
:tag('th'):wikitext(i18n.headerLooting2):done()
:tag('th'):wikitext(i18n.headerLooting3):done()
:done()
for i, data in ipairs(datas) do
if data[edition] then
local min = math.huge
local max = -math.huge
for level = 0, 3 do
min = math.min(min, data[edition][level].min)
max = math.max(max, data[edition][level].max)
end
local row = mw.html.create('tr')
:tag('td'):attr('rowspan', max - min + 2):wikitext(data.image):done()
:tag('td'):attr('rowspan', max - min + 2):wikitext(data.linktext .. noteTexts[i]):done()
for drops = min, max do
row:tag('td'):wikitext(tostring(drops)):done()
for level = 0, 3 do
local probability = data[edition][level].distribution[drops] or Frac.new(0)
row:tag('td'):wikitext(
probability == Frac.new(0) and '0' or
string.format('%s (%.2f%%)', Frac.display(probability, true), Frac.toDec(probability) * 100)
):done()
end
wikitable:node(row)
row = mw.html.create('tr')
end
row:tag('th'):wikitext(i18n.headerAverage):done()
for level = 0, 3 do
local average = data[edition][level].average
row:tag('th'):wikitext(string.format('%s (%.2f)', Frac.display(average), Frac.toDec(average))):done()
end
wikitable:node(row)
end
end
return tostring(wikitable)
end
local function createCalculatorTable(datas, noteTexts, mode, edition)
local wikitable = mw.html.create('table'):addClass('wikitable'):addClass('calculator-container')
:tag('tr')
:tag('th')
:attr('colspan', '2')
:wikitext(mw.getCurrentFrame():expandTemplate{ title = 'Simplecalc label', args = {
label = i18n.headerTotal[mode],
['for'] = 'count'
}})
:done()
:tag('td')
:attr('colspan', '4')
:wikitext(mw.getCurrentFrame():expandTemplate{ title = 'simplecalc', args = {
type = 'number',
id = 'count',
min = 0,
step = 1,
default = 1
}})
:done()
:done()
:tag('tr')
:tag('th'):attr('colspan', '2'):attr('rowspan', '2'):wikitext(i18n.headerThing[mode]):done()
:tag('th'):attr('colspan', '4'):wikitext(i18n.headerExpectation[mode]):done()
:done()
:tag('tr')
:tag('th'):wikitext(i18n.headerWithoutLooting):done()
:tag('th'):wikitext(i18n.headerLooting1):done()
:tag('th'):wikitext(i18n.headerLooting2):done()
:tag('th'):wikitext(i18n.headerLooting3):done()
:done()
for i, data in ipairs(datas) do
if data[edition] then
local row = mw.html.create('tr')
:tag('td'):wikitext(data.image):done()
:tag('td'):wikitext(data.linktext .. noteTexts[i]):done()
for level = 0, 3 do
local average = data[edition][level].average
if mode == 'mob' then
average = Frac.inv(average)
end
row:tag('td')
:wikitext(mw.getCurrentFrame():expandTemplate{ title = 'simplecalc', args = {
type = 'plain',
formula = string.format('count*(%s/%s)', average.numerator, average.denominator),
decimals = 2,
default = string.format('%.2f', Frac.toDec(average))
}})
:done()
end
wikitable:node(row)
end
end
return tostring(wikitable)
end
local function createTabs (datas, mode, edition)
local f = mw.getCurrentFrame()
local noteTexts = {}
for i, data in pairs(datas) do
local noteText = ''
if data[edition] then
for _, note in ipairs(data.notes) do
local ref = note.name and note or note[edition]
if ref then
noteText = noteText .. f:extensionTag{
name = 'ref',
content = ref.content,
args = { name = ref.name, group = i18n.refgroup }
}
end
end
end
noteTexts[i] = noteText
end
local tabber = f:extensionTag('tabber', string.format(
'%s=%s|-|%s=%s|-|%s=%s|-|%s=%s',
i18n.tabOverviewDecimal,
createOverviewTable('decimal', datas, noteTexts, mode, edition),
i18n.tabOverviewFraction,
createOverviewTable('fraction', datas, noteTexts, mode, edition),
i18n.tabDistribution,
createDistributionsTable(datas, noteTexts, mode, edition),
i18n.tabCalculator,
createCalculatorTable(datas, noteTexts, mode, edition)
))
return require('Module:TSLoader').call(i18n.style)
.. tostring(mw.html.create('div'):addClass('droptable-tabber'):wikitext(tabber))
.. tostring(mw.html.create('div'):addClass('droptable-references'):wikitext(f:extensionTag{
name = 'references',
args = { group = i18n.refgroup }
}))
end
local function setBucketData(datas, args)
args.name = args.name or mw.title.getCurrentTitle().text
local image, link = getImageAndLink(args, 'mob')
for _, data in ipairs(datas) do
local jsonData = mw.text.jsonEncode({
name = args.name,
image = image,
linktext = link,
java = data.java,
bedrock = data.bedrock,
notes = data.notes
}, mw.text.JSON_PRESERVE_KEYS)
local bucketObject = {
item = data.name,
json = jsonData
}
bucket(i18n.bucket).put(bucketObject)
end
end
local function getBucketData(args)
local name = args.name or args[1] or mw.title.getCurrentTitle().text
local data = bucket(i18n.bucket)
.select('json')
.where('item', name)
.run()
local jsons = {}
for _, object in ipairs(data) do
local jsondata = object['json']
table.insert(jsons, mw.text.jsonDecode(jsondata, mw.text.JSON_PRESERVE_KEYS))
end
return jsons
end
local function parseNotes(defines)
local notes = {
burn_only = { name = 'burn_only', content = i18n.noteBurnOnly },
burn_or_fire_aspect = { name = 'burn_or_fire_aspect', content = i18n.noteBurnOrFireAspect },
not_burn_only = { name = 'not_burn_only', content = i18n.noteNotBurnOnly },
not_burn_or_fire_aspect = { name = 'not_burn_or_fire_aspect', content = i18n.noteNotBurnOrFireAspect },
player_only = { name = 'player_only', content = i18n.notePlayerOnly },
player_or_pet = { name = 'player_or_pet', content = i18n.notePlayerOrPet }
}
notes.player = { java = notes.player_or_pet, bedrock = notes.player_only }
notes.burn = { java = notes.burn_or_fire_aspect, bedrock = notes.burn_only }
notes.not_burn = { java = notes.not_burn_or_fire_aspect, bedrock = notes.not_burn_only }
for _, str in ipairs(defines and mw.text.split(defines, '\n') or {}) do
local key, text = str:match('^%s*([^=]-)%s*=%s*(.-)%s*$')
if key and text then
local name, edition = key:match('^(.-)%.(.*)$')
if edition then
notes[name] = notes[name] or {}
local redirect = text:match('^>%s*(.-)$')
if redirect and notes[redirect].name then
notes[name][edition] = notes[redirect]
else
notes[name][edition] = { name = name, content = text }
end
else
notes[key] = { name = key, content = text }
end
end
end
return notes
end
function p.line( frame )
local args = require( 'Module:ProcessArgs' ).norm( frame:getParent().args )
for _, name in pairs({'independent', 'mutuallyexclusive'}) do
if args[name] then
args[name] = mw.text.jsonDecode('[' .. args[name] .. ']')
end
end
return mw.text.jsonEncode(args)
end
function p.main( frame )
return p._main( frame:getParent().args )
end
function p._main( args )
local notes = parseNotes(args.notes)
local edition = args.edition and mw.text.trim(args.edition)
local datas = linesToDatas(args, notes, edition)
if not args.nosource and mw.title.getCurrentTitle().isContentPage then
setBucketData(datas, args)
end
if edition then
return createTabs(datas, 'item', edition)
end
return i18n.titlejava .. createTabs(datas, 'item', 'java')
.. i18n.titlebedrock .. createTabs(datas, 'item', 'bedrock')
end
function p.source( frame )
return p._source( frame:getParent().args )
end
function p._source( args )
local datas = getBucketData( args )
local edition = args.edition and mw.text.trim(args.edition)
if edition then
return createTabs(datas, 'mob', edition)
end
return i18n.titlejava .. createTabs(datas, 'mob', 'java')
.. i18n.titlebedrock .. createTabs(datas, 'mob', 'bedrock')
end
return p