Editing Module:Sandbox/LVLGive feedback
The edit can be undone.
Please check the comparison below to verify that this is what you want to do, and then publish the changes below to finish undoing the edit.
| Latest revision | Your text | ||
| Line 1: | Line 1: | ||
local p = {} | local p = {} | ||
local lang = require "Module:Lang" | |||
local util_module = require "Module:Utilities" | |||
local data = mw.loadJsonData("Data:Monster_Domosed/ItemData.json") | |||
-- Helper function: get first non-nil string from variants (moved to module level) | |||
local | local function first_non_nil(...) | ||
for i = 1, select("#", ...) do | |||
local v = select(i, ...) | |||
if v and v ~= "" then return v end | |||
local | |||
end | end | ||
return | return "" | ||
end | end | ||
local function | -- Helper: get lang string with case fallback (from Domo's version) | ||
local function get_lang_with_fallback(base_key, suffix1, suffix2) | |||
return first_non_nil( | |||
lang.get_string(base_key .. suffix1), | |||
lang.get_string(base_key .. suffix2) | |||
) | |||
end | end | ||
-- ========= | -- Get component-of items sorted by tier (your fix, kept here until ready to move to Module:ItemData) | ||
local function get_component_of_sorted(item_input) | |||
local target_key = nil | |||
if data[item_input] then | |||
target_key = item_input | |||
else | |||
for k, v in pairs(data) do | |||
if v.Name == item_input and (v.IsDisabled == false or v.IsDisabled == nil) then | |||
target_key = k; break | |||
end | |||
end | |||
end | |||
if not target_key then return {} end | |||
local | local parents = {} | ||
for key, item in pairs(data) do | |||
if item.IsDisabled ~= true and item.Components and type(item.Components) == "table" then | |||
for _, comp in ipairs(item.Components) do | |||
if comp == target_key then | |||
table.insert(parents, key) | |||
break | |||
end | |||
end | |||
end | |||
end | |||
local | -- Sort by Tier instead of alphabetically (your fix) | ||
table.sort(parents, function(a, b) | |||
local tier_a = tonumber(data[a] and data[a]["Tier"]) or 0 | |||
local tier_b = tonumber(data[b] and data[b]["Tier"]) or 0 | |||
return tier_a < tier_b | |||
end) | |||
return parents | |||
end | |||
local | -- Get raw value, CSS class, and LocTokenOverride for a property | ||
if | local function get_raw_value(item_name, prop) | ||
local item = data[item_name] or nil | |||
if not item then | |||
for k,v in pairs(data) do | |||
if v.Name == item_name then item = v; break end | |||
end | |||
end | end | ||
if not item then return nil, nil, nil end | |||
local | local raw_prop = item[prop] | ||
if | if type(raw_prop) == "table" then | ||
local loc_override = raw_prop.LocTokenOverride or prop | |||
return raw_prop.Value, raw_prop.CSSClass, loc_override | |||
else | else | ||
return raw_prop, nil, prop | |||
end | end | ||
end | end | ||
-- | -- Check if property exists in language data (refactored to loop, from Domo's version) | ||
local function is_property_in_lang_data(prop) | |||
local function | local suffixes = {"_label", "_Label", "_prefix", "_Prefix", "_postfix", "_Postfix", "_postvalue_label", "_Postvalue_Label"} | ||
local | for _, suffix in ipairs(suffixes) do | ||
if lang.get_string(prop .. suffix) then | |||
return true | |||
for _, | |||
end | end | ||
end | end | ||
return false | |||
end | |||
-- Get item by name, ignoring disabled if possible | |||
if | local function get_json_item(name) | ||
for _, v in pairs(data) do | |||
if v.Name == name and (v.IsDisabled == false or v.IsDisabled == nil) then | |||
return v | |||
end | end | ||
end | end | ||
for _, v in pairs(data) do | |||
if v.Name == name then return v end | |||
end | |||
return nil | |||
end | |||
-- Format stat value for Infobox, handling prefix/postfix, CSS, scaling, and conditional | |||
local function format_stat(item_name, prop, skip_label, debug, allow_conditional) | |||
if allow_conditional == nil then allow_conditional = true end | |||
if | if prop == "AbilityCooldown" and not skip_label then | ||
return | return nil | ||
end | end | ||
local | local value, css, loc_key = get_raw_value(item_name, prop) | ||
if value == nil or value == 0 or value == "" then | |||
return nil | |||
end | end | ||
-- === | -- Convert string like "0m" or "0.0" to number for zero check | ||
local numeric_val = tonumber(tostring(value):match("^-?%d+%.?%d*")) or 0 | |||
if numeric_val == 0 then | |||
return nil | |||
end | |||
-- Skip if property has no lang data | |||
local label_key = loc_key:gsub("^#", "") | |||
if not ( | |||
lang.get_string(label_key.."_label") or | |||
lang.get_string(label_key.."_Label") or | |||
lang.get_string(label_key.."_prefix") or | |||
lang.get_string(label_key.."_Prefix") or | |||
lang.get_string(label_key.."_postfix") or | |||
lang.get_string(label_key.."_Postfix") or | |||
lang.get_string(label_key.."_postvalue_label") or | |||
lang.get_string(label_key.."_Postvalue_Label") | |||
) then | |||
return nil | |||
end | |||
local | if debug == 2 then | ||
local label_val = lang.get_string(label_key.."_label") and label_key.."_label" | |||
or lang.get_string(label_key.."_Label") and label_key.."_Label" | |||
or "(none)" | |||
local prefix_val = lang.get_string(label_key.."_prefix") and label_key.."_prefix" | |||
or lang.get_string(label_key.."_Prefix") and label_key.."_Prefix" | |||
or "(none)" | |||
local postfix_val = lang.get_string(label_key.."_postfix") and label_key.."_postfix" | |||
or lang.get_string(label_key.."_Postfix") and label_key.."_Postfix" | |||
or "(none)" | |||
return string.format( | |||
"raw invoke key: %s | raw value: %s | label key used: %s | prefix key used: %s | postfix key used: %s", | |||
loc_key, tostring(value), label_val, prefix_val, postfix_val | |||
) | |||
end | |||
if prop == "BuildUpPerShot" then label_key = "BuildupPerShot" | |||
elseif prop == "DotDuration" then label_key = "DOTDuration" | |||
end | |||
-- Get actual label, prefix, postfix with fallbacks (using helper from Domo's version) | |||
local label = skip_label and "" or get_lang_with_fallback(label_key, "_label", "_Label") | |||
local prefix = get_lang_with_fallback(label_key, "_prefix", "_Prefix") | |||
local postfix = get_lang_with_fallback(label_key, "_postfix", "_Postfix") | |||
if prefix == "" and postfix == "" and label == "" then | |||
return nil | |||
end | end | ||
local val_str = tostring(value) | |||
local | |||
------------------------------------------------------------------ | |||
-- Apply prefix | |||
------------------------------------------------------------------ | |||
if prefix ~= "" then | |||
if prefix == "{s:sign}" then | |||
local num = tonumber(val_str:match("^-?%d+%.?%d*")) | |||
if num and num >= 0 and not val_str:find("^%+") then | |||
val_str = "+"..val_str | |||
end | end | ||
else | else | ||
val_str = prefix..val_str | |||
end | end | ||
end | |||
------------------------------------------------------------------ | |||
if | -- CSS-specific formatting (after prefix to avoid double +) | ||
------------------------------------------------------------------ | |||
if css == "move_speed" or css == "sprint_speed" then | |||
if postfix == "" or not postfix:find("%%") then | |||
val_str = val_str:gsub("/s", "") .. "/s" | |||
end | |||
local num = tonumber(val_str:match("^-?%d+%.?%d*")) | |||
if num and num > 0 and not val_str:find("^%+") then | |||
val_str = "+" .. val_str | |||
end | end | ||
end | |||
------------------------------------------------------------------ | |||
-- Append postfix | |||
------------------------------------------------------------------ | |||
if prop ~= "BuildUpPerShot" and postfix ~= "" and not val_str:find(postfix, 1, true) then | |||
if postfix == "HP/s" then | |||
val_str = val_str .. " " .. postfix | |||
else | else | ||
val_str = val_str .. postfix | |||
end | end | ||
end | |||
------------------------------------------------------------------ | |||
-- Append scaling template {{Boon}} or {{Ss}} | |||
------------------------------------------------------------------ | |||
local | local item = get_json_item(item_name) | ||
local | local raw_prop = item[prop] | ||
if type(raw_prop) == "table" and raw_prop.Scale and raw_prop.Scale.Value ~= 0 then | |||
local scale_val = util_module.round_to_sig_fig(raw_prop.Scale.Value, 2) | |||
local template_name = (raw_prop.Scale.Type == "power_increase") and "Boon" or "Ss" | |||
val_str = val_str .. " " .. mw.getCurrentFrame():expandTemplate{ title = template_name, args = { scale_val } } | |||
end | |||
------------------------------------------------------------------ | |||
-- Append label | |||
------------------------------------------------------------------ | |||
if label ~= "" then | |||
val_str = val_str.." "..label | |||
end | |||
------------------------------------------------------------------ | |||
-- Append (Conditional) ONLY if allowed (your fix: only for ImportantProperties) | |||
------------------------------------------------------------------ | |||
if allow_conditional | |||
and type(raw_prop) == "table" | |||
and (raw_prop.UsageFlags == "ConditionallyApplied" or raw_prop.UsageFlags == "ConditionallyEnemyApplied") | |||
then | |||
local conditional_text = lang.get_string("Citadel_Shop_ConditionalAttribute") | |||
val_str = val_str .. string.format(' <span style="color:#C0C0C0">(%s)</span>', conditional_text) | |||
end | |||
return val_str | |||
end | |||
-- Special formatting for StatusEffect | |||
local function format_property_for_infobox(item_name, prop, skip_label, debug) | |||
if prop:match("^StatusEffect") then | |||
return string.format( | |||
"[[{{#invoke:Lang|get_string|Citadel_%s}}]] [[{{#invoke:Lang|get_string|Citadel_StatusEffect}}]]", | |||
prop | |||
) | |||
else | |||
-- allow_conditional = true: ImportantProperties can show Conditional (your fix) | |||
return format_stat(item_name, prop, skip_label, debug, true) | |||
end | |||
end | |||
function p.fill_tooltip(frame) | |||
local item_name = frame.args[1] | |||
if not item_name then return "Item name missing" end | |||
local debug = tonumber(frame.args.debug) or 0 | |||
local item = get_json_item(item_name) | |||
if not item then return "Item not found" end | |||
local ItemData = require("Module:ItemData") | |||
local lines = { | |||
"{{Infobox item", | |||
"| item_name = " .. item_name | |||
} | |||
if item.StreetBrawl == true then | |||
table.insert(lines, "| street_brawl = true") | |||
end | |||
---------------------------------------------------------------------- | |||
-- Check if AbilityCooldown or AbilityChargeUpTime exists in sections | |||
---------------------------------------------------------------------- | |||
local cooldown_found_in_sections = false | |||
local chargeup_found_in_sections = false | |||
for _, section in ipairs(item.TooltipSections or {}) do | |||
for _, entry in ipairs(section.Entries or {}) do | |||
for _, prop in ipairs(entry.Properties or {}) do | |||
if prop == "AbilityCooldown" then cooldown_found_in_sections = true end | |||
if prop == "AbilityChargeUpTime" then chargeup_found_in_sections = true end | |||
end | |||
for _, prop in ipairs(entry.ImportantProperties or {}) do | |||
if prop == "AbilityCooldown" then cooldown_found_in_sections = true end | |||
if prop == "AbilityChargeUpTime" then chargeup_found_in_sections = true end | |||
end | |||
end | end | ||
end | end | ||
-- | ---------------------------------------------------------------------- | ||
-- Innate stats | |||
---------------------------------------------------------------------- | |||
local innate_index = 1 | |||
for _, section in ipairs(item.TooltipSections or {}) do | |||
if section.Type == "EArea_Innate" then | |||
for _, entry in ipairs(section.Entries or {}) do | |||
for _, prop in ipairs(entry.Properties or {}) do | |||
if prop ~= "AbilityCooldown" and prop ~= "AbilityChargeUpTime" then | |||
-- allow_conditional = false for innate stats (your fix) | |||
local val = format_stat(item_name, prop, false, debug, false) | |||
if val then | |||
table.insert(lines, string.format("| item_stat%d = %s", innate_index, val)) | |||
innate_index = innate_index + 1 | |||
end | |||
for _, | end | ||
end | end | ||
end | end | ||
end | end | ||
end | end | ||
-- | ---------------------------------------------------------------------- | ||
-- Passive stats | |||
---------------------------------------------------------------------- | |||
local passive_counter = 1 | |||
for _, section in ipairs(item.TooltipSections or {}) do | |||
if section.Type ~= "EArea_Innate" and section.Type ~= "EArea_Active" then | |||
for _, entry in ipairs(section.Entries or {}) do | |||
local loc = entry.LocString and entry.LocString:gsub("#","") or nil | |||
if loc then | |||
table.insert(lines, string.format( | |||
"| passive%d_description = {{#invoke:Lang|get_string|%s|item_name=%s}}", | |||
passive_counter, loc, item_name | |||
for _, | )) | ||
if | end | ||
local stat_index = 1 | |||
local seen_props = {} | |||
-- ImportantProperties: allow_conditional = true (via format_property_for_infobox) | |||
for _, prop in ipairs(entry.ImportantProperties or {}) do | |||
if prop ~= "AbilityCooldown" and prop ~= "AbilityChargeUpTime" | |||
and is_property_in_lang_data(prop) and not seen_props[prop] then | |||
local val = format_property_for_infobox(item_name, prop, false, debug) | |||
if val then | |||
table.insert(lines, string.format("| passive%d_stat%d = %s", passive_counter, stat_index, val)) | |||
stat_index = stat_index + 1 | |||
seen_props[prop] = true | |||
end | |||
end | end | ||
end | end | ||
-- Properties: allow_conditional = false (your fix) | |||
for _, prop in ipairs(entry.Properties or {}) do | |||
if prop ~= "AbilityCooldown" and prop ~= "AbilityChargeUpTime" | |||
and is_property_in_lang_data(prop) and not seen_props[prop] then | |||
local val = format_stat(item_name, prop, false, debug, false) | |||
if val then | |||
table.insert(lines, string.format("| passive%d_stat%d = %s", passive_counter, stat_index, val)) | |||
stat_index = stat_index + 1 | |||
seen_props[prop] = true | |||
end | |||
end | |||
end | |||
-- AbilityCooldown | |||
for _, prop_list in ipairs({entry.Properties or {}, entry.ImportantProperties or {}}) do | |||
for _, prop in ipairs(prop_list) do | |||
if prop == "AbilityCooldown" then | |||
local cd = format_stat(item_name, prop, true, debug) | |||
if cd then | |||
table.insert(lines, string.format("| passive%d_cooldown = %s", passive_counter, cd)) | |||
end | |||
end | end | ||
end | end | ||
end | |||
-- AbilityChargeUpTime | |||
for _, prop_list in ipairs({entry.Properties or {}, entry.ImportantProperties or {}}) do | |||
for _, prop in ipairs(prop_list) do | |||
if prop == "AbilityChargeUpTime" then | |||
local cu = format_stat(item_name, prop, true, debug) | |||
if cu then | |||
table.insert(lines, string.format("| passive%d_chargeup = %s", passive_counter, cu)) | |||
end | end | ||
end | end | ||
end | end | ||
end | end | ||
passive_counter = passive_counter + 1 | |||
end | end | ||
end | end | ||
end | end | ||
-- | ---------------------------------------------------------------------- | ||
-- Active stats | |||
local | ---------------------------------------------------------------------- | ||
local active_index = 1 | |||
for _, section in ipairs(item.TooltipSections or {}) do | |||
if section.Type == "EArea_Active" then | |||
for _, entry in ipairs(section.Entries or {}) do | |||
if entry.LocString then | |||
table.insert(lines, string.format( | |||
"| active%d_description = {{#invoke:Lang|get_string|%s|item_name=%s}}", | |||
active_index, entry.LocString:gsub("#",""), item_name | |||
)) | |||
end | |||
local stat_index = 1 | |||
local seen_props = {} | |||
-- ImportantProperties: allow_conditional = true (via format_property_for_infobox) | |||
for _, prop in ipairs(entry.ImportantProperties or {}) do | |||
if prop ~= "AbilityCooldown" and prop ~= "AbilityChargeUpTime" | |||
and is_property_in_lang_data(prop) and not seen_props[prop] then | |||
local val = format_property_for_infobox(item_name, prop, false, debug) | |||
if val then | |||
table.insert(lines, string.format("| active%d_stat%d = %s", active_index, stat_index, val)) | |||
stat_index = stat_index + 1 | |||
seen_props[prop] = true | |||
end | |||
end | |||
end | |||
-- Properties: allow_conditional = false (your fix) | |||
for _, prop in ipairs(entry.Properties or {}) do | |||
if prop ~= "AbilityCooldown" and prop ~= "AbilityChargeUpTime" | |||
and is_property_in_lang_data(prop) and not seen_props[prop] then | |||
local val = format_stat(item_name, prop, false, debug, false) | |||
if val then | |||
table.insert(lines, string.format("| active%d_stat%d = %s", active_index, stat_index, val)) | |||
stat_index = stat_index + 1 | |||
seen_props[prop] = true | |||
end | end | ||
end | end | ||
end | end | ||
-- AbilityCooldown | |||
for _, prop_list in ipairs({entry.Properties or {}, entry.ImportantProperties or {}}) do | |||
for _, prop in ipairs(prop_list) do | |||
if prop == "AbilityCooldown" then | |||
local cd = format_stat(item_name, prop, true, debug) | |||
if cd then | |||
table.insert(lines, string.format("| active%d_cooldown = %s", active_index, cd)) | |||
end | |||
end | |||
end | end | ||
end | end | ||
-- AbilityChargeUpTime | |||
for _, prop_list in ipairs({entry.Properties or {}, entry.ImportantProperties or {}}) do | |||
for _, prop in ipairs(prop_list) do | |||
if prop == "AbilityChargeUpTime" then | |||
local cu = format_stat(item_name, prop, true, debug) | |||
if cu then | |||
table.insert(lines, string.format("| active%d_chargeup = %s", active_index, cu)) | |||
end | |||
end | |||
end | |||
end | end | ||
active_index = active_index + 1 | |||
end | |||
end | |||
end | |||
---------------------------------------------------------------------- | |||
-- Global fallback for AbilityCooldown (your fix: attaches to active section) | |||
---------------------------------------------------------------------- | |||
if not cooldown_found_in_sections and not chargeup_found_in_sections and item.AbilityCooldown then | |||
local cd = format_stat(item_name, "AbilityCooldown", true, debug) | |||
if cd then | |||
local has_active = false | |||
for _, section in ipairs(item.TooltipSections or {}) do | |||
if section.Type == "EArea_Active" then has_active = true; break end | |||
end | |||
if has_active then | |||
table.insert(lines, "| active1_cooldown = " .. cd) | |||
else | |||
table.insert(lines, "| item_stat_cooldown = " .. cd) | |||
end | end | ||
end | end | ||
end | end | ||
if not chargeup_found_in_sections and item.AbilityChargeUpTime then | |||
local cu = format_stat(item_name, "AbilityChargeUpTime", true, debug) | |||
if cu then | |||
table.insert(lines, "| item_stat_chargeup = " .. cu) | |||
end | |||
end | |||
) | |||
---------------------------------------------------------------------- | |||
-- Components | |||
---------------------------------------------------------------------- | |||
for i = 1, 2 do | |||
local comp = ItemData.get_component_name { | |||
args = { item_name, i } | |||
} | |||
if comp and comp ~= "" then | |||
table.insert(lines, string.format("| component%d_name = %s", i, comp)) | |||
end | |||
end | |||
---------------------------------------------------------------------- | |||
-- Is Component Of (your tier-sorted fix) | |||
---------------------------------------------------------------------- | |||
local parents = get_component_of_sorted(item_name) | |||
for i, key in ipairs(parents) do | |||
local parent_name = data[key] and data[key]["Name"] or "" | |||
if parent_name ~= "" then | |||
table.insert(lines, string.format("| iscomponentof%d_name = %s", i, parent_name)) | |||
end | |||
end | |||
---------------------------------------------------------------------- | |||
-- Sounds (always manual, passed as sound1= sound2= etc.) | |||
---------------------------------------------------------------------- | |||
for key, value in pairs(frame.args) do | |||
local index = tostring(key):match("^sound(%d+)$") | |||
if index and value ~= "" then | |||
table.insert(lines, string.format("| sound%s = %s", index, value)) | |||
end | |||
end | |||
---------------------------------------------------------------------- | |||
table.insert(lines, "}}") | |||
local output = table.concat(lines, "\n") | |||
if debug == 1 or debug == 2 then | |||
return output | |||
end | |||
return mw.getCurrentFrame():preprocess(output) | |||
end | end | ||
return p | return p | ||