模块:ItemOverview
来自星砂岛百科
更多操作
ItemOverview 提供物品页通用的“条目速览”区块,把 `获取 / 用途 / 赠礼 / 关联条目或特性` 汇总成读者更容易扫读的卡片。
常见调用方式:
{{#invoke:ItemOverview|renderOverview|面粉}}- 更推荐通过
{{ItemOverview}}包装模板调用,让标题和变量处理保持统一。
local common = require('Module:Common')
local css = require('Module:CSS')
local item_common = require('Module:ItemCommon')
local p = {}
local shop_by_item_cache
local gift_items_cache
local gift_redirects_cache
local gift_item_index_by_id
local machine_by_item_cache
local domain_record_caches
local POSITIVE_GIFT_BUCKETS = {
{ key = 'r', label = '共鸣' },
{ key = 's', label = '特殊' },
{ key = 'v', label = '最爱' },
{ key = 'k', label = '喜欢' },
}
local RESPONSE_GIFT_BUCKETS = {
{ key = 'n', label = '一般' },
{ key = 'd', label = '不喜欢' },
{ key = 'h', label = '讨厌' },
}
local DOMAIN_RECORD_CONFIGS = {
{ data_page = '数据:Resource/resource_index.json', mapping_page = '数据:Resource/resource_mapping.json' },
{ data_page = '数据:Processed/processed_index.json', mapping_page = '数据:Processed/processed_mapping.json' },
{ data_page = '数据:Food/food_index.json', mapping_page = '数据:Food/food_mapping.json' },
{ data_page = '数据:Seed/seed_index.json', mapping_page = '数据:Seed/seed_mapping.json' },
{ data_page = '数据:Tool/tool_index.json', mapping_page = '数据:Tool/tool_mapping.json' },
{ data_page = '数据:Crop/crop_index.json', mapping_page = '数据:Crop/crop_mapping.json' },
}
local function normalize_key(value)
return common.normalizeKey(value)
end
local function count_array(values)
if type(values) ~= 'table' then
return 0
end
local count = 0
for _, _ in ipairs(values) do
count = count + 1
end
return count
end
local function trim_text(value)
return common.trim(common.toText(value))
end
local function non_empty_parts(parts)
local out = {}
for _, part in ipairs(parts or {}) do
local text = trim_text(part)
if text ~= '' then
out[#out + 1] = text
end
end
return out
end
local function join_parts(parts)
local filtered = non_empty_parts(parts)
if #filtered == 0 then
return ''
end
return table.concat(filtered, ' · ')
end
local function has_items(values)
return count_array(values) > 0
end
local function count_bucket_members(bucket)
if type(bucket) ~= 'table' then
return 0
end
local seen = {}
local count = 0
local function append(values)
if type(values) ~= 'table' then
return
end
for _, value in ipairs(values) do
local numeric = tonumber(value)
if numeric ~= nil and not seen[numeric] then
seen[numeric] = true
count = count + 1
end
end
end
append(bucket.p)
append(bucket.g)
return count
end
local function count_unique_bucket_group(source, keys)
if type(source) ~= 'table' then
return 0
end
local seen = {}
local count = 0
for _, key in ipairs(keys) do
local bucket = source[key]
if type(bucket) == 'table' then
for _, list_key in ipairs({ 'p', 'g' }) do
local values = bucket[list_key]
if type(values) == 'table' then
for _, value in ipairs(values) do
local numeric = tonumber(value)
if numeric ~= nil and not seen[numeric] then
seen[numeric] = true
count = count + 1
end
end
end
end
end
end
return count
end
local function load_shop_data()
if shop_by_item_cache then
return
end
local raw = common.loadJsonData('数据:Shop/shop_by_item.json') or {}
if type(raw) == 'table' and type(raw.records) == 'table' then
shop_by_item_cache = raw.records
else
shop_by_item_cache = raw or {}
end
end
local function get_shop_summary(item_id)
load_shop_data()
local entries = shop_by_item_cache[normalize_key(item_id)] or {}
local shops_seen = {}
local entry_count = 0
for _, entry in ipairs(entries) do
entry_count = entry_count + 1
if type(entry) == 'table' then
local shop_id = trim_text(entry.sid or entry.s)
if shop_id ~= '' then
shops_seen[normalize_key(shop_id)] = true
end
end
end
local shop_count = 0
for _, _ in pairs(shops_seen) do
shop_count = shop_count + 1
end
return {
entry_count = entry_count,
shop_count = shop_count,
}
end
local function load_gift_data()
if gift_items_cache then
return
end
local raw = common.loadJsonData('数据:Gifting/gift_preferences_by_item.json') or {}
gift_items_cache = raw.items or {}
gift_redirects_cache = raw.item_redirects or {}
local registry = common.loadJsonData('数据:Gifting/gift_id_registry.json') or {}
local registry_items = registry.items or {}
gift_item_index_by_id = {}
for idx, value in ipairs(registry_items) do
local normalized = normalize_key(value)
if normalized ~= '' then
gift_item_index_by_id[normalized] = idx - 1
end
end
end
local function resolve_gift_index(index)
local key = tostring(index)
local numeric = tonumber(index)
if gift_items_cache[key] then
return key
end
if numeric ~= nil and gift_items_cache[numeric] then
return numeric
end
local redirected = gift_redirects_cache[key]
if redirected == nil and numeric ~= nil then
redirected = gift_redirects_cache[numeric]
end
if redirected == nil then
return nil
end
local redirected_key = tostring(redirected)
local redirected_numeric = tonumber(redirected)
if gift_items_cache[redirected_key] then
return redirected_key
end
if redirected_numeric ~= nil and gift_items_cache[redirected_numeric] then
return redirected_numeric
end
return nil
end
local function get_gift_summary(item_id)
load_gift_data()
local item_index = gift_item_index_by_id[normalize_key(item_id)]
if item_index == nil then
return {
value = '不可送礼',
meta = '',
}
end
local resolved_index = resolve_gift_index(item_index)
local record = resolved_index ~= nil and gift_items_cache[resolved_index] or nil
local summary = type(record) == 'table' and record.s or nil
if type(summary) ~= 'table' then
return {
value = '不可送礼',
meta = '',
}
end
local positive_total = count_unique_bucket_group(summary, { 'r', 's', 'v', 'k' })
if positive_total > 0 then
local meta_parts = {}
for _, bucket in ipairs(POSITIVE_GIFT_BUCKETS) do
local count = count_bucket_members(summary[bucket.key])
if count > 0 then
meta_parts[#meta_parts + 1] = bucket.label .. ' ' .. tostring(count)
end
end
return {
value = tostring(positive_total) .. ' 位对象偏好',
meta = join_parts(meta_parts),
}
end
local response_total = count_unique_bucket_group(summary, { 'n', 'd', 'h' })
if response_total > 0 then
local meta_parts = {}
for _, bucket in ipairs(RESPONSE_GIFT_BUCKETS) do
local count = count_bucket_members(summary[bucket.key])
if count > 0 then
meta_parts[#meta_parts + 1] = bucket.label .. ' ' .. tostring(count)
end
end
return {
value = '可送礼',
meta = join_parts(meta_parts),
}
end
return {
value = '不可送礼',
meta = '',
}
end
local function load_machine_data()
if machine_by_item_cache then
return
end
local raw = common.loadJsonData('数据:Machine/machine_recipes.json') or {}
machine_by_item_cache = raw.by_item or {}
end
local function load_domain_record_caches()
if domain_record_caches then
return
end
domain_record_caches = {}
for _, config in ipairs(DOMAIN_RECORD_CONFIGS) do
local data, mapping = item_common.loadDomainData(config.data_page, config.mapping_page)
domain_record_caches[#domain_record_caches + 1] = {
data = data,
mapping = mapping,
}
end
end
local function append_unique_value(values, seen, value)
local text = trim_text(value)
local normalized = normalize_key(text)
if text == '' or normalized == '' or seen[normalized] then
return
end
seen[normalized] = true
values[#values + 1] = text
end
local function find_detail_record(key, item_id)
load_domain_record_caches()
local candidates = {}
local seen = {}
append_unique_value(candidates, seen, key)
append_unique_value(candidates, seen, item_id)
for _, cache in ipairs(domain_record_caches) do
for _, candidate in ipairs(candidates) do
local record = item_common.findRecord(cache.data, cache.mapping, candidate)
if type(record) == 'table' then
return record
end
end
end
return nil
end
local function get_recipe_summary(item_id)
load_machine_data()
local record = machine_by_item_cache[normalize_key(item_id)]
if type(record) ~= 'table' then
return {
production_count = 0,
usage_count = 0,
}
end
return {
production_count = count_array(record.p),
usage_count = count_array(record.i),
}
end
local function collect_machine_names(record)
local out = {}
local seen = {}
local function append(values)
if type(values) ~= 'table' then
return
end
for _, value in ipairs(values) do
local name = trim_text(value)
local normalized = normalize_key(name)
if name ~= '' and normalized ~= '' and not seen[normalized] then
seen[normalized] = true
out[#out + 1] = name
end
end
end
append(record.production_machines)
append(record.process_machines)
return out
end
local function build_source_card(record, shop_summary)
local value = trim_text(item_common.getDisplayField(record, 'origin'))
local locked_origin = trim_text(item_common.getDisplayField(record, 'locked_origin'))
local meta_parts = {}
if shop_summary.shop_count > 0 then
meta_parts[#meta_parts + 1] = '共 ' .. tostring(shop_summary.shop_count) .. ' 处商店出售'
end
if locked_origin ~= '' then
meta_parts[#meta_parts + 1] = '解锁:' .. locked_origin
end
if value == '' then
if shop_summary.shop_count > 0 then
value = '商店出售'
elseif locked_origin ~= '' then
value = '需要解锁'
else
return nil
end
end
return {
label = '获取',
value = value,
meta = join_parts(meta_parts),
}
end
local function build_usage_card(record, recipe_summary)
local value = trim_text(item_common.getDisplayField(record, 'use_description'))
local meta_parts = {}
if recipe_summary.production_count > 0 then
meta_parts[#meta_parts + 1] = '可由 ' .. tostring(recipe_summary.production_count) .. ' 种配方获得'
end
if recipe_summary.usage_count > 0 then
meta_parts[#meta_parts + 1] = '用于 ' .. tostring(recipe_summary.usage_count) .. ' 个配方'
end
local machine_names = collect_machine_names(record)
if has_items(machine_names) then
meta_parts[#meta_parts + 1] = '相关设备:' .. item_common.machineLinks(machine_names)
end
if value == '' then
if recipe_summary.usage_count > 0 then
value = '参与制作'
elseif recipe_summary.production_count > 0 then
value = '可被制作'
elseif has_items(machine_names) then
value = '设备相关'
else
return nil
end
end
return {
label = '用途',
value = value,
meta = join_parts(meta_parts),
}
end
local function build_gift_card(item_id)
local summary = get_gift_summary(item_id)
return {
label = '赠礼',
value = summary.value,
meta = summary.meta,
}
end
local function build_feature_card(record)
local crop_link = trim_text(item_common.getDisplayField(record, 'crop_item_link'))
if crop_link ~= '' then
return {
label = '关联条目',
value = crop_link,
meta = '对应作物',
}
end
local seed_link = trim_text(item_common.getDisplayField(record, 'seed_item_link'))
if seed_link ~= '' then
return {
label = '关联条目',
value = seed_link,
meta = '对应种子',
}
end
local tool_usage = trim_text(item_common.getDisplayField(record, 'tool_usage'))
local weapon_type = trim_text(item_common.getDisplayField(record, 'hunting_weapon_type'))
if tool_usage ~= '' or weapon_type ~= '' then
local value = tool_usage ~= '' and tool_usage or weapon_type
local meta = ''
if tool_usage ~= '' and weapon_type ~= '' then
meta = '发射类型:' .. weapon_type
end
return {
label = tool_usage ~= '' and '工具用途' or '武器类型',
value = value,
meta = meta,
}
end
local pet_target = trim_text(item_common.getDisplayField(record, 'pet_target_tag'))
if pet_target ~= '' then
return {
label = '适用对象',
value = pet_target,
meta = '',
}
end
local placement = trim_text(item_common.getDisplayField(record, 'placement_template'))
if placement ~= '' then
return {
label = '摆放类型',
value = placement,
meta = '',
}
end
local process_formula_origin = trim_text(item_common.getDisplayField(record, 'process_formula_origin'))
if process_formula_origin ~= '' then
return {
label = '适用设备',
value = process_formula_origin,
meta = '',
}
end
return nil
end
local function render_card(grid, card)
if type(card) ~= 'table' then
return false
end
local value = trim_text(card.value)
if value == '' then
return false
end
local node = grid:tag('div'):addClass('item-overview-card')
node:tag('div'):addClass('item-overview-label'):wikitext(trim_text(card.label))
node:tag('div'):addClass('item-overview-value'):wikitext(value)
local meta = trim_text(card.meta)
if meta ~= '' then
node:tag('div'):addClass('item-overview-meta'):wikitext(meta)
end
return true
end
function p.renderOverview(frame)
local key = common.getArg(frame, 1, '')
local identity_record = item_common.findItemRecord(key)
if type(identity_record) ~= 'table' then
return ''
end
local item_id = item_common.resolveItemId(identity_record, key)
if item_id == '' then
return ''
end
local record = find_detail_record(key, item_id) or identity_record
local cards = {}
local function append_card(card)
if type(card) == 'table' then
cards[#cards + 1] = card
end
end
append_card(build_source_card(record, get_shop_summary(item_id)))
append_card(build_usage_card(record, get_recipe_summary(item_id)))
append_card(build_gift_card(item_id))
append_card(build_feature_card(record))
local root = mw.html.create('div'):addClass('item-overview')
local grid = root:tag('div'):addClass('item-overview-grid')
local rendered_count = 0
for _, card in ipairs(cards) do
if render_card(grid, card) then
rendered_count = rendered_count + 1
end
end
if rendered_count == 0 then
return ''
end
return (css.quickCall('ItemOverview') or '') .. tostring(root)
end
return p