打开/关闭菜单
打开/关闭外观设置菜单
打开/关闭个人菜单
未登录
未登录用户的IP地址会在进行任意编辑后公开展示。

本站正在进行早期测试,目前仍存在许多内容的缺失。

模块:Shop:修订间差异

来自星砂岛百科
Sizau-bot留言 | 贡献
同步更新
 
Sizau-bot留言 | 贡献
同步更新
第86行: 第86行:
     end
     end
     return entry[field]
     return entry[field]
end
local function has_items(value)
    if type(value) ~= 'table' then
        return false
    end
    return next(value) ~= nil
end
end


第174行: 第181行:
local function render_price(frame, entry)
local function render_price(frame, entry)
     local costs = get_entry_field(entry, 'price_costs')
     local costs = get_entry_field(entry, 'price_costs')
     if type(costs) == 'table' and #costs > 0 then
     if has_items(costs) then
         local parts = {}
         local parts = {}
         for _, cost in ipairs(costs) do
         for _, cost in ipairs(costs) do
第265行: 第272行:


local function render_sold_by_table(frame, entries)
local function render_sold_by_table(frame, entries)
     if type(entries) ~= 'table' or #entries == 0 then
     if not has_items(entries) then
         return ''
         return ''
     end
     end
第303行: 第310行:
local function render_inventory_table(frame, shop_record)
local function render_inventory_table(frame, shop_record)
     local entries = get_shop_field(shop_record, 'entries')
     local entries = get_shop_field(shop_record, 'entries')
     if type(entries) ~= 'table' or #entries == 0 then
     if not has_items(entries) then
         return ''
         return ''
     end
     end
第358行: 第365行:
     local key = common.getArg(frame, 1, '')
     local key = common.getArg(frame, 1, '')
     local entries = get_entries_for_item(key)
     local entries = get_entries_for_item(key)
     if #entries == 0 then
     if not has_items(entries) then
         return '暂无商店出售记录。'
         return '暂无商店出售记录。'
     end
     end
第370行: 第377行:
         return '未找到商店数据。'
         return '未找到商店数据。'
     end
     end
     return render_inventory_table(frame, shop_record)
     local rendered = render_inventory_table(frame, shop_record)
    if rendered == '' then
        return '暂无商店库存记录。'
    end
    return rendered
end
end


return p
return p

2026年3月17日 (二) 11:59的版本

模板:Documentation subpage Shop 提供商店库存、物品反查与商店元数据读取能力,供 Template:SoldByTemplate:ShopInventoryTemplate: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}}
  • 商店库存页会按较宽的物品大类分组,并在每组前输出小标题。
  • 组内条目会按“大类 -> 物品类型 -> 分类语义 -> 条件层级 -> 物品系列 / 等级”稳定排序。
  • 条件层级默认是“无条件 -> 普通条件 -> 称号等级 -> 任务 / 特殊条件”。
  • 当某一组条目共享同一刷新周期时,`限购` 与 `刷新` 会自动合并成更紧凑的 `购买限制`。

字段

  • name
  • description
  • map_description
  • kind
  • open_duration
  • shop_template
  • festival_template
  • location_names
  • area_ids
  • sources
  • entries

计算字段

  • kind_display
  • source_display
  • location_display
  • area_ids_display
  • area_count
  • template_refs_display
  • entry_count
  • currency_summary
  • auto_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 has_items(value)
    if type(value) ~= 'table' then
        return false
    end
    return next(value) ~= nil
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 has_items(costs) 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 not has_items(entries) 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 not has_items(entries) 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 not has_items(entries) 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
    local rendered = render_inventory_table(frame, shop_record)
    if rendered == '' then
        return '暂无商店库存记录。'
    end
    return rendered
end

return p