模块:Machine:修订间差异
来自星砂岛百科
更多操作
无编辑摘要 |
优化 RecipeUsage 机器展示结构与样式 |
||
| (未显示2个用户的33个中间版本) | |||
| 第1行: | 第1行: | ||
local common = require('Module:Common') | local common = require('Module:Common') | ||
local item_common = require('Module:ItemCommon') | local item_common = require('Module:ItemCommon') | ||
local item = require('Module:Item') | |||
local css = require('Module:CSS') | |||
local p = {} | local p = {} | ||
local FIELD_MAP = { | local FIELD_MAP = { | ||
type = 't', | type = 't', | ||
type_display = 'td', | type_display = 'td', | ||
| 第28行: | 第27行: | ||
craft = 'c', | craft = 'c', | ||
upgrade = 'u', | upgrade = 'u', | ||
prev_machine = 'pm', | |||
next_machine = 'nm', | |||
} | |||
local RECIPE_FIELD_DEFAULTS = { | |||
result = 'r', | |||
result_count = 'rc', | |||
materials = 'm', | |||
time = 't', | |||
machine = 'mc', | |||
cooking_method = 'cm', | |||
} | } | ||
local data_cache | local data_cache | ||
local mapping_cache | local mapping_cache | ||
local recipe_cache | |||
local recipe_list | |||
local item_recipe_cache | |||
local field_map = RECIPE_FIELD_DEFAULTS | |||
local function is_empty(value) | local function is_empty(value) | ||
| 第41行: | 第55行: | ||
end | end | ||
return true | return true | ||
end | |||
local function count_array(arr) | |||
if type(arr) ~= 'table' then | |||
return 0 | |||
end | |||
local count = 0 | |||
for _ in pairs(arr) do | |||
count = count + 1 | |||
end | |||
return count | |||
end | end | ||
| 第49行: | 第75行: | ||
local raw_data | local raw_data | ||
raw_data, mapping_cache = item_common.loadDomainData( | raw_data, mapping_cache = item_common.loadDomainData('数据:Machine/machine_index.json') | ||
if type(raw_data) == 'table' and type(raw_data.records) == 'table' then | if type(raw_data) == 'table' and type(raw_data.records) == 'table' then | ||
data_cache = raw_data.records | data_cache = raw_data.records | ||
| 第69行: | 第92行: | ||
end | end | ||
local function get_record_field(record, field) | local function get_record_field(record, key, field) | ||
if type(record) ~= 'table' then | if type(record) ~= 'table' then | ||
return nil | return nil | ||
end | end | ||
local short_key = get_short_field(field) | local short_key = get_short_field(field) | ||
if record[short_key] ~= nil then | if record[short_key] ~= nil then | ||
return record[short_key] | return record[short_key] | ||
end | end | ||
return record[field] | if record[field] ~= nil then | ||
return record[field] | |||
end | |||
if field == 'id' or field == 'name' or field == 'name_en' then | |||
return item_common.getIdentityField(record, key, field) | |||
end | |||
return nil | |||
end | end | ||
| 第96行: | 第126行: | ||
end | end | ||
if seconds < 60 then | if seconds < 60 then | ||
return tostring(math.floor(seconds)) .. ' | return tostring(math.floor(seconds)) .. '秒' | ||
end | end | ||
local minutes = math.floor(seconds / 60) | local minutes = math.floor(seconds / 60) | ||
local remain = math.floor(seconds % 60) | local remain = math.floor(seconds % 60) | ||
if remain == 0 then | if remain == 0 then | ||
return tostring(minutes) .. ' | return tostring(minutes) .. '分' | ||
end | end | ||
return tostring(minutes) .. ' | return tostring(minutes) .. '分' .. tostring(remain) .. '秒' | ||
end | end | ||
| 第110行: | 第141行: | ||
return '' | return '' | ||
end | end | ||
local item_ids = {} | |||
for item_id in pairs(materials) do | |||
item_ids[#item_ids + 1] = item_id | |||
end | |||
item_common.sortItemKeys(item_ids) | |||
local parts = {} | local parts = {} | ||
for item_id | for _, item_id in ipairs(item_ids) do | ||
parts[#parts + 1] = frame:expandTemplate{ | parts[#parts + 1] = frame:expandTemplate{ | ||
title = 'Item', | title = 'Item', | ||
args = { item_id, tostring( | args = { item_id, tostring(materials[item_id]), class = 'block' }, | ||
} | } | ||
end | end | ||
return table.concat(parts, '') | return table.concat(parts, '') | ||
end | |||
local function render_item_template(frame, item_id) | |||
local resolved_id = common.trim(item_id or '') | |||
if resolved_id == '' then | |||
return '' | |||
end | |||
local css_out = css.quickCall('Item') or '' | |||
return css_out .. item.renderItemWithArgs(frame, { resolved_id }) | |||
end | |||
local function page_exists(title_text) | |||
local title = mw.title.new(common.trim(title_text)) | |||
return title and title.exists or false | |||
end | |||
local function render_machine_reference(machine_id) | |||
local resolved_id = common.trim(machine_id or '') | |||
if resolved_id == '' then | |||
return '' | |||
end | |||
local machine_name = common.trim(item.getNameByKey(resolved_id) or '') | |||
if machine_name == '' then | |||
machine_name = resolved_id | |||
end | |||
local image_name = resolved_id .. '.png' | |||
local has_icon = common.filePageExists(image_name) | |||
local out = { '<span class="itemtemplate itemtemplateblock recipe-usage-machine-chip">' } | |||
if has_icon then | |||
out[#out + 1] = '<span class="item-icon-container">' | |||
out[#out + 1] = ('[[File:%s|24x24px|link=]]'):format(image_name) | |||
out[#out + 1] = '</span>' | |||
end | |||
out[#out + 1] = '<span class="item-text">' | |||
if page_exists(machine_name) then | |||
out[#out + 1] = ('[[%s|%s]]'):format(machine_name, machine_name) | |||
else | |||
out[#out + 1] = mw.text.encode(machine_name) | |||
end | |||
out[#out + 1] = '</span>' | |||
out[#out + 1] = '</span>' | |||
return table.concat(out) | |||
end | end | ||
local function render_craft_table(title_text, station_label, station_value, materials_label, materials_value, time_label, time_value, exp_label, exp_value) | local function render_craft_table(title_text, station_label, station_value, materials_label, materials_value, time_label, time_value, exp_label, exp_value) | ||
local out = {} | local out = {} | ||
out[#out + 1] = ' | out[#out + 1] = '<div class="recipe-usage-table-wrap">' | ||
out[#out + 1] = ' | out[#out + 1] = '<table class="wikitable machine-craft-table recipe-usage-table">' | ||
out[#out + 1] = ' | out[#out + 1] = '<tr><th colspan="2">' .. title_text .. '</th></tr>' | ||
out[#out + 1] = ' | out[#out + 1] = '<tr><th>' .. station_label .. '</th><td>' .. station_value .. '</td></tr>' | ||
out[#out + 1] = '<tr><th>' .. materials_label .. '</th><td>' .. materials_value .. '</td></tr>' | |||
out[#out + 1] = ' | |||
if time_value ~= '' then | if time_value ~= '' then | ||
out[#out + 1] = ' | out[#out + 1] = '<tr><th>' .. time_label .. '</th><td>' .. time_value .. '</td></tr>' | ||
end | end | ||
if exp_value ~= '' then | if exp_value ~= '' then | ||
out[#out + 1] = ' | out[#out + 1] = '<tr><th>' .. exp_label .. '</th><td>' .. exp_value .. '</td></tr>' | ||
end | end | ||
out[#out + 1] = ' | out[#out + 1] = '</table>' | ||
out[#out + 1] = '</div>' | |||
return table.concat(out, '\n') | return table.concat(out, '\n') | ||
end | |||
local function is_array_table(value) | |||
if type(value) ~= 'table' then | |||
return false | |||
end | |||
local count = 0 | |||
for key in pairs(value) do | |||
if type(key) ~= 'number' then | |||
return false | |||
end | |||
count = count + 1 | |||
end | |||
if count == 0 then | |||
return false | |||
end | |||
for index = 1, count do | |||
if value[index] == nil then | |||
return false | |||
end | |||
end | |||
return true | |||
end | |||
local function normalize_material_options(materials) | |||
if type(materials) ~= 'table' then | |||
return {} | |||
end | |||
if is_array_table(materials) then | |||
local options = {} | |||
for _, option in ipairs(materials) do | |||
if type(option) == 'table' and not is_empty(option) then | |||
options[#options + 1] = option | |||
end | |||
end | |||
return options | |||
end | |||
if is_empty(materials) then | |||
return {} | |||
end | |||
return { materials } | |||
end | |||
local function render_material_option(frame, materials) | |||
if type(materials) ~= 'table' or is_empty(materials) then | |||
return '' | |||
end | |||
local item_ids = {} | |||
for item_id in pairs(materials) do | |||
item_ids[#item_ids + 1] = item_id | |||
end | |||
item_common.sortItemKeys(item_ids) | |||
local parts = {} | |||
for _, item_id in ipairs(item_ids) do | |||
parts[#parts + 1] = item.renderItemWithArgs(frame, { item_id, tostring(materials[item_id]), class = 'block' }) | |||
end | |||
return table.concat(parts, '') | |||
end | |||
local function filter_material_option(materials, excluded_item_id) | |||
if type(materials) ~= 'table' then | |||
return {} | |||
end | |||
local filtered = {} | |||
for item_id, count in pairs(materials) do | |||
if item_id ~= excluded_item_id then | |||
filtered[item_id] = count | |||
end | |||
end | |||
return filtered | |||
end | |||
local function render_material_option_or_empty(frame, materials) | |||
local rendered = render_material_option(frame, materials) | |||
if rendered == '' then | |||
return '—' | |||
end | |||
return rendered | |||
end | |||
local function render_recipe_materials(frame, materials, excluded_item_id) | |||
local options = normalize_material_options(materials) | |||
if #options == 0 then | |||
return '' | |||
end | |||
if excluded_item_id and excluded_item_id ~= '' then | |||
local filtered_options = {} | |||
local has_non_empty_option = false | |||
for _, option in ipairs(options) do | |||
local filtered = filter_material_option(option, excluded_item_id) | |||
if not is_empty(filtered) then | |||
has_non_empty_option = true | |||
end | |||
filtered_options[#filtered_options + 1] = filtered | |||
end | |||
if not has_non_empty_option then | |||
return '—' | |||
end | |||
options = filtered_options | |||
end | |||
if #options == 1 then | |||
return render_material_option_or_empty(frame, options[1]) | |||
end | |||
local out = {} | |||
out[#out + 1] = '<div class="recipe-usage-option-label">可选材料方案</div>' | |||
out[#out + 1] = '<ul class="recipe-usage-option-list">' | |||
for _, option in ipairs(options) do | |||
out[#out + 1] = '<li>' .. render_material_option_or_empty(frame, option) .. '</li>' | |||
end | |||
out[#out + 1] = '</ul>' | |||
return table.concat(out, '') | |||
end | |||
local function material_counts_for_item(materials, item_id) | |||
local counts = {} | |||
local seen = {} | |||
for _, option in ipairs(normalize_material_options(materials)) do | |||
local count = tonumber(option[item_id]) or 0 | |||
if count > 0 and not seen[count] then | |||
seen[count] = true | |||
counts[#counts + 1] = count | |||
end | |||
end | |||
table.sort(counts) | |||
return counts | |||
end | |||
local function render_material_count_list(counts) | |||
if #counts == 0 then | |||
return '' | |||
end | |||
if #counts == 1 then | |||
return tostring(counts[1]) | |||
end | |||
local out = { '<ul class="recipe-usage-count-list">' } | |||
for _, count in ipairs(counts) do | |||
out[#out + 1] = '<li>' .. tostring(count) .. '</li>' | |||
end | |||
out[#out + 1] = '</ul>' | |||
return table.concat(out, '') | |||
end | |||
local function load_recipes() | |||
if recipe_cache then | |||
return | |||
end | |||
local data = common.loadJsonData('数据:Machine/machine_recipes.json') or {} | |||
recipe_list = data.recipes or {} | |||
recipe_cache = data.by_machine or {} | |||
item_recipe_cache = data.by_item or {} | |||
field_map = (data._meta or {}).field_map or RECIPE_FIELD_DEFAULTS | |||
end | |||
local function recipe_time_text(recipe) | |||
if not recipe then | |||
return '' | |||
end | |||
local value = tonumber(recipe[field_map.time]) or 0 | |||
if value <= 0 then | |||
return '' | |||
end | |||
return format_time(value) | |||
end | |||
local function render_recipe_result(frame, recipe) | |||
if not recipe then | |||
return '' | |||
end | |||
local result_id = recipe[field_map.result] | |||
if not result_id or result_id == '' then | |||
return '' | |||
end | |||
local result_count = tonumber(recipe[field_map.result_count]) or 1 | |||
if result_count > 1 then | |||
return item.renderItemWithArgs(frame, { result_id, tostring(result_count), class = 'block' }) | |||
end | |||
return item.renderItemWithArgs(frame, { result_id, class = 'block' }) | |||
end | |||
local function machine_group_heading(frame, machine_id) | |||
local rendered = render_machine_reference(machine_id) | |||
if rendered == '' then | |||
return '' | |||
end | |||
return '<div class="recipe-usage-machine"><div class="recipe-usage-machine-label">' .. rendered .. '</div></div>' | |||
end | |||
local function compare_recipe_ids(left_rid, right_rid) | |||
local left_recipe = recipe_list[left_rid + 1] | |||
local right_recipe = recipe_list[right_rid + 1] | |||
if not left_recipe or not right_recipe then | |||
return (tonumber(left_rid) or 0) < (tonumber(right_rid) or 0) | |||
end | |||
local left_result_id = common.trim(left_recipe[field_map.result] or '') | |||
local right_result_id = common.trim(right_recipe[field_map.result] or '') | |||
if left_result_id ~= right_result_id then | |||
return item_common.compareItemKeys(left_result_id, right_result_id) | |||
end | |||
local left_machine_id = common.trim(left_recipe[field_map.machine] or '') | |||
local right_machine_id = common.trim(right_recipe[field_map.machine] or '') | |||
if left_machine_id ~= right_machine_id then | |||
return item_common.compareItemKeys(left_machine_id, right_machine_id) | |||
end | |||
local left_time = tonumber(left_recipe[field_map.time]) or 0 | |||
local right_time = tonumber(right_recipe[field_map.time]) or 0 | |||
if left_time ~= right_time then | |||
return left_time < right_time | |||
end | |||
return left_rid < right_rid | |||
end | |||
local function sort_recipe_ids(recipe_ids) | |||
if type(recipe_ids) ~= 'table' or #recipe_ids <= 1 then | |||
return recipe_ids | |||
end | |||
table.sort(recipe_ids, compare_recipe_ids) | |||
return recipe_ids | |||
end | |||
local function split_recipe_groups_by_machine(recipe_ids) | |||
local ordered_groups = {} | |||
local groups_by_machine = {} | |||
for _, rid in ipairs(recipe_ids) do | |||
local recipe = recipe_list[rid + 1] | |||
if recipe then | |||
local machine_id = common.trim(recipe[field_map.machine] or '') | |||
local group = groups_by_machine[machine_id] | |||
if not group then | |||
group = { | |||
machine_id = machine_id, | |||
recipe_ids = {}, | |||
} | |||
groups_by_machine[machine_id] = group | |||
ordered_groups[#ordered_groups + 1] = group | |||
end | |||
group.recipe_ids[#group.recipe_ids + 1] = rid | |||
end | |||
end | |||
local featured_groups = {} | |||
local remaining_ids = {} | |||
for _, group in ipairs(ordered_groups) do | |||
sort_recipe_ids(group.recipe_ids) | |||
if #group.recipe_ids >= 3 then | |||
featured_groups[#featured_groups + 1] = group | |||
else | |||
for _, rid in ipairs(group.recipe_ids) do | |||
remaining_ids[#remaining_ids + 1] = rid | |||
end | |||
end | |||
end | |||
table.sort(featured_groups, function(left_group, right_group) | |||
return item_common.compareItemKeys(left_group.machine_id, right_group.machine_id) | |||
end) | |||
sort_recipe_ids(remaining_ids) | |||
return featured_groups, remaining_ids | |||
end | end | ||
| 第157行: | 第515行: | ||
end | end | ||
local craft = get_record_field(record, 'craft') | local craft = get_record_field(record, key, 'craft') | ||
if field == 'craft_machine' and craft then | if field == 'craft_machine' and craft then | ||
local machine_id = get_craft_field(craft, 'm', 'machine') | local machine_id = get_craft_field(craft, 'm', 'machine') | ||
if machine_id and machine_id ~= '' then | if machine_id and machine_id ~= '' then | ||
return | return render_item_template(frame, machine_id) | ||
end | end | ||
return '' | return '' | ||
| 第171行: | 第529行: | ||
return common.toText(get_craft_field(craft, 'e', 'exp')) | return common.toText(get_craft_field(craft, 'e', 'exp')) | ||
end | end | ||
local upgrade = get_record_field(record, 'upgrade') | |||
local upgrade = get_record_field(record, key, 'upgrade') | |||
if field == 'upgrade_from' and upgrade then | if field == 'upgrade_from' and upgrade then | ||
local from_id = get_craft_field(upgrade, 'f', 'from_machine') | local from_id = get_craft_field(upgrade, 'f', 'from_machine') | ||
if from_id and from_id ~= '' then | if from_id and from_id ~= '' then | ||
return '[[' .. | local from_name = item.getNameByKey(from_id) | ||
if from_name and from_name ~= '' then | |||
return '[[' .. from_name .. ']]' | |||
end | |||
end | end | ||
return '' | return '' | ||
| 第186行: | 第548行: | ||
end | end | ||
if field == 'construction_template_count' then | if field == 'construction_template_count' then | ||
local value = tonumber(get_record_field(record, field)) or 0 | local value = tonumber(get_record_field(record, key, field)) or 0 | ||
if value <= 0 then | if value <= 0 then | ||
return '' | return '' | ||
| 第193行: | 第555行: | ||
end | end | ||
if field == 'energy_capacity' then | if field == 'energy_capacity' then | ||
local value = tonumber(get_record_field(record, field)) or 0 | local value = tonumber(get_record_field(record, key, field)) or 0 | ||
if value <= 0 then | if value <= 0 then | ||
return '' | return '' | ||
end | end | ||
return tostring(value) | return tostring(value) | ||
end | |||
if field == 'prev_machine' then | |||
local prev_id = get_record_field(record, key, 'prev_machine') | |||
if prev_id and prev_id ~= '' then | |||
return render_item_template(frame, prev_id) | |||
end | |||
return '' | |||
end | |||
if field == 'next_machine' then | |||
local next_id = get_record_field(record, key, 'next_machine') | |||
if next_id and next_id ~= '' then | |||
return render_item_template(frame, next_id) | |||
end | |||
return '' | |||
end | end | ||
return common.toText(get_record_field(record, field)) | return common.toText(get_record_field(record, key, field)) | ||
end | end | ||
| 第209行: | 第585行: | ||
return '' | return '' | ||
end | end | ||
local craft = get_record_field(record, 'craft') | |||
local craft = get_record_field(record, key, 'craft') | |||
if not craft then | if not craft then | ||
return '' | return '' | ||
| 第222行: | 第599行: | ||
return '' | return '' | ||
end | end | ||
local upgrade = get_record_field(record, 'upgrade') | |||
local upgrade = get_record_field(record, key, 'upgrade') | |||
if not upgrade then | if not upgrade then | ||
return '' | return '' | ||
| 第237行: | 第615行: | ||
local out = {} | local out = {} | ||
local craft = get_record_field(record, 'craft') | |||
local craft = get_record_field(record, key, 'craft') | |||
local craft_materials = get_craft_field(craft, 'mat', 'materials') | local craft_materials = get_craft_field(craft, 'mat', 'materials') | ||
if craft and not is_empty(craft_materials) then | if craft and not is_empty(craft_materials) then | ||
| 第243行: | 第622行: | ||
local machine_display = '—' | local machine_display = '—' | ||
if machine_id and common.trim(machine_id) ~= '' then | if machine_id and common.trim(machine_id) ~= '' then | ||
machine_display = '[[' .. | local machine_name = item.getNameByKey(machine_id) | ||
if machine_name and machine_name ~= '' then | |||
machine_display = '[[' .. machine_name .. ']]' | |||
end | |||
end | end | ||
out[#out + 1] = render_craft_table( | out[#out + 1] = render_craft_table( | ||
| 第258行: | 第640行: | ||
end | end | ||
local upgrade = get_record_field(record, 'upgrade') | local upgrade = get_record_field(record, key, 'upgrade') | ||
local upgrade_materials = get_craft_field(upgrade, 'mat', 'materials') | local upgrade_materials = get_craft_field(upgrade, 'mat', 'materials') | ||
if upgrade and not is_empty(upgrade_materials) then | if upgrade and not is_empty(upgrade_materials) then | ||
| 第264行: | 第646行: | ||
local from_display = '—' | local from_display = '—' | ||
if from_id and common.trim(from_id) ~= '' then | if from_id and common.trim(from_id) ~= '' then | ||
from_display = '[[' .. | local from_name = item.getNameByKey(from_id) | ||
if from_name and from_name ~= '' then | |||
from_display = '[[' .. from_name .. ']]' | |||
end | |||
end | end | ||
out[#out + 1] = render_craft_table( | out[#out + 1] = render_craft_table( | ||
| 第279行: | 第664行: | ||
end | end | ||
return table.concat(out, '\n') | local css_out = (css.quickCall('Item') or '') .. (css.quickCall('RecipeUsage') or '') | ||
return css_out .. table.concat(out, '\n') | |||
end | |||
function p.recipeList(frame) | |||
local key = common.getArg(frame, 1, '') | |||
local record = find_record(key) | |||
if not record then | |||
return '' | |||
end | |||
local machine_id = get_record_field(record, key, 'id') | |||
if not machine_id or machine_id == '' then | |||
return '' | |||
end | |||
load_recipes() | |||
local recipe_ids = recipe_cache[mw.ustring.lower(machine_id)] | |||
if not recipe_ids then | |||
return '该机器暂无独有配方。' | |||
end | |||
local has_time = false | |||
for _, rid in ipairs(recipe_ids) do | |||
local recipe = recipe_list[rid + 1] | |||
if recipe and recipe_time_text(recipe) ~= '' then | |||
has_time = true | |||
break | |||
end | |||
end | |||
local out = {} | |||
out[#out + 1] = '<div class="recipe-usage-table-wrap">' | |||
out[#out + 1] = '<table class="wikitable recipe-usage-table">' | |||
out[#out + 1] = '<tr><th>产物</th><th>材料</th>' | |||
if has_time then | |||
out[#out + 1] = '<th>时间</th>' | |||
end | |||
out[#out + 1] = '</tr>' | |||
local ordered_recipe_ids = {} | |||
for _, rid in ipairs(recipe_ids) do | |||
ordered_recipe_ids[#ordered_recipe_ids + 1] = rid | |||
end | |||
sort_recipe_ids(ordered_recipe_ids) | |||
for _, rid in ipairs(ordered_recipe_ids) do | |||
local recipe = recipe_list[rid + 1] | |||
if recipe then | |||
out[#out + 1] = '<tr>' | |||
out[#out + 1] = '<td>' .. render_recipe_result(frame, recipe) .. '</td>' | |||
out[#out + 1] = '<td>' .. render_recipe_materials(frame, recipe[field_map.materials]) .. '</td>' | |||
if has_time then | |||
out[#out + 1] = '<td>' .. recipe_time_text(recipe) .. '</td>' | |||
end | |||
out[#out + 1] = '</tr>' | |||
end | |||
end | |||
out[#out + 1] = '</table>' | |||
out[#out + 1] = '</div>' | |||
local css_out = (css.quickCall('Item') or '') .. (css.quickCall('RecipeUsage') or '') | |||
return css_out .. table.concat(out, '\n') | |||
end | |||
local function recipe_ids_have_time(recipe_ids) | |||
for _, rid in ipairs(recipe_ids) do | |||
local recipe = recipe_list[rid + 1] | |||
if recipe and recipe_time_text(recipe) ~= '' then | |||
return true | |||
end | |||
end | |||
return false | |||
end | |||
local function render_product_recipe_table(frame, recipe_ids, include_machine_column) | |||
local ordered_recipe_ids = {} | |||
for _, rid in ipairs(recipe_ids) do | |||
ordered_recipe_ids[#ordered_recipe_ids + 1] = rid | |||
end | |||
sort_recipe_ids(ordered_recipe_ids) | |||
local has_time = recipe_ids_have_time(ordered_recipe_ids) | |||
local out = {} | |||
out[#out + 1] = '<div class="recipe-usage-table-wrap">' | |||
out[#out + 1] = '<table class="wikitable recipe-usage-table">' | |||
out[#out + 1] = '<tr><th>产出</th>' | |||
if include_machine_column then | |||
out[#out + 1] = '<th>机器</th>' | |||
end | |||
out[#out + 1] = '<th>所需材料</th>' | |||
if has_time then | |||
out[#out + 1] = '<th>时间</th>' | |||
end | |||
out[#out + 1] = '</tr>' | |||
for _, rid in ipairs(ordered_recipe_ids) do | |||
local recipe = recipe_list[rid + 1] | |||
if recipe then | |||
out[#out + 1] = '<tr>' | |||
out[#out + 1] = '<td>' .. render_recipe_result(frame, recipe) .. '</td>' | |||
if include_machine_column then | |||
out[#out + 1] = '<td>' .. render_machine_reference(recipe[field_map.machine]) .. '</td>' | |||
end | |||
out[#out + 1] = '<td>' .. render_recipe_materials(frame, recipe[field_map.materials]) .. '</td>' | |||
if has_time then | |||
out[#out + 1] = '<td>' .. recipe_time_text(recipe) .. '</td>' | |||
end | |||
out[#out + 1] = '</tr>' | |||
end | |||
end | |||
out[#out + 1] = '</table>' | |||
out[#out + 1] = '</div>' | |||
return table.concat(out, '') | |||
end | |||
local function render_ingredient_recipe_table(frame, recipe_ids, current_item_id, include_machine_column) | |||
local ordered_recipe_ids = {} | |||
for _, rid in ipairs(recipe_ids) do | |||
ordered_recipe_ids[#ordered_recipe_ids + 1] = rid | |||
end | |||
sort_recipe_ids(ordered_recipe_ids) | |||
local out = {} | |||
out[#out + 1] = '<div class="recipe-usage-table-wrap">' | |||
out[#out + 1] = '<table class="wikitable recipe-usage-table">' | |||
out[#out + 1] = '<tr><th>产物</th>' | |||
if include_machine_column then | |||
out[#out + 1] = '<th>机器</th>' | |||
end | |||
out[#out + 1] = '<th>所需材料</th></tr>' | |||
for _, rid in ipairs(ordered_recipe_ids) do | |||
local recipe = recipe_list[rid + 1] | |||
if recipe then | |||
local counts = material_counts_for_item(recipe[field_map.materials], current_item_id) | |||
if #counts > 0 then | |||
out[#out + 1] = '<tr>' | |||
out[#out + 1] = '<td>' .. render_recipe_result(frame, recipe) .. '</td>' | |||
if include_machine_column then | |||
out[#out + 1] = '<td>' .. render_machine_reference(recipe[field_map.machine]) .. '</td>' | |||
end | |||
out[#out + 1] = '<td>' .. render_recipe_materials(frame, recipe[field_map.materials]) .. '</td>' | |||
out[#out + 1] = '</tr>' | |||
end | |||
end | |||
end | |||
out[#out + 1] = '</table>' | |||
out[#out + 1] = '</div>' | |||
return table.concat(out, '') | |||
end | |||
local function append_recipe_usage_sections(out, frame, recipe_ids, current_item_id, section_title, render_table) | |||
local featured_groups, remaining_ids = split_recipe_groups_by_machine(recipe_ids) | |||
out[#out + 1] = '<div class="recipe-usage-section">' | |||
out[#out + 1] = '<h3 class="recipe-usage-section-title">' .. section_title .. '</h3>' | |||
for _, group in ipairs(featured_groups) do | |||
local heading = machine_group_heading(frame, group.machine_id) | |||
if heading ~= '' then | |||
out[#out + 1] = heading | |||
end | |||
out[#out + 1] = render_table(frame, group.recipe_ids, current_item_id, false) | |||
end | |||
if #remaining_ids > 0 then | |||
if #featured_groups > 0 then | |||
out[#out + 1] = '<h4 class="recipe-usage-subtitle">其他制作站</h4>' | |||
end | |||
out[#out + 1] = render_table(frame, remaining_ids, current_item_id, true) | |||
end | |||
out[#out + 1] = '</div>' | |||
end | |||
function p.itemRecipes(frame) | |||
local item_key = common.getArg(frame, 1, '') | |||
if item_key == '' then | |||
item_key = common.getCurrentTitleText() | |||
end | |||
local item_id = item.getIdByKey(item_key) | |||
if not item_id or item_id == '' then | |||
return '' | |||
end | |||
load_recipes() | |||
local item_data = item_recipe_cache[mw.ustring.lower(item_id)] | |||
if not item_data then | |||
return '' | |||
end | |||
local product_ids = item_data.p or {} | |||
local ingredient_ids = item_data.i or {} | |||
local product_count = count_array(product_ids) | |||
local ingredient_count = count_array(ingredient_ids) | |||
if product_count == 0 and ingredient_count == 0 then | |||
return '' | |||
end | |||
local out = {} | |||
if product_count > 0 then | |||
append_recipe_usage_sections(out, frame, product_ids, item_id, '制作配方', function(current_frame, recipe_ids_for_table, _, include_machine_column) | |||
return render_product_recipe_table(current_frame, recipe_ids_for_table, include_machine_column) | |||
end) | |||
end | |||
if ingredient_count > 0 then | |||
if #out > 0 then | |||
out[#out + 1] = '' | |||
end | |||
append_recipe_usage_sections(out, frame, ingredient_ids, item_id, '用于制作', render_ingredient_recipe_table) | |||
end | |||
local css_out = (css.quickCall('Item') or '') .. (css.quickCall('RecipeUsage') or '') | |||
return css_out .. '<div class="recipe-usage">' .. table.concat(out, '\n') .. '</div>' | |||
end | |||
function p.machineAsIngredient(frame) | |||
local key = common.getArg(frame, 1, '') | |||
local record = find_record(key) | |||
if not record then | |||
return '' | |||
end | |||
local machine_id = get_record_field(record, key, 'id') | |||
if not machine_id or machine_id == '' then | |||
return '' | |||
end | |||
load_recipes() | |||
local item_data = item_recipe_cache[mw.ustring.lower(machine_id)] | |||
if not item_data or not item_data.i or count_array(item_data.i) == 0 then | |||
return '' | |||
end | |||
local ordered_recipe_ids = {} | |||
for _, rid in ipairs(item_data.i) do | |||
ordered_recipe_ids[#ordered_recipe_ids + 1] = rid | |||
end | |||
sort_recipe_ids(ordered_recipe_ids) | |||
local out = {} | |||
out[#out + 1] = '<div class="recipe-usage-table-wrap">' | |||
out[#out + 1] = '<table class="wikitable recipe-usage-table">' | |||
out[#out + 1] = '<tr><th>产物</th><th>所需数量</th></tr>' | |||
for _, rid in ipairs(ordered_recipe_ids) do | |||
local recipe = recipe_list[rid + 1] | |||
if recipe then | |||
local counts = material_counts_for_item(recipe[field_map.materials], machine_id) | |||
if #counts > 0 then | |||
out[#out + 1] = '<tr>' | |||
out[#out + 1] = '<td>' .. render_recipe_result(frame, recipe) .. '</td>' | |||
out[#out + 1] = '<td>' .. render_material_count_list(counts) .. '</td>' | |||
out[#out + 1] = '</tr>' | |||
end | |||
end | |||
end | |||
out[#out + 1] = '</table>' | |||
out[#out + 1] = '</div>' | |||
local css_out = (css.quickCall('Item') or '') .. (css.quickCall('RecipeUsage') or '') | |||
return css_out .. table.concat(out, '\n') | |||
end | end | ||
return p | return p | ||
2026年4月2日 (四) 17:08的最新版本
概述
Machine 提供机器域的字段读取、材料列表、配方信息与反查展示,供 {{Infobox machine}}、{{MachineRecipes}} 与 {{RecipeUsage}} 调用。
用法
{{#invoke:Machine|getField|工作台|type_display}}
{{#invoke:Machine|craftInfo|工作台}}
{{#invoke:Machine|recipeList|工作台}}
{{#invoke:Machine|itemRecipes|木板}}
函数
getField:读取机器字段。ingredientList:渲染建造材料。upgradeIngredientList:渲染升级材料。craftInfo:渲染机器信息块。recipeList:渲染机器产出配方列表。itemRecipes:渲染物品的机器配方反查。machineAsIngredient:渲染机器作为材料的反查结果。
数据来源
local common = require('Module:Common')
local item_common = require('Module:ItemCommon')
local item = require('Module:Item')
local css = require('Module:CSS')
local p = {}
local FIELD_MAP = {
type = 't',
type_display = 'td',
item_level = 'lv',
rarity = 'r',
sell_price = 'sp',
base_value = 'bv',
max_stack = 'ms',
max_quality = 'mq',
tags = 'g',
origin = 'o',
locked_origin = 'lo',
use_description = 'ud',
unlock_title = 'ut',
construct_template = 'ct',
construction_template_count = 'ctc',
energy_capacity = 'ec',
production_formula = 'pf',
production_formula_origin = 'pfo',
craft = 'c',
upgrade = 'u',
prev_machine = 'pm',
next_machine = 'nm',
}
local RECIPE_FIELD_DEFAULTS = {
result = 'r',
result_count = 'rc',
materials = 'm',
time = 't',
machine = 'mc',
cooking_method = 'cm',
}
local data_cache
local mapping_cache
local recipe_cache
local recipe_list
local item_recipe_cache
local field_map = RECIPE_FIELD_DEFAULTS
local function is_empty(value)
if value == nil then
return true
end
for _ in pairs(value) do
return false
end
return true
end
local function count_array(arr)
if type(arr) ~= 'table' then
return 0
end
local count = 0
for _ in pairs(arr) do
count = count + 1
end
return count
end
local function load_data()
if data_cache then
return
end
local raw_data
raw_data, mapping_cache = item_common.loadDomainData('数据:Machine/machine_index.json')
if type(raw_data) == 'table' and type(raw_data.records) == 'table' then
data_cache = raw_data.records
else
data_cache = raw_data or {}
end
end
local function find_record(key)
load_data()
return item_common.findRecord(data_cache, mapping_cache, key)
end
local function get_short_field(field)
return FIELD_MAP[field] or field
end
local function get_record_field(record, key, field)
if type(record) ~= 'table' then
return nil
end
local short_key = get_short_field(field)
if record[short_key] ~= nil then
return record[short_key]
end
if record[field] ~= nil then
return record[field]
end
if field == 'id' or field == 'name' or field == 'name_en' then
return item_common.getIdentityField(record, key, field)
end
return nil
end
local function get_craft_field(craft, short_key, long_key)
if type(craft) ~= 'table' then
return nil
end
if craft[short_key] ~= nil then
return craft[short_key]
end
return craft[long_key]
end
local function format_time(seconds)
seconds = tonumber(seconds) or 0
if seconds <= 0 then
return ''
end
if seconds < 60 then
return tostring(math.floor(seconds)) .. '秒'
end
local minutes = math.floor(seconds / 60)
local remain = math.floor(seconds % 60)
if remain == 0 then
return tostring(minutes) .. '分'
end
return tostring(minutes) .. '分' .. tostring(remain) .. '秒'
end
local function render_materials(frame, materials)
if is_empty(materials) then
return ''
end
local item_ids = {}
for item_id in pairs(materials) do
item_ids[#item_ids + 1] = item_id
end
item_common.sortItemKeys(item_ids)
local parts = {}
for _, item_id in ipairs(item_ids) do
parts[#parts + 1] = frame:expandTemplate{
title = 'Item',
args = { item_id, tostring(materials[item_id]), class = 'block' },
}
end
return table.concat(parts, '')
end
local function render_item_template(frame, item_id)
local resolved_id = common.trim(item_id or '')
if resolved_id == '' then
return ''
end
local css_out = css.quickCall('Item') or ''
return css_out .. item.renderItemWithArgs(frame, { resolved_id })
end
local function page_exists(title_text)
local title = mw.title.new(common.trim(title_text))
return title and title.exists or false
end
local function render_machine_reference(machine_id)
local resolved_id = common.trim(machine_id or '')
if resolved_id == '' then
return ''
end
local machine_name = common.trim(item.getNameByKey(resolved_id) or '')
if machine_name == '' then
machine_name = resolved_id
end
local image_name = resolved_id .. '.png'
local has_icon = common.filePageExists(image_name)
local out = { '<span class="itemtemplate itemtemplateblock recipe-usage-machine-chip">' }
if has_icon then
out[#out + 1] = '<span class="item-icon-container">'
out[#out + 1] = ('[[File:%s|24x24px|link=]]'):format(image_name)
out[#out + 1] = '</span>'
end
out[#out + 1] = '<span class="item-text">'
if page_exists(machine_name) then
out[#out + 1] = ('[[%s|%s]]'):format(machine_name, machine_name)
else
out[#out + 1] = mw.text.encode(machine_name)
end
out[#out + 1] = '</span>'
out[#out + 1] = '</span>'
return table.concat(out)
end
local function render_craft_table(title_text, station_label, station_value, materials_label, materials_value, time_label, time_value, exp_label, exp_value)
local out = {}
out[#out + 1] = '<div class="recipe-usage-table-wrap">'
out[#out + 1] = '<table class="wikitable machine-craft-table recipe-usage-table">'
out[#out + 1] = '<tr><th colspan="2">' .. title_text .. '</th></tr>'
out[#out + 1] = '<tr><th>' .. station_label .. '</th><td>' .. station_value .. '</td></tr>'
out[#out + 1] = '<tr><th>' .. materials_label .. '</th><td>' .. materials_value .. '</td></tr>'
if time_value ~= '' then
out[#out + 1] = '<tr><th>' .. time_label .. '</th><td>' .. time_value .. '</td></tr>'
end
if exp_value ~= '' then
out[#out + 1] = '<tr><th>' .. exp_label .. '</th><td>' .. exp_value .. '</td></tr>'
end
out[#out + 1] = '</table>'
out[#out + 1] = '</div>'
return table.concat(out, '\n')
end
local function is_array_table(value)
if type(value) ~= 'table' then
return false
end
local count = 0
for key in pairs(value) do
if type(key) ~= 'number' then
return false
end
count = count + 1
end
if count == 0 then
return false
end
for index = 1, count do
if value[index] == nil then
return false
end
end
return true
end
local function normalize_material_options(materials)
if type(materials) ~= 'table' then
return {}
end
if is_array_table(materials) then
local options = {}
for _, option in ipairs(materials) do
if type(option) == 'table' and not is_empty(option) then
options[#options + 1] = option
end
end
return options
end
if is_empty(materials) then
return {}
end
return { materials }
end
local function render_material_option(frame, materials)
if type(materials) ~= 'table' or is_empty(materials) then
return ''
end
local item_ids = {}
for item_id in pairs(materials) do
item_ids[#item_ids + 1] = item_id
end
item_common.sortItemKeys(item_ids)
local parts = {}
for _, item_id in ipairs(item_ids) do
parts[#parts + 1] = item.renderItemWithArgs(frame, { item_id, tostring(materials[item_id]), class = 'block' })
end
return table.concat(parts, '')
end
local function filter_material_option(materials, excluded_item_id)
if type(materials) ~= 'table' then
return {}
end
local filtered = {}
for item_id, count in pairs(materials) do
if item_id ~= excluded_item_id then
filtered[item_id] = count
end
end
return filtered
end
local function render_material_option_or_empty(frame, materials)
local rendered = render_material_option(frame, materials)
if rendered == '' then
return '—'
end
return rendered
end
local function render_recipe_materials(frame, materials, excluded_item_id)
local options = normalize_material_options(materials)
if #options == 0 then
return ''
end
if excluded_item_id and excluded_item_id ~= '' then
local filtered_options = {}
local has_non_empty_option = false
for _, option in ipairs(options) do
local filtered = filter_material_option(option, excluded_item_id)
if not is_empty(filtered) then
has_non_empty_option = true
end
filtered_options[#filtered_options + 1] = filtered
end
if not has_non_empty_option then
return '—'
end
options = filtered_options
end
if #options == 1 then
return render_material_option_or_empty(frame, options[1])
end
local out = {}
out[#out + 1] = '<div class="recipe-usage-option-label">可选材料方案</div>'
out[#out + 1] = '<ul class="recipe-usage-option-list">'
for _, option in ipairs(options) do
out[#out + 1] = '<li>' .. render_material_option_or_empty(frame, option) .. '</li>'
end
out[#out + 1] = '</ul>'
return table.concat(out, '')
end
local function material_counts_for_item(materials, item_id)
local counts = {}
local seen = {}
for _, option in ipairs(normalize_material_options(materials)) do
local count = tonumber(option[item_id]) or 0
if count > 0 and not seen[count] then
seen[count] = true
counts[#counts + 1] = count
end
end
table.sort(counts)
return counts
end
local function render_material_count_list(counts)
if #counts == 0 then
return ''
end
if #counts == 1 then
return tostring(counts[1])
end
local out = { '<ul class="recipe-usage-count-list">' }
for _, count in ipairs(counts) do
out[#out + 1] = '<li>' .. tostring(count) .. '</li>'
end
out[#out + 1] = '</ul>'
return table.concat(out, '')
end
local function load_recipes()
if recipe_cache then
return
end
local data = common.loadJsonData('数据:Machine/machine_recipes.json') or {}
recipe_list = data.recipes or {}
recipe_cache = data.by_machine or {}
item_recipe_cache = data.by_item or {}
field_map = (data._meta or {}).field_map or RECIPE_FIELD_DEFAULTS
end
local function recipe_time_text(recipe)
if not recipe then
return ''
end
local value = tonumber(recipe[field_map.time]) or 0
if value <= 0 then
return ''
end
return format_time(value)
end
local function render_recipe_result(frame, recipe)
if not recipe then
return ''
end
local result_id = recipe[field_map.result]
if not result_id or result_id == '' then
return ''
end
local result_count = tonumber(recipe[field_map.result_count]) or 1
if result_count > 1 then
return item.renderItemWithArgs(frame, { result_id, tostring(result_count), class = 'block' })
end
return item.renderItemWithArgs(frame, { result_id, class = 'block' })
end
local function machine_group_heading(frame, machine_id)
local rendered = render_machine_reference(machine_id)
if rendered == '' then
return ''
end
return '<div class="recipe-usage-machine"><div class="recipe-usage-machine-label">' .. rendered .. '</div></div>'
end
local function compare_recipe_ids(left_rid, right_rid)
local left_recipe = recipe_list[left_rid + 1]
local right_recipe = recipe_list[right_rid + 1]
if not left_recipe or not right_recipe then
return (tonumber(left_rid) or 0) < (tonumber(right_rid) or 0)
end
local left_result_id = common.trim(left_recipe[field_map.result] or '')
local right_result_id = common.trim(right_recipe[field_map.result] or '')
if left_result_id ~= right_result_id then
return item_common.compareItemKeys(left_result_id, right_result_id)
end
local left_machine_id = common.trim(left_recipe[field_map.machine] or '')
local right_machine_id = common.trim(right_recipe[field_map.machine] or '')
if left_machine_id ~= right_machine_id then
return item_common.compareItemKeys(left_machine_id, right_machine_id)
end
local left_time = tonumber(left_recipe[field_map.time]) or 0
local right_time = tonumber(right_recipe[field_map.time]) or 0
if left_time ~= right_time then
return left_time < right_time
end
return left_rid < right_rid
end
local function sort_recipe_ids(recipe_ids)
if type(recipe_ids) ~= 'table' or #recipe_ids <= 1 then
return recipe_ids
end
table.sort(recipe_ids, compare_recipe_ids)
return recipe_ids
end
local function split_recipe_groups_by_machine(recipe_ids)
local ordered_groups = {}
local groups_by_machine = {}
for _, rid in ipairs(recipe_ids) do
local recipe = recipe_list[rid + 1]
if recipe then
local machine_id = common.trim(recipe[field_map.machine] or '')
local group = groups_by_machine[machine_id]
if not group then
group = {
machine_id = machine_id,
recipe_ids = {},
}
groups_by_machine[machine_id] = group
ordered_groups[#ordered_groups + 1] = group
end
group.recipe_ids[#group.recipe_ids + 1] = rid
end
end
local featured_groups = {}
local remaining_ids = {}
for _, group in ipairs(ordered_groups) do
sort_recipe_ids(group.recipe_ids)
if #group.recipe_ids >= 3 then
featured_groups[#featured_groups + 1] = group
else
for _, rid in ipairs(group.recipe_ids) do
remaining_ids[#remaining_ids + 1] = rid
end
end
end
table.sort(featured_groups, function(left_group, right_group)
return item_common.compareItemKeys(left_group.machine_id, right_group.machine_id)
end)
sort_recipe_ids(remaining_ids)
return featured_groups, remaining_ids
end
function p.getField(frame)
local key = common.getArg(frame, 1, '')
local field = common.getArg(frame, 2, '')
if field == '' then
return ''
end
local record = find_record(key)
if not record then
return ''
end
local craft = get_record_field(record, key, 'craft')
if field == 'craft_machine' and craft then
local machine_id = get_craft_field(craft, 'm', 'machine')
if machine_id and machine_id ~= '' then
return render_item_template(frame, machine_id)
end
return ''
end
if field == 'craft_time' and craft then
return format_time(get_craft_field(craft, 't', 'time'))
end
if field == 'craft_exp' and craft then
return common.toText(get_craft_field(craft, 'e', 'exp'))
end
local upgrade = get_record_field(record, key, 'upgrade')
if field == 'upgrade_from' and upgrade then
local from_id = get_craft_field(upgrade, 'f', 'from_machine')
if from_id and from_id ~= '' then
local from_name = item.getNameByKey(from_id)
if from_name and from_name ~= '' then
return '[[' .. from_name .. ']]'
end
end
return ''
end
if field == 'upgrade_time' and upgrade then
return format_time(get_craft_field(upgrade, 't', 'time'))
end
if field == 'upgrade_exp' and upgrade then
return common.toText(get_craft_field(upgrade, 'e', 'exp'))
end
if field == 'construction_template_count' then
local value = tonumber(get_record_field(record, key, field)) or 0
if value <= 0 then
return ''
end
return tostring(value)
end
if field == 'energy_capacity' then
local value = tonumber(get_record_field(record, key, field)) or 0
if value <= 0 then
return ''
end
return tostring(value)
end
if field == 'prev_machine' then
local prev_id = get_record_field(record, key, 'prev_machine')
if prev_id and prev_id ~= '' then
return render_item_template(frame, prev_id)
end
return ''
end
if field == 'next_machine' then
local next_id = get_record_field(record, key, 'next_machine')
if next_id and next_id ~= '' then
return render_item_template(frame, next_id)
end
return ''
end
return common.toText(get_record_field(record, key, field))
end
function p.ingredientList(frame)
local key = common.getArg(frame, 1, '')
local record = find_record(key)
if not record then
return ''
end
local craft = get_record_field(record, key, 'craft')
if not craft then
return ''
end
return render_materials(frame, get_craft_field(craft, 'mat', 'materials'))
end
function p.upgradeIngredientList(frame)
local key = common.getArg(frame, 1, '')
local record = find_record(key)
if not record then
return ''
end
local upgrade = get_record_field(record, key, 'upgrade')
if not upgrade then
return ''
end
return render_materials(frame, get_craft_field(upgrade, 'mat', 'materials'))
end
function p.craftInfo(frame)
local key = common.getArg(frame, 1, '')
local record = find_record(key)
if not record then
return ''
end
local out = {}
local craft = get_record_field(record, key, 'craft')
local craft_materials = get_craft_field(craft, 'mat', 'materials')
if craft and not is_empty(craft_materials) then
local machine_id = get_craft_field(craft, 'm', 'machine')
local machine_display = '—'
if machine_id and common.trim(machine_id) ~= '' then
local machine_name = item.getNameByKey(machine_id)
if machine_name and machine_name ~= '' then
machine_display = '[[' .. machine_name .. ']]'
end
end
out[#out + 1] = render_craft_table(
'制造配方',
'制造站',
machine_display,
'材料',
render_materials(frame, craft_materials),
'时间',
format_time(get_craft_field(craft, 't', 'time')),
'经验',
common.toText(get_craft_field(craft, 'e', 'exp'))
)
end
local upgrade = get_record_field(record, key, 'upgrade')
local upgrade_materials = get_craft_field(upgrade, 'mat', 'materials')
if upgrade and not is_empty(upgrade_materials) then
local from_id = get_craft_field(upgrade, 'f', 'from_machine')
local from_display = '—'
if from_id and common.trim(from_id) ~= '' then
local from_name = item.getNameByKey(from_id)
if from_name and from_name ~= '' then
from_display = '[[' .. from_name .. ']]'
end
end
out[#out + 1] = render_craft_table(
'升级配方',
'升级自',
from_display,
'材料',
render_materials(frame, upgrade_materials),
'时间',
format_time(get_craft_field(upgrade, 't', 'time')),
'经验',
common.toText(get_craft_field(upgrade, 'e', 'exp'))
)
end
local css_out = (css.quickCall('Item') or '') .. (css.quickCall('RecipeUsage') or '')
return css_out .. table.concat(out, '\n')
end
function p.recipeList(frame)
local key = common.getArg(frame, 1, '')
local record = find_record(key)
if not record then
return ''
end
local machine_id = get_record_field(record, key, 'id')
if not machine_id or machine_id == '' then
return ''
end
load_recipes()
local recipe_ids = recipe_cache[mw.ustring.lower(machine_id)]
if not recipe_ids then
return '该机器暂无独有配方。'
end
local has_time = false
for _, rid in ipairs(recipe_ids) do
local recipe = recipe_list[rid + 1]
if recipe and recipe_time_text(recipe) ~= '' then
has_time = true
break
end
end
local out = {}
out[#out + 1] = '<div class="recipe-usage-table-wrap">'
out[#out + 1] = '<table class="wikitable recipe-usage-table">'
out[#out + 1] = '<tr><th>产物</th><th>材料</th>'
if has_time then
out[#out + 1] = '<th>时间</th>'
end
out[#out + 1] = '</tr>'
local ordered_recipe_ids = {}
for _, rid in ipairs(recipe_ids) do
ordered_recipe_ids[#ordered_recipe_ids + 1] = rid
end
sort_recipe_ids(ordered_recipe_ids)
for _, rid in ipairs(ordered_recipe_ids) do
local recipe = recipe_list[rid + 1]
if recipe then
out[#out + 1] = '<tr>'
out[#out + 1] = '<td>' .. render_recipe_result(frame, recipe) .. '</td>'
out[#out + 1] = '<td>' .. render_recipe_materials(frame, recipe[field_map.materials]) .. '</td>'
if has_time then
out[#out + 1] = '<td>' .. recipe_time_text(recipe) .. '</td>'
end
out[#out + 1] = '</tr>'
end
end
out[#out + 1] = '</table>'
out[#out + 1] = '</div>'
local css_out = (css.quickCall('Item') or '') .. (css.quickCall('RecipeUsage') or '')
return css_out .. table.concat(out, '\n')
end
local function recipe_ids_have_time(recipe_ids)
for _, rid in ipairs(recipe_ids) do
local recipe = recipe_list[rid + 1]
if recipe and recipe_time_text(recipe) ~= '' then
return true
end
end
return false
end
local function render_product_recipe_table(frame, recipe_ids, include_machine_column)
local ordered_recipe_ids = {}
for _, rid in ipairs(recipe_ids) do
ordered_recipe_ids[#ordered_recipe_ids + 1] = rid
end
sort_recipe_ids(ordered_recipe_ids)
local has_time = recipe_ids_have_time(ordered_recipe_ids)
local out = {}
out[#out + 1] = '<div class="recipe-usage-table-wrap">'
out[#out + 1] = '<table class="wikitable recipe-usage-table">'
out[#out + 1] = '<tr><th>产出</th>'
if include_machine_column then
out[#out + 1] = '<th>机器</th>'
end
out[#out + 1] = '<th>所需材料</th>'
if has_time then
out[#out + 1] = '<th>时间</th>'
end
out[#out + 1] = '</tr>'
for _, rid in ipairs(ordered_recipe_ids) do
local recipe = recipe_list[rid + 1]
if recipe then
out[#out + 1] = '<tr>'
out[#out + 1] = '<td>' .. render_recipe_result(frame, recipe) .. '</td>'
if include_machine_column then
out[#out + 1] = '<td>' .. render_machine_reference(recipe[field_map.machine]) .. '</td>'
end
out[#out + 1] = '<td>' .. render_recipe_materials(frame, recipe[field_map.materials]) .. '</td>'
if has_time then
out[#out + 1] = '<td>' .. recipe_time_text(recipe) .. '</td>'
end
out[#out + 1] = '</tr>'
end
end
out[#out + 1] = '</table>'
out[#out + 1] = '</div>'
return table.concat(out, '')
end
local function render_ingredient_recipe_table(frame, recipe_ids, current_item_id, include_machine_column)
local ordered_recipe_ids = {}
for _, rid in ipairs(recipe_ids) do
ordered_recipe_ids[#ordered_recipe_ids + 1] = rid
end
sort_recipe_ids(ordered_recipe_ids)
local out = {}
out[#out + 1] = '<div class="recipe-usage-table-wrap">'
out[#out + 1] = '<table class="wikitable recipe-usage-table">'
out[#out + 1] = '<tr><th>产物</th>'
if include_machine_column then
out[#out + 1] = '<th>机器</th>'
end
out[#out + 1] = '<th>所需材料</th></tr>'
for _, rid in ipairs(ordered_recipe_ids) do
local recipe = recipe_list[rid + 1]
if recipe then
local counts = material_counts_for_item(recipe[field_map.materials], current_item_id)
if #counts > 0 then
out[#out + 1] = '<tr>'
out[#out + 1] = '<td>' .. render_recipe_result(frame, recipe) .. '</td>'
if include_machine_column then
out[#out + 1] = '<td>' .. render_machine_reference(recipe[field_map.machine]) .. '</td>'
end
out[#out + 1] = '<td>' .. render_recipe_materials(frame, recipe[field_map.materials]) .. '</td>'
out[#out + 1] = '</tr>'
end
end
end
out[#out + 1] = '</table>'
out[#out + 1] = '</div>'
return table.concat(out, '')
end
local function append_recipe_usage_sections(out, frame, recipe_ids, current_item_id, section_title, render_table)
local featured_groups, remaining_ids = split_recipe_groups_by_machine(recipe_ids)
out[#out + 1] = '<div class="recipe-usage-section">'
out[#out + 1] = '<h3 class="recipe-usage-section-title">' .. section_title .. '</h3>'
for _, group in ipairs(featured_groups) do
local heading = machine_group_heading(frame, group.machine_id)
if heading ~= '' then
out[#out + 1] = heading
end
out[#out + 1] = render_table(frame, group.recipe_ids, current_item_id, false)
end
if #remaining_ids > 0 then
if #featured_groups > 0 then
out[#out + 1] = '<h4 class="recipe-usage-subtitle">其他制作站</h4>'
end
out[#out + 1] = render_table(frame, remaining_ids, current_item_id, true)
end
out[#out + 1] = '</div>'
end
function p.itemRecipes(frame)
local item_key = common.getArg(frame, 1, '')
if item_key == '' then
item_key = common.getCurrentTitleText()
end
local item_id = item.getIdByKey(item_key)
if not item_id or item_id == '' then
return ''
end
load_recipes()
local item_data = item_recipe_cache[mw.ustring.lower(item_id)]
if not item_data then
return ''
end
local product_ids = item_data.p or {}
local ingredient_ids = item_data.i or {}
local product_count = count_array(product_ids)
local ingredient_count = count_array(ingredient_ids)
if product_count == 0 and ingredient_count == 0 then
return ''
end
local out = {}
if product_count > 0 then
append_recipe_usage_sections(out, frame, product_ids, item_id, '制作配方', function(current_frame, recipe_ids_for_table, _, include_machine_column)
return render_product_recipe_table(current_frame, recipe_ids_for_table, include_machine_column)
end)
end
if ingredient_count > 0 then
if #out > 0 then
out[#out + 1] = ''
end
append_recipe_usage_sections(out, frame, ingredient_ids, item_id, '用于制作', render_ingredient_recipe_table)
end
local css_out = (css.quickCall('Item') or '') .. (css.quickCall('RecipeUsage') or '')
return css_out .. '<div class="recipe-usage">' .. table.concat(out, '\n') .. '</div>'
end
function p.machineAsIngredient(frame)
local key = common.getArg(frame, 1, '')
local record = find_record(key)
if not record then
return ''
end
local machine_id = get_record_field(record, key, 'id')
if not machine_id or machine_id == '' then
return ''
end
load_recipes()
local item_data = item_recipe_cache[mw.ustring.lower(machine_id)]
if not item_data or not item_data.i or count_array(item_data.i) == 0 then
return ''
end
local ordered_recipe_ids = {}
for _, rid in ipairs(item_data.i) do
ordered_recipe_ids[#ordered_recipe_ids + 1] = rid
end
sort_recipe_ids(ordered_recipe_ids)
local out = {}
out[#out + 1] = '<div class="recipe-usage-table-wrap">'
out[#out + 1] = '<table class="wikitable recipe-usage-table">'
out[#out + 1] = '<tr><th>产物</th><th>所需数量</th></tr>'
for _, rid in ipairs(ordered_recipe_ids) do
local recipe = recipe_list[rid + 1]
if recipe then
local counts = material_counts_for_item(recipe[field_map.materials], machine_id)
if #counts > 0 then
out[#out + 1] = '<tr>'
out[#out + 1] = '<td>' .. render_recipe_result(frame, recipe) .. '</td>'
out[#out + 1] = '<td>' .. render_material_count_list(counts) .. '</td>'
out[#out + 1] = '</tr>'
end
end
end
out[#out + 1] = '</table>'
out[#out + 1] = '</div>'
local css_out = (css.quickCall('Item') or '') .. (css.quickCall('RecipeUsage') or '')
return css_out .. table.concat(out, '\n')
end
return p