打开/关闭搜索
搜索
打开/关闭菜单
1K
5.2K
4
8.2K
星砂岛百科
导航
首页
最近更改
随机页面
MediaWiki帮助
特殊页面
上传文件
打开/关闭外观设置菜单
通知
打开/关闭个人菜单
未登录
未登录用户的IP地址会在进行任意编辑后公开展示。
user-interface-preferences
个人工具
创建账号
登录
本站正在进行早期测试,目前仍存在许多内容的缺失。
查看“︁模块:Gifting”︁的源代码
来自星砂岛百科
查看
阅读
查看源代码
查看历史
associated-pages
模块
讨论
更多操作
←
模块:Gifting
因为以下原因,您没有权限编辑该页面:
您请求的操作仅限属于该用户组的用户执行:已验证邮箱用户
您没有权限编辑
模块
命名空间内的页面。
您必须确认您的电子邮件地址才能编辑页面。请通过
参数设置
设置并确认您的电子邮件地址。
您可以查看和复制此页面的源代码。
local common = require('Module:Common') local css = require('Module:CSS') local item_module = require('Module:Item') local npc_module = require('Module:NPC') local p = {} local character_cache local character_records local character_ids local character_mapping local global_rules local tag_items local item_cache local item_by_index local item_mapping local item_redirects local id_registry local registry_character_ids local registry_item_ids local registry_character_index_by_id local registry_item_index_by_id local summary_cache local LUACACHE_NAMESPACE = 'gifting:npc_tabs' local LUACACHE_VERSION = '2026-04-02-version-2' local level_to_bucket = { resonance = 'resonance', special = 'special', loved = 'loved', liked = 'liked', neutral = 'neutral', disliked = 'disliked', hated = 'hated', r = 'resonance', s = 'special', v = 'loved', k = 'liked', n = 'neutral', d = 'disliked', h = 'hated', } local bucket_to_short = { resonance = 'r', special = 's', loved = 'v', liked = 'k', neutral = 'n', disliked = 'd', hated = 'h', } local function normalize_key(value) return common.normalizeKey(value) end local function is_hidden_item_name(name) local text = common.trim(name) return text ~= '' and mw.ustring.sub(text, 1, 1) == '∫' end local function resolve_level_bucket(level) if level == nil then return nil end if type(level) == 'string' then local normalized = normalize_key(level) return level_to_bucket[normalized] end local numeric = tonumber(level) if numeric == 4 then return 'resonance' end if numeric == 3 then return 'special' end if numeric == 2 then return 'loved' end if numeric == 1 then return 'liked' end if numeric == 0 then return 'neutral' end if numeric == -1 then return 'disliked' end if numeric == -2 then return 'hated' end return nil end local function empty_summary() return { resonance = { personal = {}, global = {} }, special = { personal = {}, global = {} }, loved = { personal = {}, global = {} }, liked = { personal = {}, global = {} }, neutral = { personal = {}, global = {} }, disliked = { personal = {}, global = {} }, hated = { personal = {}, global = {} }, } end local function load_id_registry() if id_registry then return end id_registry = common.loadJsonData('数据:Gifting/gift_id_registry.json') or {} registry_character_ids = id_registry.characters or {} registry_item_ids = id_registry.items or {} registry_character_index_by_id = {} for idx, value in ipairs(registry_character_ids) do local key = normalize_key(value) if key ~= '' then registry_character_index_by_id[key] = idx - 1 end end registry_item_index_by_id = {} for idx, value in ipairs(registry_item_ids) do local key = normalize_key(value) if key ~= '' then registry_item_index_by_id[key] = idx - 1 end end end local function item_id_from_index(index) load_id_registry() local n = tonumber(index) if not n then return '' end return common.trim(registry_item_ids[n + 1] or '') end local function character_id_from_index(index) load_id_registry() local n = tonumber(index) if not n then return '' end return common.trim(registry_character_ids[n + 1] or '') end local function load_character_data() if character_cache then return end character_cache = common.loadJsonData('数据:Gifting/gift_preferences.json') or {} character_records = character_cache.characters or {} global_rules = character_cache.global or {} tag_items = character_cache.tag_items or {} load_id_registry() character_ids = {} for idx, record in ipairs(character_records) do local cid = character_id_from_index(idx - 1) if cid ~= '' then character_ids[normalize_key(cid)] = idx end end character_mapping = common.loadJsonData('数据:Gifting/gifting_mapping.json') or { name_to_id = {}, id_to_name = {}, aliases = {}, overrides = { name_to_id = {}, aliases = {}, }, } end local function load_item_data() if item_cache then return end item_cache = common.loadJsonData('数据:Gifting/gift_preferences_by_item.json') or {} item_by_index = item_cache.items or {} item_redirects = item_cache.item_redirects or {} item_mapping = common.loadJsonData('数据:Item/item_mapping.json') or { name_to_id = {}, id_to_name = {}, aliases = {}, overrides = { name_to_id = {}, aliases = {}, }, } end local function find_character_record(key) load_character_data() local resolved = common.trim(key) if resolved == '' then resolved = common.getCurrentTitleText() end local normalized = normalize_key(resolved) local idx = character_ids[normalized] if not idx then local override_id = character_mapping.overrides and character_mapping.overrides.name_to_id and character_mapping.overrides.name_to_id[normalized] if override_id then idx = character_ids[normalize_key(override_id)] end end if not idx then local override_alias = character_mapping.overrides and character_mapping.overrides.aliases and character_mapping.overrides.aliases[normalized] if override_alias then idx = character_ids[normalize_key(override_alias)] end end if not idx then local mapped_id = character_mapping.name_to_id and character_mapping.name_to_id[normalized] if mapped_id then idx = character_ids[normalize_key(mapped_id)] end end if not idx then local alias_id = character_mapping.aliases and character_mapping.aliases[normalized] if alias_id then idx = character_ids[normalize_key(alias_id)] end end if not idx then return nil end return character_records[idx], idx - 1 end local function resolve_item_index(index) local key = tostring(index) local num_key = tonumber(index) if item_by_index[key] then return key end if num_key and item_by_index[num_key] then return num_key end local redirected = item_redirects and item_redirects[key] if not redirected and num_key then redirected = item_redirects[num_key] end if redirected then local redirected_key = tostring(redirected) local redirected_num = tonumber(redirected) if item_by_index[redirected_key] then return redirected_key end if redirected_num and item_by_index[redirected_num] then return redirected_num end end return nil end local function find_item_record(key) load_item_data() load_id_registry() local resolved = common.trim(key) if resolved == '' then resolved = common.getCurrentTitleText() end local normalized = normalize_key(resolved) local item_id = '' local item_index = registry_item_index_by_id[normalized] if item_index ~= nil then item_id = registry_item_ids[item_index + 1] or '' end if item_id == '' then local override_id = item_mapping.overrides and item_mapping.overrides.name_to_id and item_mapping.overrides.name_to_id[normalized] if override_id and override_id ~= '' then item_id = override_id end end if item_id == '' then local override_alias = item_mapping.overrides and item_mapping.overrides.aliases and item_mapping.overrides.aliases[normalized] if override_alias and override_alias ~= '' then item_id = override_alias end end if item_id == '' then local mapped_id = item_mapping.name_to_id and item_mapping.name_to_id[normalized] if mapped_id and mapped_id ~= '' then item_id = mapped_id end end if item_id == '' then local alias_id = item_mapping.aliases and item_mapping.aliases[normalized] if alias_id and alias_id ~= '' then item_id = alias_id end end if item_id == '' then return nil end if item_index == nil then item_index = registry_item_index_by_id[normalize_key(item_id)] end if item_index == nil then return nil end local resolved_index = resolve_item_index(item_index) if not resolved_index then return nil end return item_by_index[resolved_index] end local function append_unique_indexes(values, seen, target) if type(values) ~= 'table' then return end for _, value in ipairs(values) do local index = tonumber(value) if index ~= nil and not seen[index] then seen[index] = true target[#target + 1] = index end end end local function expand_rule_items(rule) local result = {} local seen = {} for _, tag in ipairs(rule.t or {}) do append_unique_indexes(tag_items[tag], seen, result) end append_unique_indexes(rule.s, seen, result) return result end local function build_character_summary(record, record_index) if record_index ~= nil then summary_cache = summary_cache or {} if summary_cache[record_index] then return summary_cache[record_index] end end local summary = empty_summary() local global_seen = { resonance = {}, special = {}, loved = {}, liked = {}, neutral = {}, disliked = {}, hated = {}, } local personal_seen = { resonance = {}, special = {}, loved = {}, liked = {}, neutral = {}, disliked = {}, hated = {}, } local all_personal_items = {} for _, rule in ipairs(record.p or {}) do for _, item_idx in ipairs(expand_rule_items(rule)) do all_personal_items[item_idx] = true end end for _, rule in ipairs(global_rules) do local bucket = resolve_level_bucket(rule.l) if bucket and summary[bucket] then for _, item_idx in ipairs(expand_rule_items(rule)) do if not all_personal_items[item_idx] and not global_seen[bucket][item_idx] then global_seen[bucket][item_idx] = true summary[bucket].global[#summary[bucket].global + 1] = item_idx end end end end for _, rule in ipairs(record.p or {}) do local bucket = resolve_level_bucket(rule.l) if bucket and summary[bucket] then for _, item_idx in ipairs(expand_rule_items(rule)) do if not personal_seen[bucket][item_idx] then personal_seen[bucket][item_idx] = true summary[bucket].personal[#summary[bucket].personal + 1] = item_idx end end end end if record_index ~= nil then summary_cache[record_index] = summary end return summary end local function get_summary_bucket(record, bucket, record_index) if type(record) ~= 'table' then return nil end local resolved_bucket = level_to_bucket[bucket] or normalize_key(bucket) local summary = build_character_summary(record, record_index) return summary[resolved_bucket] end local function item_label_from_index(item_index) load_item_data() local item_id = item_id_from_index(item_index) if item_id == '' then return '' end local label = common.trim(item_mapping.id_to_name and item_mapping.id_to_name[normalize_key(item_id)] or '') if is_hidden_item_name(label) then return '' end return label end local function item_display_meta(item_index) local item_id = item_id_from_index(item_index) if item_id == '' then return nil end local name = item_label_from_index(item_index) if name == '' then return nil end local file_name = item_id if file_name == '' then file_name = name end return { id = item_id, name = name, image = file_name .. '.png', } end local function item_id_display_fallback(item_index) if item_label_from_index(item_index) == '' then return '' end return item_id_from_index(item_index) end local character_index_cache local function load_character_index() if character_index_cache then return end character_index_cache = common.loadJsonData('数据:Character/character_index.json') or {} end local function is_creature(name) load_character_index() local normalized = normalize_key(name) local record = character_index_cache[normalized] if record and record.entity_kind == 'creature' then return true end return false end local function item_template_from_meta(frame, meta) if type(meta) ~= 'table' then return '' end local item_key = common.trim(meta.id or meta.name or '') if item_key == '' then return '' end local file_name = common.trim(meta.image or '') if file_name ~= '' and not common.filePageExists(file_name) then return '' end local output = item_module.renderItemWithArgs(frame, { item_key, class = 'block', }) return common.trim(output) ~= '' and output or '' end local function character_label_from_index(character_index) load_character_data() local character_id = character_id_from_index(character_index) if character_id == '' then return '' end return common.trim(character_mapping.id_to_name and character_mapping.id_to_name[normalize_key(character_id)] or '') end local function join_indexes(values, label_resolver, fallback_resolver) if type(values) ~= 'table' or #values == 0 then return '' end local parts = {} for _, value in ipairs(values) do local idx = tonumber(value) if idx ~= nil then local label = label_resolver and label_resolver(idx) or '' if label ~= '' then parts[#parts + 1] = ('[[%s]]'):format(label) else local fallback = fallback_resolver and fallback_resolver(idx) or '' if fallback ~= '' then parts[#parts + 1] = fallback end end end end return table.concat(parts, '、') end local function render_bucket(bucket_data, personal_label, global_label, label_resolver, fallback_resolver) if type(bucket_data) ~= 'table' then return '' end local parts = {} local personal = join_indexes(bucket_data.personal, label_resolver, fallback_resolver) local global = join_indexes(bucket_data.global, label_resolver, fallback_resolver) if personal ~= '' then parts[#parts + 1] = personal_label .. ':' .. personal end if global ~= '' then parts[#parts + 1] = global_label .. ':' .. global end return table.concat(parts, '<br />') end local function render_item_grid(frame, values) local items = {} local seen = {} if type(values) == 'table' then for _, value in ipairs(values) do local idx = tonumber(value) if idx ~= nil then local meta = item_display_meta(idx) if meta then local dedupe_key = normalize_key(meta.id or meta.name) .. '|' .. (meta.image or 'default') if not seen[dedupe_key] then seen[dedupe_key] = true local item_markup = item_template_from_meta(frame, meta) if item_markup and item_markup ~= '' then items[#items + 1] = item_markup end end end end end end if #items == 0 then return '<div class="gifting-empty">暂无可显示物品</div>' end local root = mw.html.create('div'):addClass('gifting-item-grid') for _, item_markup in ipairs(items) do root:tag('div') :addClass('gifting-item-entry') :wikitext(item_markup) end return tostring(root) end local function render_preference_panel(frame, title, values) local panel = mw.html.create('div'):addClass('gifting-preference-panel') panel:tag('div'):addClass('gifting-preference-panel-title'):wikitext(title) panel:tag('div'):addClass('gifting-preference-panel-body'):wikitext(render_item_grid(frame, values)) return tostring(panel) end local function render_npc_level(frame, record, record_index, bucket) local bucket_data = get_summary_bucket(record, bucket, record_index) if type(bucket_data) ~= 'table' then return '<div class="gifting-level-grid"><div class="gifting-empty">暂无数据</div></div>' end local root = mw.html.create('div') :addClass('gifting-level-grid') :addClass('gifting-level-grid--' .. bucket) root:wikitext(render_preference_panel(frame, '个人喜好', bucket_data.personal or {})) root:wikitext(render_preference_panel(frame, '通用喜好', bucket_data.global or {})) return tostring(root) end local function build_tabber_content(frame, record, record_index) local tabs = { { key = 'loved', label = '最爱' }, { key = 'liked', label = '喜欢' }, { key = 'neutral', label = '一般' }, { key = 'disliked', label = '不喜欢' }, { key = 'hated', label = '讨厌' }, { key = 'resonance', label = '共鸣' }, { key = 'special', label = '特殊' }, } local parts = {} for index, tab in ipairs(tabs) do parts[#parts + 1] = tab.label .. '=' .. render_npc_level(frame, record, record_index, tab.key) if index < #tabs then parts[#parts + 1] = '|-|' end end return table.concat(parts, '\n') end function p.getField(frame) local key = common.getArg(frame, 1, '') local field = common.getArg(frame, 2, '') if field == '' then return '' end local record, character_index = find_character_record(key) if not record then return '' end if field == 'id' then return character_id_from_index(character_index) end if field == 'name' then return common.toText(record.n) end if field == 'name_en' then return common.toText(record.en) end if field == 'character_type' then return common.toText(record.t) end if field == 'personal_rules' then return common.toText(record.p) end return common.toText(record[field]) end function p.preferenceList(frame) local key = common.getArg(frame, 1, '') local field = common.getArg(frame, 2, '') if field == '' then return '' end local record = find_character_record(key) if not record then return '' end return render_bucket(get_summary_bucket(record, field), '个人喜好', '通用喜好', item_label_from_index, item_id_display_fallback) end function p.renderNpcTabs(frame) local key = common.getArg(frame, 1, '') local resolved = common.trim(key) if resolved == '' then resolved = common.getCurrentTitleText() end local cache_key = common.buildLuaCacheKey(LUACACHE_NAMESPACE, LUACACHE_VERSION, normalize_key(resolved)) local content = common.luaCacheGet(cache_key) if content == nil then local record, record_index = find_character_record(resolved) if not record then common.luaCacheSet(cache_key, '') content = '' else content = build_tabber_content(frame, record, record_index) common.luaCacheSet(cache_key, content) end end local css_out = (css.quickCall('GiftsByNPC') or '') .. (css.quickCall('Item') or '') if content == '' then return css_out .. '<div class="gifting-empty">未找到送礼数据</div>' end return css_out .. frame:extensionTag('tabber', content, { class = 'tabber-no-active-indicator gifting-tabber' }) end local function render_character_list(frame, values) if type(values) ~= 'table' then return '' end local parts = {} for _, value in ipairs(values) do local idx = tonumber(value) if idx ~= nil then local name = character_label_from_index(idx) if name ~= '' then if is_creature(name) then parts[#parts + 1] = '[[' .. name .. ']]' else local output = npc_module.renderNPCByKey(name) if common.trim(output) ~= '' then parts[#parts + 1] = output end end end end end return table.concat(parts, '、') end function p.preferenceListByItem(frame) local key = common.getArg(frame, 1, '') local field = common.getArg(frame, 2, '') if field == '' then return '' end local record = find_item_record(key) if not record then return '' end local bucket = level_to_bucket[field] or normalize_key(field) local short = bucket_to_short[bucket] local compact_bucket = record.s and short and record.s[short] or nil if type(compact_bucket) ~= 'table' then return '' end local parts = {} local personal = render_character_list(frame, compact_bucket.p or {}) local global = render_character_list(frame, compact_bucket.g or {}) if personal ~= '' then parts[#parts + 1] = '个人喜好角色:' .. personal end if global ~= '' then parts[#parts + 1] = '通用喜好角色:' .. global end return table.concat(parts, '<br />') end local BLOCKED_CHARACTERS = { '程洲', '弦一', '貂', '熊猫', '鹦鹉' } local PET_ANIMALS = { '水豚', '猫', '狗', '狐狸', '小熊猫', '松鼠' } local function is_blocked_character(name) for _, blocked in ipairs(BLOCKED_CHARACTERS) do if name == blocked then return true end end return false end local function is_pet_animal(name) for _, pet in ipairs(PET_ANIMALS) do if name == pet then return true end end return false end local function render_character_list_filtered(frame, values) if type(values) ~= 'table' then return '' end local npc_parts = {} local pet_parts = {} local seen = {} for _, value in ipairs(values) do local idx = tonumber(value) if idx ~= nil then local name = character_label_from_index(idx) if name ~= '' and not is_blocked_character(name) and not seen[name] then seen[name] = true if is_pet_animal(name) then pet_parts[#pet_parts + 1] = '<span class="npctemplate">' .. name .. '</span>' else local output = npc_module.renderNPCByKey(name) if common.trim(output) ~= '' then npc_parts[#npc_parts + 1] = output end end end end end local all_parts = {} for _, part in ipairs(npc_parts) do all_parts[#all_parts + 1] = part end for _, part in ipairs(pet_parts) do all_parts[#all_parts + 1] = part end return table.concat(all_parts, ' · ') end function p.renderGiftsByItem(frame) local key = common.getArg(frame, 1, '') if key == '' then key = common.getCurrentTitleText() end local css_out = css.quickCall('NPC') or '' local record = find_item_record(key) if not record or type(record.s) ~= 'table' then return css_out .. "'''" .. key .. "'''不可用于送礼。" end local buckets = { { key = 'loved', short = 'v', label = '最爱', icon = 'Gifting love.png' }, { key = 'liked', short = 'k', label = '喜欢', icon = 'Gifting like.png' }, { key = 'neutral', short = 'n', label = '一般', icon = '' }, { key = 'disliked', short = 'd', label = '不喜欢', icon = '' }, { key = 'hated', short = 'h', label = '讨厌', icon = '' }, { key = 'resonance', short = 'r', label = '共鸣', icon = '' }, { key = 'special', short = 's', label = '特殊', icon = '' }, } local rows = {} for _, bucket in ipairs(buckets) do local compact_bucket = record.s[bucket.short] if type(compact_bucket) == 'table' then local personal = render_character_list_filtered(frame, compact_bucket.p or {}) local global = render_character_list_filtered(frame, compact_bucket.g or {}) local all_chars = {} if personal ~= '' then all_chars[#all_chars + 1] = personal end if global ~= '' then all_chars[#all_chars + 1] = global end if #all_chars > 0 then local label_html = bucket.label if bucket.icon ~= '' then label_html = '[[File:' .. bucket.icon .. '|16px|link=]] ' .. bucket.label end rows[#rows + 1] = { label = label_html, content = table.concat(all_chars, ' • ') } end end end if #rows == 0 then return css_out .. "'''" .. key .. "'''不可用于送礼。" end local html = mw.html.create('table'):addClass('wikitable') html:tag('tr') :tag('th'):css('white-space', 'nowrap'):wikitext('类型'):done() :tag('th'):wikitext('人物'):done() for _, row in ipairs(rows) do html:tag('tr') :tag('th'):css('white-space', 'nowrap'):wikitext(row.label):done() :tag('td'):wikitext(row.content):done() end return css_out .. tostring(html) end return p
该页面嵌入的页面:
模板:Tl
(
查看源代码
)
模块:ArgsUtil
(
查看源代码
)
模块:Gifting/doc
(
查看源代码
)
模块:Template link
(
查看源代码
)
返回
模块:Gifting
。
查看“︁模块:Gifting”︁的源代码
来自星砂岛百科