Module:GameData
Appearance
Documentation for this module may be created at Module:GameData/doc
local p = {}
local snapshot
local function data()
if not snapshot then snapshot = mw.loadJsonData('GameData:Reference.json') end
return snapshot
end
local function missing(key)
return '<strong class="error">Missing game reference: ' .. mw.text.nowiki(key) ..
'</strong>[[Category:Pages with missing game references]]'
end
local function duration(value)
local seconds = value / 1000
local units = {{86400, 'day'}, {3600, 'hour'}, {60, 'minute'}, {1, 'second'}}
for _, unit in ipairs(units) do
if seconds >= unit[1] then
local amount = math.floor(seconds / unit[1] * 100 + 0.5) / 100
return mw.language.getContentLanguage():formatNum(amount) .. ' ' .. unit[2] .. (amount == 1 and '' or 's')
end
end
return tostring(value) .. ' milliseconds'
end
local function itemDuration(value)
local parts = {}
local remaining = value
for _, unit in ipairs({{86400000, 'd'}, {3600000, 'h'}, {60000, 'm'}, {1000, 's'}, {1, 'ms'}}) do
local amount = math.floor(remaining / unit[1])
if amount > 0 then
table.insert(parts, tostring(amount) .. unit[2])
remaining = remaining - amount * unit[1]
end
end
return #parts > 0 and table.concat(parts) or '0s'
end
local function format(record)
if record.format == 'markdown' then return record.wikitext or missing('markdown') end
if record.format == 'duration' then return duration(tonumber(record.value)) end
if record.format == 'number' then return mw.language.getContentLanguage():formatNum(record.value) end
return mw.text.nowiki(tostring(record.value))
end
local function args(frame)
return frame:getParent() and frame:getParent().args or frame.args
end
function p.value(frame)
local a = args(frame)
local key = mw.text.trim(a[1] or '')
local record = data().values[key]
if not record then return missing(key) end
return format(record)
end
local function item(id)
if not tostring(id):match('^%d+$') then return nil end
return data().items['item' .. tostring(tonumber(id))]
end
local function iconFile(filename)
if not filename then return '' end
return '<span class="bc-item-inline-icon">[[File:' .. mw.text.nowiki(filename) .. '|20x20px|middle|link=|alt=]]</span>'
end
local function icon(emoji)
local record = (data().icons or {})[emoji]
return record and iconFile(record.imageFile) or ''
end
local function boostType(entry)
local tier = (entry.presentation or {}).boostTier
if not tier then return nil end
local category = tier.category == 'Tool' and 'Tool Augment' or (tier.category or '') .. ' Boost'
return 'T' .. mw.language.getContentLanguage():formatNum(tier.tier) .. ' ' .. category
end
local function itemType(entry)
local boost = boostType(entry)
if boost then return boost end
if entry.effect then return 'Rare Loot Boost' end
if entry.damage then return 'Weapon' end
for _, attribute in ipairs(entry.attributes) do
if attribute == 'equippable' then return 'Equipment' end
end
if (entry.presentation or {}).loot and entry.presentation.loot[1] then return 'Loot Container' end
if entry.growTime then return 'Crop' end
for _, attribute in ipairs(entry.attributes) do
if attribute == 'relayItem' then return 'Relay Material' end
end
for _, source in ipairs(entry.lootSources) do
if source == 'rare' then return 'Rare Item' end
end
if entry.usedToCraft[1] then return 'Crafting Ingredient' end
if entry.useLimit then return 'Usable Item' end
if entry.petEnergy then return 'Pet Food' end
return 'Material'
end
local function entryLink(entry, detailed)
local kind = detailed and boostType(entry)
local suffix = kind and '<span class="bc-item-link-tier"> (' .. mw.text.nowiki(kind) .. ')</span>' or ''
return iconFile(entry.imageFile) .. '[[' .. mw.text.nowiki(entry.article) .. '|' .. mw.text.nowiki(entry.name) .. ']]' .. suffix
end
local function linkByName(name, detailed)
for _, entry in pairs(data().items) do
if entry.idName == name then
return entryLink(entry, detailed)
end
end
return mw.text.nowiki(name)
end
function p.itemValue(frame)
local a = args(frame)
local entry = item(a.id or a[1] or '')
if not entry then return missing('item ' .. (a.id or a[1] or '')) end
local field = a.field or a[2] or ''
local value = entry[field]
if value == nil or type(value) == 'table' then return missing(field) end
local kind = 'text'
if field == 'growTime' or field == 'cooldownTime' then return itemDuration(value)
elseif type(value) == 'number' then kind = 'number' end
return format({value = value, format = kind})
end
local function number(value)
return mw.language.getContentLanguage():formatNum(value)
end
local function compact(value)
local units = {{1e12, 'T'}, {1e9, 'B'}, {1e6, 'M'}, {1e3, 'K'}}
for _, unit in ipairs(units) do
if value >= unit[1] then
local amount = math.floor(value / unit[1] * 100 + 0.5) / 100
return tostring(mw.html.create('abbr'):attr('title', number(value))
:wikitext(number(amount) .. unit[2]))
end
end
return number(value)
end
local function itemLink(id, detailed)
local entry = item(id)
if not entry then return missing('item ' .. tostring(id)) end
return entryLink(entry, detailed)
end
function p.infobox(frame)
local a = args(frame)
local entry = item(a.id or a[1] or '')
if not entry then return missing('item ' .. (a.id or a[1] or '')) end
local presentation = entry.presentation or {}
local box = mw.html.create('table'):addClass('wikitable bc-infobox bc-item-infobox')
box:tag('caption'):wikitext(mw.text.nowiki(entry.name))
local function row(label, value)
if not value or value == '' then return end
local tr = box:tag('tr')
tr:tag('th'):attr('scope', 'row'):wikitext(label)
tr:tag('td'):wikitext(value)
end
if entry.imageFile or entry.desc then
local picture = box:tag('tr'):tag('td'):attr('colspan', 2):addClass('bc-item-picture')
if entry.imageFile then picture:wikitext('[[File:' .. mw.text.nowiki(entry.imageFile) .. '|128px|alt=' .. mw.text.nowiki(entry.name) .. ']]') end
if entry.desc then picture:tag('blockquote'):addClass('bc-item-flavor'):wikitext(mw.text.nowiki(entry.desc)) end
end
row('Type', itemType(entry))
if entry.retired then row('Status', '[[:Category:Removed features|Removed from the current game]]') end
row('Worth', compact(entry.cost) .. ' BC')
local attributes = {}
for _, value in ipairs(entry.attributes) do attributes[value] = true end
local flags = {}
if entry.useLimit then table.insert(flags, 'Usable') end
if attributes.equippable then table.insert(flags, '[[Equipment|Equippable]]') end
if attributes.petHoldable or entry.damageMultiplier then table.insert(flags, '[[Pets|Pet holdable]]') end
if attributes.accountBound then table.insert(flags, 'Account bound') end
if attributes.relayItem then table.insert(flags, '[[Relay Network|Relay item]]') end
if attributes.relayRestricted then table.insert(flags, 'Cannot be [[Refinery|refined]]') end
if attributes.excludeFromMarket and not attributes.accountBound then table.insert(flags, 'Cannot be listed on the [[Market]]') end
row('Properties', table.concat(flags, ' · '))
if entry.damage then
row('Damage', compact(entry.damage) .. (entry.damageMultiplier and
' (' .. number(entry.damageMultiplier) .. '× worth)' or ''))
end
local effects = {}
local function boost(label, effect)
if effect then table.insert(effects, number(effect.multiplier) .. '× ' .. label .. ' for ' .. itemDuration(effect.duration)) end
end
boost('Pet Adventure Speed', entry.petAdventureBoostEffect)
boost('Farm Plot Multiplier', entry.farmPlotBoostEffect)
boost('Tool Augment', entry.augmentProperties)
if entry.effect then
local actions = {work='Work', fish='Fish', hunt='Hunt', mine='Mine', explore='Explore'}
boost(entry.effect.type == 'actionBoost' and (actions[entry.effect.action] or mw.text.nowiki(entry.effect.action)) or 'Rare Loot', entry.effect)
end
row('Effect', table.concat(effects, '<br />'))
if entry.cooldownTime then row('Cooldown', itemDuration(entry.cooldownTime)) end
if entry.growTime then row('Grow time', itemDuration(entry.growTime)) end
if entry.petEnergy then
row('Pet food', compact(entry.petEnergy) .. ' energy per item' ..
(entry.foodTier and ' · Food tier ' .. number(entry.foodTier) or ''))
end
local obtained = {}
for _, source in ipairs(presentation.lootSources or {}) do
local article = ({fish='Fishing', hunt='Hunting', mine='Mining', explore='Explore'})[source.source]
local target = source.itemId and itemLink(source.itemId) or
(source.emoji and icon(source.emoji) or '') ..
(article and '[[' .. article .. '|' .. mw.text.nowiki(source.label) .. ']]' or mw.text.nowiki(source.label))
local label = source.itemId and (source.source == 'rare' and 'Rare Loot / ' .. target or target .. ' Loot') or target
table.insert(obtained, label .. ' (' .. string.format('%.2f', math.max(0.01, source.percent)) .. '%)')
end
-- Older retained records may predate presentation metadata.
if not presentation.lootSources then
for _, source in ipairs(entry.lootSources) do table.insert(obtained, linkByName(source)) end
end
if entry.worthPerGainedFromBurn then table.insert(obtained, '[[Burn]]: 1 per ' .. compact(entry.worthPerGainedFromBurn) .. ' BC burned on average, before perks') end
if entry.worthPerGainedFromPetFeed then table.insert(obtained, '[[Pets|Pet feeding]]: 1 per ' .. compact(entry.worthPerGainedFromPetFeed) .. ' BC of food fed on average, before perks') end
if entry.worthPerGainedFromHarvest then table.insert(obtained, '[[Farm]]: 1 per ' .. compact(entry.worthPerGainedFromHarvest) .. ' BC of unboosted crops harvested on average, before perks') end
row('Obtained from', table.concat(obtained, '<br />'))
local recipe = {}
for _, ingredient in ipairs(entry.recipe) do
table.insert(recipe, linkByName(ingredient[1]) .. ' × ' .. compact(ingredient[2]))
end
if #recipe > 0 then row('Recipe', table.concat(recipe, '<br />')) end
row('Internal ID', '<code>' .. tostring(entry.id) .. '</code>')
return tostring(box)
end
function p.itemSections(frame)
local a = args(frame)
local entry = item(a.id or a[1] or '')
if not entry then return missing('item ' .. (a.id or a[1] or '')) end
local output = {}
local presentation = entry.presentation or {}
local usages = presentation.uses or {}
local categories = {tools='Tools', farm='Farm', pets='Pets', relay='Relay Network'}
local groups = {}
local crafting = {}
for _, name in ipairs(entry.usedToCraft) do table.insert(crafting, linkByName(name, true)) end
table.insert(groups, {title='Used to craft', entries=crafting})
for _, group in ipairs({{'upgrades', 'Used to upgrade'}, {'purchases', 'Used to purchase'}, {'research', 'Used to research'}}) do
local lines = {}
for _, usage in ipairs(usages[group[1]] or {}) do
local suffix = ''
if usage.threshold then
local prefix = ({bay='#', count='#', tier='Tier ', level='Lv. '})[usage.thresholdKind] or ''
suffix = ' (' .. prefix .. number(usage.threshold) .. '+)'
end
table.insert(lines, (usage.emoji and icon(usage.emoji) or '') .. '[[' .. categories[usage.category] .. '|' .. mw.text.nowiki(usage.label) .. ']]' .. suffix)
end
table.insert(groups, {title=group[2], entries=lines})
end
for _, group in ipairs(groups) do
if #group.entries > 0 then
if #output == 0 then table.insert(output, '\n== Item uses ==\n') end
table.insert(output, '\n=== ' .. group.title .. ' ===\n* ' .. table.concat(group.entries, '\n* ') .. '\n')
end
end
if presentation.loot and presentation.loot[1] then
table.insert(output, '\n== Loot ==\nThe following chances apply to each draw from this item’s loot pool.\n')
local loot = mw.html.create('table'):addClass('wikitable bc-item-loot')
local header = loot:tag('tr')
for _, title in ipairs({'Item', 'Chance', 'Odds'}) do header:tag('th'):attr('scope', 'col'):wikitext(title) end
for _, reward in ipairs(presentation.loot) do
local tr = loot:tag('tr')
tr:tag('td'):wikitext(itemLink(reward.itemId, true))
tr:tag('td'):wikitext(string.format('%.2f', reward.probability * 100) .. '%')
tr:tag('td'):wikitext(mw.text.nowiki(reward.odds))
end
table.insert(output, tostring(loot) .. '\n')
end
return table.concat(output)
end
function p.referenceTable(frame)
local key = args(frame)[1] or ''
local source = data().tables[key]
if not source then return missing(key) end
local output = mw.html.create('table'):addClass('wikitable')
local head = output:tag('tr')
for _, column in ipairs(source.columns) do head:tag('th'):attr('scope', 'col'):wikitext(mw.text.nowiki(column)) end
for _, record in ipairs(source.rows) do
local tr = output:tag('tr')
for _, value in ipairs(record) do
tr:tag('td'):wikitext(format({value=value, format=type(value) == 'number' and 'number' or 'text'}))
end
end
return tostring(output)
end
return p