打开/关闭搜索
搜索
打开/关闭菜单
1K
5.2K
4
8.2K
星砂岛百科
导航
首页
最近更改
随机页面
MediaWiki帮助
特殊页面
上传文件
打开/关闭外观设置菜单
通知
打开/关闭个人菜单
未登录
未登录用户的IP地址会在进行任意编辑后公开展示。
user-interface-preferences
个人工具
创建账号
登录
本站正在进行早期测试,目前仍存在许多内容的缺失。
查看“︁模块:Schedule”︁的源代码
来自星砂岛百科
查看
阅读
查看源代码
查看历史
associated-pages
模块
讨论
更多操作
←
模块:Schedule
因为以下原因,您没有权限编辑该页面:
您请求的操作仅限属于该用户组的用户执行:已验证邮箱用户
您没有权限编辑
模块
命名空间内的页面。
您必须确认您的电子邮件地址才能编辑页面。请通过
参数设置
设置并确认您的电子邮件地址。
您可以查看和复制此页面的源代码。
local common = require('Module:Common') local character_common = require('Module:CharacterCommon') local p = {} local raw_schedule_records local candidate_registry local expanded_record_cache local mapping local location_labels local source_order = { regular = 1, special = 2, living_together = 3, } local source_labels = { regular = '常规日程', special = '特殊日程', living_together = '同居日程', } local weekday_labels = { monday = '周一', tuesday = '周二', wednesday = '周三', thursday = '周四', friday = '周五', saturday = '周六', sunday = '周日', } local season_number_labels = { ['1'] = '春季', ['2'] = '夏季', ['3'] = '秋季', ['4'] = '冬季', } local season_plain_labels = { spring = '春季', summer = '夏季', autumn = '秋季', fall = '秋季', winter = '冬季', } local season_keys = { ['1'] = 'spring', ['2'] = 'summer', ['3'] = 'autumn', ['4'] = 'winter', } local season_template_keys = { ['1'] = '1', ['2'] = '2', ['3'] = '3', ['4'] = '4', spring = 'spring', summer = 'summer', autumn = 'fall', fall = 'fall', winter = 'winter', } local weekday_number_labels = { ['0'] = '周日', ['1'] = '周一', ['2'] = '周二', ['3'] = '周三', ['4'] = '周四', ['5'] = '周五', ['6'] = '周六', } local weekday_keys = { ['0'] = 'sunday', ['1'] = 'monday', ['2'] = 'tuesday', ['3'] = 'wednesday', ['4'] = 'thursday', ['5'] = 'friday', ['6'] = 'saturday', } local weather_labels = { Rain = '雨天', ClearSky = '晴天', Snow = '雪天', } local weather_template_keys = { Rain = 'Rainy', ClearSky = 'Sunny', Snow = 'Snowy', } local weather_plain_labels = { Rain = '雨天', Rainy = '雨天', ClearSky = '晴天', Sunny = '晴天', Snow = '雪天', Snowy = '雪天', } local festival_labels = { ['Festival.BeachFestival_Formal'] = '沙滩节', ['Festival.SpringFestival_Formal'] = '春节', } local festival_heading_labels = { ['Festival.SpringFestivalActivity_POI_AfterFestival'] = '春节准备', ['Festival.SpringFestivalActivity_POI_InFestival'] = '春节期间', } local festival_date_ranges = { ['Festival.BeachFestival_Formal'] = { month = 2, day_min = 27, day_max = 27, }, ['Festival.SpringFestivalActivity_POI_AfterFestival'] = { month = 4, day_min = 25, day_max = 28, }, ['Festival.SpringFestivalActivity_POI_InFestival'] = { month = 1, day_min = 1, day_max = 4, }, } local buildable_labels = { BuildableRegion_Boat = '渡口建造', } local compare_op_labels = { Equal = '等于', GreatOrEqualThan = '大于等于', GreatThan = '大于', LessOrEqualThan = '小于等于', LessThan = '小于', Between = '介于', } local generic_condition_descs = { ['On Raining Days'] = true, ['雨天日程'] = true, ['雨雪天'] = true, ['雨雪天日程'] = true, } local generic_mission_goals = { ['执行互动'] = true, } local function normalize_key(value) return character_common.normalizeKey(value) end local function copy_array(values) local out = {} if type(values) ~= 'table' then return out end for _, value in ipairs(values) do out[#out + 1] = value end return out end local function normalize_number_array(values) local out = {} if type(values) ~= 'table' then return out end for _, value in ipairs(values) do local number = tonumber(value) if number ~= nil then out[#out + 1] = number end end return out end local function has_ipairs_items(values) if type(values) ~= 'table' then return false end for _ in ipairs(values) do return true end return false end local function append_unique(target, values) if type(target) ~= 'table' or type(values) ~= 'table' then return end local seen = {} for _, value in ipairs(target) do seen[tostring(value)] = true end for _, value in ipairs(values) do local key = tostring(value) if not seen[key] then target[#target + 1] = value seen[key] = true end end end local function sort_text_list(values, order_map) table.sort(values, function(a, b) local a_key = tostring(a) local b_key = tostring(b) local a_rank = order_map and order_map[a_key] or nil local b_rank = order_map and order_map[b_key] or nil if a_rank and b_rank and a_rank ~= b_rank then return a_rank < b_rank end if a_rank and not b_rank then return true end if b_rank and not a_rank then return false end return a_key < b_key end) end local function sort_number_list(values) table.sort(values, function(a, b) return tonumber(a) < tonumber(b) end) end local function pick_primary_action(candidates) if type(candidates) ~= 'table' or #candidates == 0 then return {} end local ranked = {} for _, candidate in ipairs(candidates) do if type(candidate) == 'table' then ranked[#ranked + 1] = candidate end end table.sort(ranked, function(a, b) local a_weight = tonumber(a.weight or 0) or 0 local b_weight = tonumber(b.weight or 0) or 0 if a_weight ~= b_weight then return a_weight > b_weight end local a_score = tonumber(a.score or 0) or 0 local b_score = tonumber(b.score or 0) or 0 if a_score ~= b_score then return a_score > b_score end return common.trim(a.template) > common.trim(b.template) end) return ranked[1] or {} end local function build_slot_summary(slot) if type(slot) ~= 'table' then return '' end local mission_template = common.trim(slot.mission_template) if mission_template ~= '' then local mission_display = common.trim(slot.mission_display) if mission_display ~= '' then return mission_display end local mission_title = common.trim(slot.mission_title) if mission_title ~= '' then return mission_title end return mission_template end local primary_action = slot.primary_action if type(primary_action) ~= 'table' then return '' end local display_name = common.trim(primary_action.display_name) local location_hint = common.trim(primary_action.location_hint) local location_label = common.trim(primary_action.location_label) local location_display = location_label ~= '' and location_label or location_hint local template_name = common.trim(primary_action.template) if display_name ~= '' and location_display ~= '' then return display_name .. ' (' .. location_display .. ')' end if display_name ~= '' then return display_name end if location_display ~= '' and template_name ~= '' then return template_name .. ' (' .. location_display .. ')' end return template_name end local function resolve_location_label(value) local text = common.trim(value) if text == '' or type(location_labels) ~= 'table' then return '' end local direct = common.trim(location_labels[text] or '') if direct ~= '' then return direct end local festival_base = mw.ustring.match(text, '^(.+)_BeachFestival$') if festival_base then local base = common.trim(location_labels[festival_base] or '') if base ~= '' then return base .. '(海滩节)' end end local location_key, actor_key = mw.ustring.match(text, '^(.*)_([A-Za-z][A-Za-z0-9]+)$') if location_key and actor_key then local location_label = common.trim(location_labels[location_key] or '') local actor_label = common.trim(location_labels[actor_key] or '') if location_label ~= '' and actor_label ~= '' then return actor_label .. '(' .. location_label .. ')' end if location_label ~= '' then return location_label .. '(' .. actor_key .. ')' end if actor_label ~= '' then return actor_label end end return '' end local function expand_candidate_entry(raw_candidate) if type(raw_candidate) ~= 'table' then return {} end return { template = common.trim(raw_candidate.t), display_name = common.trim(raw_candidate.d), location_hint = common.trim(raw_candidate.l), location_label = common.trim(raw_candidate.ll or resolve_location_label(raw_candidate.l)), merged_display_name = common.trim(raw_candidate.md), weight = raw_candidate.w or 0, score = raw_candidate.s or 0, } end local function expand_compact_slot(raw_slot, candidate_registry) if type(raw_slot) ~= 'table' then return {} end local slot = { begin_time = common.trim(raw_slot.b), end_time = common.trim(raw_slot.e), kind = common.trim(raw_slot.k), } if slot.kind == 'actions' then local action_candidates = {} if type(raw_slot.a) == 'table' then for _, raw_index in ipairs(raw_slot.a) do local index = tonumber(raw_index) if index ~= nil then local candidate = candidate_registry[index + 1] if type(candidate) == 'table' then action_candidates[#action_candidates + 1] = expand_candidate_entry(candidate) end end end end slot.action_candidates = action_candidates slot.primary_action = pick_primary_action(action_candidates) elseif slot.kind == 'mission' then slot.mission_template = common.trim(raw_slot.mt) slot.mission_title = common.trim(raw_slot.tt) slot.mission_goal = common.trim(raw_slot.g) slot.mission_display = common.trim(raw_slot.md) end slot.summary = build_slot_summary(slot) return slot end local function expand_compact_variant(raw_variant, candidate_registry) if type(raw_variant) ~= 'table' then return {} end local months = normalize_number_array(raw_variant.m) local weekdays = normalize_number_array(raw_variant.w) local days = normalize_number_array(raw_variant.d) local month_keys = {} local weekday_label_keys = {} for _, value in ipairs(months) do local key = season_keys[tostring(value)] if key then month_keys[#month_keys + 1] = key end end for _, value in ipairs(weekdays) do local key = weekday_keys[tostring(value)] if key then weekday_label_keys[#weekday_label_keys + 1] = key end end local slots = {} if type(raw_variant.sl) == 'table' then for _, raw_slot in ipairs(raw_variant.sl) do if type(raw_slot) == 'table' then slots[#slots + 1] = expand_compact_slot(raw_slot, candidate_registry) end end end return { source = common.trim(raw_variant.o), schedule_id = common.trim(raw_variant.i), months = months, month_labels = month_keys, weekdays = weekdays, weekday_labels = weekday_label_keys, days = days, priority = '', condition_desc = common.trim(raw_variant.c), condition_summary = type(raw_variant.cs) == 'table' and raw_variant.cs or {}, condition_raw = {}, slots = slots, } end local function expand_compact_record(record_key, raw_record, candidate_registry) if type(raw_record) ~= 'table' then return nil end local counts = type(raw_record.r) == 'table' and raw_record.r or {} local variants = {} if type(raw_record.v) == 'table' then for _, raw_variant in ipairs(raw_record.v) do if type(raw_variant) == 'table' then variants[#variants + 1] = expand_compact_variant(raw_variant, candidate_registry) end end end return { id = common.trim(raw_record.i), name = common.trim(raw_record.n), key = record_key, calendar_id = common.trim(raw_record.c), regular_count = tonumber(counts[1] or 0) or 0, special_count = tonumber(counts[2] or 0) or 0, living_together_count = tonumber(counts[3] or 0) or 0, variants = variants, } end local function load_data() if raw_schedule_records then return end local raw_data = common.loadJsonData('数据:Character/character_schedule_index.json') or {} if type(raw_data.records) == 'table' then raw_schedule_records = raw_data.records candidate_registry = type(raw_data.candidate_registry) == 'table' and raw_data.candidate_registry or {} else raw_schedule_records = raw_data candidate_registry = {} end expanded_record_cache = {} mapping = character_common.loadCharacterMapping() location_labels = common.loadJsonData('数据:Character/schedule_location_labels.json') or {} end local function expand_record_by_key(record_key) if record_key == '' then return nil end if expanded_record_cache[record_key] ~= nil then return expanded_record_cache[record_key] or nil end local raw_record = raw_schedule_records[record_key] if type(raw_record) ~= 'table' then expanded_record_cache[record_key] = false return nil end local record if type(raw_record.v) == 'table' then record = expand_compact_record(record_key, raw_record, candidate_registry or {}) else record = raw_record end expanded_record_cache[record_key] = record or false return record end local function find_record(key) load_data() local _, record_key = character_common.findRecordWithKey(raw_schedule_records, mapping, key) return expand_record_by_key(record_key) end local function list_key(values) if type(values) ~= 'table' then return '' end local out = {} for _, value in ipairs(values) do out[#out + 1] = tostring(value) end return table.concat(out, ',') end local function build_condition_key(items) if type(items) ~= 'table' then return '' end local out = {} for _, item in ipairs(items) do if type(item) == 'table' then out[#out + 1] = table.concat({ common.trim(item.type), tostring(item.reverse), common.trim(item.dest_type), common.trim(item.key), common.trim(item.tag), common.trim(item.op), common.trim(item.value), tostring(item.check_day), tostring(item.check_month), tostring(item.check_weekday), tostring(item.check_year), common.trim(item.year_op), common.trim(item.year_value), }, '~') end end return table.concat(out, '||') end local function build_slot_key(slots) if type(slots) ~= 'table' then return '' end local out = {} for _, slot in ipairs(slots) do if type(slot) == 'table' then local primary_action = slot.primary_action local primary_template = '' if type(primary_action) == 'table' then primary_template = common.trim(primary_action.template) end out[#out + 1] = table.concat({ common.trim(slot.begin_time), common.trim(slot.end_time), common.trim(slot.kind), common.trim(slot.summary), common.trim(slot.mission_template), primary_template, }, '~') end end return table.concat(out, '||') end local function clone_variant(variant) return { source = common.trim(variant.source), schedule_id = common.trim(variant.schedule_id), months = copy_array(variant.months), month_labels = copy_array(variant.month_labels), weekdays = copy_array(variant.weekdays), weekday_labels = copy_array(variant.weekday_labels), days = copy_array(variant.days), priority = variant.priority, condition_desc = common.trim(variant.condition_desc), condition_summary = variant.condition_summary or {}, condition_raw = variant.condition_raw or {}, slots = variant.slots or {}, } end local function merge_variants(variants) local grouped = {} local order = {} if type(variants) ~= 'table' then return order end for _, variant in ipairs(variants) do if type(variant) == 'table' then local key = table.concat({ common.trim(variant.source), common.trim(variant.schedule_id), list_key(variant.months), list_key(variant.days), common.trim(variant.condition_desc), build_condition_key(variant.condition_summary), build_slot_key(variant.slots), }, '|') if not grouped[key] then grouped[key] = clone_variant(variant) order[#order + 1] = grouped[key] else append_unique(grouped[key].weekdays, variant.weekdays or {}) append_unique(grouped[key].weekday_labels, variant.weekday_labels or {}) append_unique(grouped[key].months, variant.months or {}) append_unique(grouped[key].month_labels, variant.month_labels or {}) append_unique(grouped[key].days, variant.days or {}) end end end for _, variant in ipairs(order) do sort_number_list(variant.months) sort_number_list(variant.weekdays) sort_number_list(variant.days) sort_text_list(variant.month_labels, { spring = 1, summer = 2, autumn = 3, winter = 4, }) sort_text_list(variant.weekday_labels, { monday = 1, tuesday = 2, wednesday = 3, thursday = 4, friday = 5, saturday = 6, sunday = 7, }) end local function condition_priority(variant) local has_weekday = type(variant.weekdays) == 'table' and #variant.weekdays > 0 local has_rain = false local has_clear = false if type(variant.condition_summary) == 'table' then for _, item in ipairs(variant.condition_summary) do if type(item) == 'table' then local item_type = common.trim(item.type) local is_reverse = item.reverse == true local dest_type = common.trim(item.dest_type) if item_type == 'weather_today' then if not is_reverse and dest_type == 'Rain' then has_rain = true elseif is_reverse and (dest_type == 'Rain' or dest_type == 'ClearSky') then has_clear = true elseif not is_reverse and dest_type == 'Sunny' then has_clear = true end end end end end if has_clear and not has_weekday then return 0 end if has_rain and not has_weekday then return 1 end if has_weekday and not has_rain then return 2 end if has_weekday and has_rain then return 3 end return 4 end table.sort(order, function(a, b) local a_source = source_order[a.source] or 99 local b_source = source_order[b.source] or 99 if a_source ~= b_source then return a_source < b_source end local a_condition_priority = condition_priority(a) local b_condition_priority = condition_priority(b) if a_condition_priority ~= b_condition_priority then return a_condition_priority < b_condition_priority end local a_day = tonumber(a.days[1] or 99) or 99 local b_day = tonumber(b.days[1] or 99) or 99 if a_day ~= b_day then return a_day < b_day end local a_month = tonumber(a.months[1] or 99) or 99 local b_month = tonumber(b.months[1] or 99) or 99 if a_month ~= b_month then return a_month < b_month end local a_weekday = tonumber(a.weekdays[1] or 99) or 99 local b_weekday = tonumber(b.weekdays[1] or 99) or 99 if a_weekday ~= b_weekday then return a_weekday < b_weekday end local a_desc = common.trim(a.condition_desc) local b_desc = common.trim(b.condition_desc) if a_desc ~= b_desc then return a_desc < b_desc end return common.trim(a.schedule_id) < common.trim(b.schedule_id) end) return order end local function map_labels(values, label_map) if type(values) ~= 'table' then return {} end local out = {} for _, value in ipairs(values) do local text = common.trim(value) if text ~= '' then out[#out + 1] = label_map[text] or text end end return out end local function join_parts(values) if type(values) ~= 'table' or #values == 0 then return '' end return table.concat(values, '、') end local function unique_texts(values) local out = {} local seen = {} if type(values) ~= 'table' then return out end for _, value in ipairs(values) do local text = common.trim(value) if text ~= '' and not seen[text] then out[#out + 1] = text seen[text] = true end end return out end local function rank_candidates(candidates) local ranked = {} if type(candidates) ~= 'table' then return ranked end for _, candidate in ipairs(candidates) do if type(candidate) == 'table' then ranked[#ranked + 1] = candidate end end table.sort(ranked, function(a, b) local a_weight = tonumber(a.weight or 0) or 0 local b_weight = tonumber(b.weight or 0) or 0 if a_weight ~= b_weight then return a_weight > b_weight end local a_score = tonumber(a.score or 0) or 0 local b_score = tonumber(b.score or 0) or 0 if a_score ~= b_score then return a_score > b_score end return common.trim(a.template) > common.trim(b.template) end) return ranked end local function clean_condition_desc(value) local text = common.trim(value) text = mw.ustring.gsub(text, '^%*+', '') return common.trim(text) end local function should_use_structured_condition(desc, summary) local cleaned = clean_condition_desc(desc) if cleaned == '' then return true end if generic_condition_descs[cleaned] then return true end if mw.ustring.find(cleaned, '[A-Za-z]') and not mw.ustring.find(cleaned, '[一-龥]') then return true end if type(summary) == 'table' then for _, item in ipairs(summary) do if type(item) == 'table' and (item.type == 'calendar_now_is_festival' or item.type == 'weather_today') then return true end end end return false end local function describe_compare_range(op, value, min_value, max_value, label_map, suffix) local resolved_op = common.trim(op) local resolved_value = common.trim(value) local resolved_min = common.trim(min_value) local resolved_max = common.trim(max_value) local resolved_suffix = suffix or '' local function map_value(raw) local key = common.trim(raw) if key == '' then return '' end if label_map and label_map[key] then return label_map[key] end return key .. resolved_suffix end if resolved_op == 'Equal' and resolved_value ~= '' then return map_value(resolved_value) end if resolved_op == 'Between' and resolved_min ~= '' and resolved_max ~= '' then local left = map_value(resolved_min) local right = map_value(resolved_max) if label_map then return left .. ' - ' .. right end return resolved_min .. ' - ' .. resolved_max .. resolved_suffix end local compare_label = compare_op_labels[resolved_op] or resolved_op if compare_label ~= '' and resolved_value ~= '' then return compare_label .. map_value(resolved_value) end return '' end local function resolve_season_template_key(value) local normalized = mw.ustring.lower(common.trim(value)) if normalized == '' then return nil end return season_template_keys[normalized] or season_template_keys[season_keys[normalized] or ''] end local function format_season_token(value) local key = resolve_season_template_key(value) if key then return '{{Season|' .. key .. '}}' end return '' end local function format_season_day_text(months, days) if #months == 1 and #days > 0 then local key = resolve_season_template_key(months[1]) if key then local season_days = {} for _, day in ipairs(days) do local value = common.trim(day) if value ~= '' then season_days[#season_days + 1] = '{{Season|' .. key .. '|' .. value .. '}}' end end if #season_days > 0 then return join_parts(season_days) end end end return '' end local function format_condition_date_text(month_value, day_value) local season_key = resolve_season_template_key(month_value) local day_text = common.trim(day_value) if season_key ~= nil and day_text ~= '' then return '{{Season|' .. season_key .. '|' .. day_text .. '}}' end if season_key ~= nil then return '{{Season|' .. season_key .. '}}' end if day_text ~= '' then return day_text .. '日' end return '' end local function format_condition_date_range_text(month_value, day_min_value, day_max_value) local season_key = resolve_season_template_key(month_value) local min_text = common.trim(day_min_value) local max_text = common.trim(day_max_value) if season_key ~= nil and min_text ~= '' and max_text ~= '' then if min_text == max_text then return '{{Season|' .. season_key .. '|' .. min_text .. '}}' end return '{{Season|' .. season_key .. '|' .. min_text .. '}} - {{Season|' .. season_key .. '|' .. max_text .. '}}' end if min_text ~= '' and max_text ~= '' then if min_text == max_text then return min_text .. '日' end return min_text .. '日-' .. max_text .. '日' end return '' end local function build_apply_text(variant) local parts = {} local month_values = variant.month_labels or {} local day_values = variant.days or {} local month_parts = {} local weekdays = map_labels(variant.weekday_labels, weekday_labels) local season_day_text = format_season_day_text(month_values, day_values) local days = {} if season_day_text == '' then for _, month in ipairs(month_values) do local token = format_season_token(month) if token ~= '' then month_parts[#month_parts + 1] = token end end for _, day in ipairs(day_values) do local value = common.trim(day) if value ~= '' then days[#days + 1] = value .. '日' end end if #month_parts > 0 then parts[#parts + 1] = join_parts(month_parts) end if #days > 0 then parts[#parts + 1] = join_parts(days) end end if #weekdays > 0 then parts[#parts + 1] = join_parts(weekdays) end if season_day_text ~= '' then parts[#parts + 1] = season_day_text end return table.concat(parts, ' / ') end local describe_condition_item local function build_structured_condition_title(summary) if type(summary) ~= 'table' then return '' end local festival_template = '' local festival_text = '' local weather_text = '' local exact_date_text = '' local ranged_date_text = '' for _, item in ipairs(summary) do if type(item) == 'table' then if item.type == 'calendar_now_is_festival' and not item.reverse then festival_template = common.trim(item.festival_template) festival_text = festival_labels[festival_template] or festival_heading_labels[festival_template] or festival_template elseif item.type == 'weather_today' and weather_text == '' then local dest_type = common.trim(item.dest_type) local template_key = weather_template_keys[dest_type] local label = weather_labels[dest_type] or dest_type weather_text = template_key and ('{{Weather|' .. template_key .. '}}') or label elseif item.type == 'time_check_date' then if exact_date_text == '' and item.check_month and item.check_day and common.trim(item.month_op) == 'Equal' and common.trim(item.day_op) == 'Equal' then exact_date_text = format_condition_date_text(item.month_value, item.day_value) elseif ranged_date_text == '' and item.check_month and item.check_day and common.trim(item.month_op) == 'Equal' and common.trim(item.day_op) == 'Between' and not item.reverse then ranged_date_text = format_condition_date_range_text(item.month_value, item.day_min_value, item.day_max_value) end end end end local fallback_range = festival_date_ranges[festival_template] if exact_date_text == '' and fallback_range and fallback_range.day_min == fallback_range.day_max then exact_date_text = format_condition_date_text(fallback_range.month, fallback_range.day_min) end if festival_text ~= '' and exact_date_text ~= '' then return exact_date_text .. '|' .. festival_text end if festival_text ~= '' and ranged_date_text == '' and fallback_range then ranged_date_text = format_condition_date_range_text(fallback_range.month, fallback_range.day_min, fallback_range.day_max) end if festival_text ~= '' and ranged_date_text ~= '' then return ranged_date_text .. '|' .. festival_text end if weather_text ~= '' then return weather_text end return '' end describe_condition_item = function(item) if type(item) ~= 'table' then return '' end if item.type == 'weather_today' then local dest_type = common.trim(item.dest_type) local template_key = weather_template_keys[dest_type] local label = weather_labels[dest_type] or dest_type local weather_text = template_key and ('{{Weather|' .. template_key .. '}}') or label if weather_text ~= '' then if item.reverse then return '非' .. weather_text end return weather_text end return '天气条件' end if item.type == 'entity_data_check_string' then local key = common.trim(item.key) local op = common.trim(item.op) local value = common.trim(item.value) if key == 'Schedule_Typhoon' and value == '1' then if item.reverse then return '非台风天气' end return '台风天气' end if key ~= '' and value ~= '' then local prefix = item.reverse and '不满足:' or '' if op ~= '' then return prefix .. key .. ' ' .. op .. ' ' .. value end return prefix .. key .. ' = ' .. value end end if item.type == 'npc_affection_star' then local value = common.trim(item.value) if value == '' then return item.reverse and '好感条件不满足' or '好感条件' end local op = common.trim(item.op) if op == 'GreatOrEqualThan' then return item.reverse and ('好感度未达到 ' .. value .. ' 心') or ('好感度达到 ' .. value .. ' 心') end if op == 'Equal' then return item.reverse and ('好感度不为 ' .. value .. ' 心') or ('好感度为 ' .. value .. ' 心') end local compare_label = compare_op_labels[op] or op if compare_label ~= '' then return item.reverse and ('好感条件不满足 ' .. compare_label .. ' ' .. value .. ' 心') or ('好感度 ' .. compare_label .. ' ' .. value .. ' 心') end return item.reverse and '好感条件不满足' or '好感条件' end if item.type == 'calendar_now_is_festival' then local festival_template = common.trim(item.festival_template) local label = festival_labels[festival_template] or festival_heading_labels[festival_template] or festival_template if label ~= '' then if item.reverse then return '非' .. label end return label end return item.reverse and '非节庆期间' or '节庆期间' end if item.type == 'buildable_region_check_completed' then local template_name = common.trim(item.template) local label = buildable_labels[template_name] or template_name if label ~= '' then if item.reverse then return label .. '未完成' end return label .. '已完成' end return item.reverse and '建筑条件未完成' or '建筑条件已完成' end if item.type == 'check_has_owner' then if item.reverse then return '未被收养' end return '已被收养' end if item.type == 'time_check_date' then local parts = {} if item.check_month then local month_text = describe_compare_range( item.month_op, item.month_value, item.month_min_value, item.month_max_value, season_number_labels ) if month_text ~= '' then parts[#parts + 1] = month_text else parts[#parts + 1] = '月份' end end if item.check_day then local day_text = describe_compare_range( item.day_op, item.day_value, item.day_min_value, item.day_max_value, nil, '日' ) if day_text ~= '' then parts[#parts + 1] = day_text else parts[#parts + 1] = '日期' end end if item.check_weekday then local weekday_text = describe_compare_range( item.weekday_op, item.weekday_value, item.weekday_min_value, item.weekday_max_value, weekday_number_labels ) if weekday_text ~= '' then parts[#parts + 1] = weekday_text else parts[#parts + 1] = '星期' end end if item.check_year then local year_text = '' if common.trim(item.year_op) == 'Equal' and common.trim(item.year_value) ~= '' then year_text = '第' .. common.trim(item.year_value) .. '年' else year_text = describe_compare_range( item.year_op, item.year_value, item.year_min_value, item.year_max_value ) if year_text ~= '' then year_text = '年份' .. year_text end end if year_text ~= '' then parts[#parts + 1] = year_text else parts[#parts + 1] = '年份' end end if #parts > 0 then local text = table.concat(parts, '') if item.reverse then return '非' .. text end return text end return item.reverse and '日期条件不满足' or '日期条件' end return '特殊条件' end local function build_condition_text(variant) local condition_desc = clean_condition_desc(variant.condition_desc) local summary = variant.condition_summary local title_text = build_structured_condition_title(summary) local parts = {} if type(summary) == 'table' then for _, item in ipairs(summary) do local text = describe_condition_item(item) if text ~= '' then parts[#parts + 1] = text end end end local structured_text = table.concat(unique_texts(parts), ';') if title_text ~= '' then return title_text end if condition_desc == '' then return structured_text end if structured_text == '' then return condition_desc end if should_use_structured_condition(condition_desc, summary) then return structured_text end return condition_desc end local function build_variant_heading(variant) local parts = {} local apply_text = build_apply_text(variant) local condition_text = build_condition_text(variant) if apply_text ~= '' then parts[#parts + 1] = apply_text end if condition_text ~= '' then parts[#parts + 1] = condition_text end if #parts == 0 then return '默认' end return table.concat(parts, '|') end local function resolve_season_plain_label(value) local normalized = mw.ustring.lower(common.trim(value)) if normalized == '' then return '' end local season_key = season_keys[normalized] or normalized if season_number_labels[season_key] then return season_number_labels[season_key] end if season_plain_labels[season_key] then return season_plain_labels[season_key] end return common.trim(value) end local function build_variant_tab_label(variant) local text = build_variant_heading(variant) text = mw.ustring.gsub(text, '{{%s*[Ss]eason%s*|%s*([^|}]+)%s*|%s*([^}]+)%s*}}', function(key, day) local season_label = resolve_season_plain_label(key) local day_text = common.trim(day) if season_label ~= '' and day_text ~= '' then return season_label .. day_text .. '日' end if season_label ~= '' then return season_label end return common.trim(key) end) text = mw.ustring.gsub(text, '{{%s*[Ss]eason%s*|%s*([^}]+)%s*}}', function(key) local season_label = resolve_season_plain_label(key) return season_label ~= '' and season_label or common.trim(key) end) text = mw.ustring.gsub(text, '{{%s*[Ww]eather%s*|%s*([^}]+)%s*}}', function(key) local weather_key = common.trim(key) return weather_plain_labels[weather_key] or weather_labels[weather_key] or weather_key end) text = mw.ustring.gsub(text, '%[%[([^%]|]+)|([^%]]+)%]%]', '%2') text = mw.ustring.gsub(text, '%[%[([^%]]+)%]%]', '%1') text = mw.ustring.gsub(text, "'''", '') text = mw.ustring.gsub(text, '<[^>]+>', '') text = common.trim(text) return text ~= '' and text or '默认' end local function format_candidate_text(candidate) if type(candidate) ~= 'table' then return '' end local merged_display_name = common.trim(candidate.merged_display_name) if merged_display_name ~= '' then return merged_display_name end local display_name = common.trim(candidate.display_name) local location_label = common.trim(candidate.location_label) if location_label == '' then location_label = resolve_location_label(candidate.location_hint) end if display_name ~= '' and location_label ~= '' and not mw.ustring.find(display_name, location_label, 1, true) then return display_name .. '(' .. location_label .. ')' end if display_name ~= '' then return display_name end if location_label ~= '' then return location_label end local template_name = common.trim(candidate.template) if template_name ~= '' then return template_name end return common.trim(candidate.location_hint) end local function format_probability(weight, total_weight) local current = tonumber(weight or 0) or 0 local total = tonumber(total_weight or 0) or 0 if current <= 0 then return '—' end if total <= 0 then return tostring(current) end local percent = current * 100 / total if math.abs(percent - math.floor(percent + 0.5)) < 0.05 then return string.format('%d%%', math.floor(percent + 0.5)) end return string.format('%.1f%%', percent) end local format_slot_activity local format_slot_note local function build_slot_schedule_entries(slot) local entries = {} if type(slot) ~= 'table' then return entries end if common.trim(slot.kind) == 'mission' then local mission_activity = format_slot_activity(slot) local mission_note = format_slot_note(slot) entries[#entries + 1] = { activity = mission_note ~= '' and (mission_activity .. '<br /><small>' .. mission_note .. '</small>') or mission_activity, probability = '—', } return entries end local ranked_candidates = rank_candidates(slot.action_candidates) if #ranked_candidates == 0 then local activity_text = format_slot_activity(slot) if activity_text ~= '' then entries[#entries + 1] = { activity = activity_text, probability = '—', } end return entries end local total_weight = 0 for _, candidate in ipairs(ranked_candidates) do total_weight = total_weight + (tonumber(candidate.weight or 0) or 0) end for _, candidate in ipairs(ranked_candidates) do local activity_text = format_candidate_text(candidate) if activity_text ~= '' then entries[#entries + 1] = { activity = activity_text, probability = format_probability(candidate.weight, total_weight), } end end return entries end local function format_slot_time(slot) local begin_time = common.trim(slot.begin_time) local end_time = common.trim(slot.end_time) if begin_time ~= '' and end_time ~= '' then return begin_time .. '-' .. end_time end return begin_time ~= '' and begin_time or end_time end format_slot_activity = function(slot) if type(slot) ~= 'table' then return '' end if common.trim(slot.kind) == 'mission' then local mission_display = common.trim(slot.mission_display) if mission_display ~= '' then return mission_display end local mission_title = common.trim(slot.mission_title) if mission_title ~= '' then return mission_title end return '任务' end local primary_action = type(slot.primary_action) == 'table' and slot.primary_action or nil if primary_action then local activity_text = format_candidate_text(primary_action) if activity_text ~= '' then return activity_text end end return common.trim(slot.summary) end format_slot_note = function(slot) if type(slot) ~= 'table' then return '' end if common.trim(slot.kind) == 'mission' then local notes = {} local mission_goal = common.trim(slot.mission_goal) local mission_title = common.trim(slot.mission_title) if mission_goal ~= '' and not generic_mission_goals[mission_goal] then notes[#notes + 1] = mission_goal elseif mission_title ~= '' and mission_title ~= common.trim(slot.mission_display) then notes[#notes + 1] = mission_title end return table.concat(notes, ';') end return '' end local function render_variant_table(variant) local root = mw.html.create('table'):addClass('wikitable'):addClass('char-data-table'):addClass('char-schedule-table') local head = root:tag('tr') head:tag('th'):wikitext('时间') head:tag('th'):wikitext('安排') head:tag('th'):wikitext('概率') for _, slot in ipairs(variant.slots or {}) do local entries = build_slot_schedule_entries(slot) if #entries == 0 then entries = { { activity = format_slot_activity(slot), probability = '—', } } end local slot_time = format_slot_time(slot) local time_text = slot_time ~= '' and slot_time or '—' if #entries == 1 then local row = root:tag('tr') row:tag('td'):wikitext(time_text) row:tag('td'):wikitext(common.trim(entries[1].activity) ~= '' and entries[1].activity or '—') row:tag('td'):wikitext(common.trim(entries[1].probability) ~= '' and entries[1].probability or '—') else for index, entry in ipairs(entries) do local row = root:tag('tr') if index == 1 then row:tag('td') :attr('rowspan', tostring(#entries)) :wikitext(time_text) end row:tag('td'):wikitext(common.trim(entry.activity) ~= '' and entry.activity or '—') row:tag('td'):wikitext(common.trim(entry.probability) ~= '' and entry.probability or '—') end end end return tostring(root) end local function render_schedule_group(variant, include_heading) local out = { '<div class="char-schedule-group">' } if include_heading ~= false then out[#out + 1] = '<div class="char-schedule-heading"><b>' .. build_variant_heading(variant) .. '</b></div>' end out[#out + 1] = '<div class="char-table-wrap">' .. render_variant_table(variant) .. '</div>' out[#out + 1] = '</div>' return table.concat(out, '\n') end local function render_group(frame, source, variants) local items = {} for _, variant in ipairs(variants) do if variant.source == source and has_ipairs_items(variant.slots) then items[#items + 1] = variant end end if #items == 0 then return '' end if #items == 1 then return render_schedule_group(items[1], true) end local tab_parts = {} for index, variant in ipairs(items) do if index > 1 then tab_parts[#tab_parts + 1] = '|-|' end tab_parts[#tab_parts + 1] = build_variant_tab_label(variant) .. '=\n' .. render_schedule_group(variant, true) end return frame:extensionTag('tabber', table.concat(tab_parts, '\n'), { class = 'char-tabber char-schedule-group-tabber', }) end local function has_multi_candidate_slots(variants) if type(variants) ~= 'table' then return false end for _, variant in ipairs(variants) do for _, slot in ipairs(variant.slots or {}) do if #build_slot_schedule_entries(slot) > 1 then return true end end end return false end local function filter_display_variants(variants) local out = {} if type(variants) ~= 'table' then return out end for _, variant in ipairs(variants) do if type(variant) == 'table' and has_ipairs_items(variant.slots) then out[#out + 1] = variant end end return out end local function render_schedule_by_key(frame, key) local record = find_record(key) if not record then return '<div class="char-empty char-schedule-empty">未找到角色行程数据</div>' end local variants = filter_display_variants(merge_variants(record.variants)) if #variants == 0 then return '<div class="char-empty char-schedule-empty">暂无日程数据。</div>' end local tab_parts = {} for _, source in ipairs({ 'regular', 'special', 'living_together' }) do local block = render_group(frame, source, variants) if block ~= '' then if #tab_parts > 0 then tab_parts[#tab_parts + 1] = '|-|' end tab_parts[#tab_parts + 1] = (source_labels[source] or source) .. '=' tab_parts[#tab_parts + 1] = block end end if #tab_parts == 0 then return '<div class="char-empty char-schedule-empty">暂无可展示的行程数据。</div>' end local out = { '<div class="char-schedule-root">' } if has_multi_candidate_slots(variants) then out[#out + 1] = '<div class="char-note char-schedule-note">同一时段如出现多行,表示该时段存在多个候选动作;概率为该时段候选动作的相对权重。</div>' end out[#out + 1] = frame:extensionTag('tabber', table.concat(tab_parts, '\n'), { class = 'char-tabber char-schedule-tabber', }) out[#out + 1] = '</div>' return table.concat(out, '\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 = find_record(key) if not record then return '' end return common.toText(record[field]) end function p.renderSchedule(frame) local key = common.getArg(frame, 1, '') local output = render_schedule_by_key(frame, key) if output == '' then return '' end return frame:preprocess(output) end return p
该页面嵌入的页面:
模板:Tl
(
查看源代码
)
模块:ArgsUtil
(
查看源代码
)
模块:Schedule/doc
(
查看源代码
)
模块:Template link
(
查看源代码
)
返回
模块:Schedule
。
查看“︁模块:Schedule”︁的源代码
来自星砂岛百科