模块:Shop
来自星砂岛百科
更多操作
模板:Documentation subpage Shop 提供商店库存、物品反查与商店元数据读取能力,供 Template:SoldBy、Template:ShopInventory 和 Template:Infobox building 调用。
示例
{{#invoke:Shop|renderSoldBy|土豆种子}}{{#invoke:Shop|renderShopInventory|肥记早餐店}}{{#invoke:Shop|getShopField|Shop.BreakfastCar|open_duration}}{{#invoke:Shop|getShopField|Shop.Grocery|currency_summary}}
导出函数
renderSoldBy:渲染物品页的商店出售反查表。renderShopInventory:渲染商店库存表。getShopField:获取商店元数据字段与页面级计算字段。
展示规则
- 金币 / 星砂 / 经验会优先转成
{{Gold}}/{{Star}}/{{Exp}}。 - 商店库存页会按较宽的物品大类分组,并在每组前输出小标题。
- 组内条目会按“大类 -> 物品类型 -> 分类语义 -> 条件层级 -> 物品系列 / 等级”稳定排序。
- 条件层级默认是“无条件 -> 普通条件 -> 称号等级 -> 任务 / 特殊条件”。
- 当某一组条目共享同一刷新周期时,`限购` 与 `刷新` 会自动合并成更紧凑的 `购买限制`。
字段
namedescriptionmap_descriptionkindopen_durationshop_templatefestival_templatelocation_namesarea_idssourcesentries
计算字段
kind_displaysource_displaylocation_displayarea_ids_displayarea_counttemplate_refs_displayentry_countcurrency_summaryauto_categories
local common = require('Module:Common')
local item_common = require('Module:ItemCommon')
local item = require('Module:Item')
local css = require('Module:CSS')
local p = {}
local SHOP_FIELD_MAP = {
name = 'n',
kind = 'k',
open_duration = 'od',
shop_template = 'st',
festival_template = 'ft',
area_ids = 'a',
sources = 'ss',
entries = 'e',
}
local ENTRY_FIELD_MAP = {
item_id = 'i',
group = 'g',
source = 's',
price_value = 'pv',
price_currency = 'pc',
price_costs = 'ca',
max_count = 'm',
refresh_interval = 'r',
condition = 'c',
title_requirement = 'tr',
discount = 'd',
need_discount = 'nd',
only_one = 'o',
ui_sort = 'u',
}
local REVERSE_SHOP_KEY = 'sid'
local DEFAULT_CURRENCY = 'Currency.Default'
local shop_data_cache
local shop_mapping_cache
local by_item_cache
local function normalize_key(value)
return common.normalizeKey(value)
end
local function load_shop_data()
if shop_data_cache then
return
end
local raw_data
raw_data, shop_mapping_cache = item_common.loadDomainData('数据:Shop/shop_index.json', '数据:Shop/shop_mapping.json')
if type(raw_data) == 'table' and type(raw_data.records) == 'table' then
shop_data_cache = raw_data.records
else
shop_data_cache = raw_data or {}
end
local raw_reverse = common.loadJsonData('数据:Shop/shop_by_item.json') or {}
if type(raw_reverse) == 'table' and type(raw_reverse.records) == 'table' then
by_item_cache = raw_reverse.records
else
by_item_cache = raw_reverse or {}
end
end
local function get_shop_field(record, field)
if type(record) ~= 'table' then
return nil
end
local short_key = SHOP_FIELD_MAP[field] or field
if record[short_key] ~= nil then
return record[short_key]
end
return record[field]
end
local function get_entry_field(entry, field)
if type(entry) ~= 'table' then
return nil
end
local short_key = ENTRY_FIELD_MAP[field] or field
if entry[short_key] ~= nil then
return entry[short_key]
end
return entry[field]
end
local function resolve_shop_id_from_mapping(value)
if type(shop_mapping_cache) ~= 'table' then
return ''
end
local normalized = normalize_key(value)
if normalized == '' then
return ''
end
if shop_mapping_cache.name_to_id and shop_mapping_cache.name_to_id[normalized] then
return shop_mapping_cache.name_to_id[normalized]
end
if shop_mapping_cache.aliases and shop_mapping_cache.aliases[normalized] then
return shop_mapping_cache.aliases[normalized]
end
return ''
end
local function find_shop_record(key)
load_shop_data()
local resolved = common.trim(key)
if resolved == '' then
resolved = common.getCurrentTitleText()
end
local normalized = normalize_key(resolved)
if shop_data_cache[normalized] then
return shop_data_cache[normalized], resolved
end
local mapped_id = resolve_shop_id_from_mapping(resolved)
if mapped_id ~= '' then
local mapped_key = normalize_key(mapped_id)
if shop_data_cache[mapped_key] then
return shop_data_cache[mapped_key], mapped_id
end
end
return nil, ''
end
local function resolve_item_id(key)
local record = item_common.findItemRecord(key)
if type(record) == 'table' and common.trim(record.id or '') ~= '' then
return common.trim(record.id)
end
return common.trim(key)
end
local function get_entries_for_item(key)
load_shop_data()
local item_id = resolve_item_id(key)
if item_id == '' then
return {}
end
return by_item_cache[normalize_key(item_id)] or {}
end
local function render_shop_link(shop_record, shop_id)
local display_name = common.trim(get_shop_field(shop_record, 'name') or shop_id)
if display_name == '' then
display_name = common.trim(shop_id)
end
if display_name == '' then
return ''
end
return ('[[%s]]'):format(display_name)
end
local function render_currency_item(frame, currency_id, count)
local resolved_currency = common.trim(currency_id)
if resolved_currency == '' then
return ''
end
local suffix = ''
if tonumber(count) and tonumber(count) > 0 then
suffix = tostring(math.floor(tonumber(count)))
end
return item.renderItemWithArgs(frame, {
resolved_currency,
suffix,
})
end
local function render_price(frame, entry)
local costs = get_entry_field(entry, 'price_costs')
if type(costs) == 'table' and #costs > 0 then
local parts = {}
for _, cost in ipairs(costs) do
if type(cost) == 'table' and common.trim(cost[1] or '') ~= '' then
parts[#parts + 1] = render_currency_item(frame, cost[1], cost[2])
end
end
return table.concat(parts, '')
end
local currency = common.trim(get_entry_field(entry, 'price_currency') or DEFAULT_CURRENCY)
local value = tonumber(get_entry_field(entry, 'price_value')) or 0
if currency == '' or currency == DEFAULT_CURRENCY then
if value <= 0 then
return '免费'
end
return tostring(math.floor(value)) .. ' 金币'
end
return render_currency_item(frame, currency, value)
end
local function format_refresh(refresh_interval)
local refresh = tonumber(refresh_interval) or 0
if refresh == -1 then
return '不刷新'
end
if refresh == 1 then
return '每日刷新'
end
if refresh > 1 then
return tostring(math.floor(refresh)) .. ' 天刷新'
end
return ''
end
local function format_limit(entry)
local parts = {}
local max_count = tonumber(get_entry_field(entry, 'max_count')) or 0
if max_count == -1 or max_count == 0 then
parts[#parts + 1] = '不限购'
else
parts[#parts + 1] = '限购 ' .. tostring(math.floor(max_count))
end
local refresh_text = format_refresh(get_entry_field(entry, 'refresh_interval'))
if refresh_text ~= '' then
parts[#parts + 1] = refresh_text
end
if get_entry_field(entry, 'only_one') then
parts[#parts + 1] = '单次仅购 1'
end
return table.concat(parts, ' / ')
end
local function format_condition(entry)
local parts = {}
local title_requirement = common.trim(get_entry_field(entry, 'title_requirement') or '')
local condition = common.trim(get_entry_field(entry, 'condition') or '')
local discount = tonumber(get_entry_field(entry, 'discount')) or 0
local need_discount = get_entry_field(entry, 'need_discount')
if title_requirement ~= '' then
parts[#parts + 1] = title_requirement
end
if condition ~= '' then
parts[#parts + 1] = condition
end
if discount ~= 0 then
parts[#parts + 1] = '折扣值 ' .. tostring(math.floor(discount))
elseif need_discount then
parts[#parts + 1] = '受折扣系统影响'
end
if #parts == 0 then
return ''
end
return table.concat(parts, '<br>')
end
local function render_item_cell(frame, entry)
local item_id = common.trim(get_entry_field(entry, 'item_id') or '')
if item_id == '' then
return ''
end
return item.renderItemWithArgs(frame, { item_id, class = 'block' })
end
local function render_sold_by_table(frame, entries)
if type(entries) ~= 'table' or #entries == 0 then
return ''
end
load_shop_data()
local out = {}
out[#out + 1] = css.quickCall('Item') or ''
out[#out + 1] = '{| class="wikitable"'
out[#out + 1] = '! 商店'
out[#out + 1] = '! 价格'
out[#out + 1] = '! 限购 / 刷新'
out[#out + 1] = '! 条件'
for _, entry in ipairs(entries) do
local shop_id = common.trim(entry[REVERSE_SHOP_KEY] or '')
local shop_record = shop_data_cache[normalize_key(shop_id)]
local shop_cell = render_shop_link(shop_record, shop_id)
local price_cell = render_price(frame, entry)
local limit_cell = format_limit(entry)
local condition_cell = format_condition(entry)
if condition_cell == '' then
condition_cell = '无'
end
out[#out + 1] = '|-'
out[#out + 1] = '| ' .. shop_cell
out[#out + 1] = '| ' .. price_cell
out[#out + 1] = '| ' .. limit_cell
out[#out + 1] = '| ' .. condition_cell
end
out[#out + 1] = '|}'
return table.concat(out, '\n')
end
local function render_inventory_table(frame, shop_record)
local entries = get_shop_field(shop_record, 'entries')
if type(entries) ~= 'table' or #entries == 0 then
return ''
end
local out = {}
out[#out + 1] = css.quickCall('Item') or ''
out[#out + 1] = '{| class="wikitable"'
out[#out + 1] = '! 物品'
out[#out + 1] = '! 分类'
out[#out + 1] = '! 价格'
out[#out + 1] = '! 限购 / 刷新'
out[#out + 1] = '! 条件'
for _, entry in ipairs(entries) do
local category = common.trim(get_entry_field(entry, 'group') or '')
local price_cell = render_price(frame, entry)
local limit_cell = format_limit(entry)
local condition_cell = format_condition(entry)
if category == '' then
category = '未分组'
end
if condition_cell == '' then
condition_cell = '无'
end
out[#out + 1] = '|-'
out[#out + 1] = '| ' .. render_item_cell(frame, entry)
out[#out + 1] = '| ' .. category
out[#out + 1] = '| ' .. price_cell
out[#out + 1] = '| ' .. limit_cell
out[#out + 1] = '| ' .. condition_cell
end
out[#out + 1] = '|}'
return table.concat(out, '\n')
end
function p.getShopField(frame)
local key = common.getArg(frame, 1, '')
local field = common.getArg(frame, 2, '')
if field == '' then
return ''
end
local shop_record = find_shop_record(key)
if not shop_record then
return ''
end
return common.toText(get_shop_field(shop_record, field))
end
function p.renderSoldBy(frame)
local key = common.getArg(frame, 1, '')
local entries = get_entries_for_item(key)
if #entries == 0 then
return '暂无商店出售记录。'
end
return render_sold_by_table(frame, entries)
end
function p.renderShopInventory(frame)
local key = common.getArg(frame, 1, '')
local shop_record = find_shop_record(key)
if not shop_record then
return '未找到商店数据。'
end
return render_inventory_table(frame, shop_record)
end
return p