Editing Module:AbilityTable/ComplexRenderersGive 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: | ||
-- Complex cell renderers for AbilityTable. | -- Complex cell renderers for AbilityTable. | ||
local Lists = require("Module:AbilityTable/Lists") | local Lists = require("Module:AbilityTable/Lists") | ||
local GameData = require("Module:GameData") | |||
-- ============================================================ | -- ============================================================ | ||
| Line 47: | Line 48: | ||
end | end | ||
-- | -- Returns true if prop exists with a non-zero value anywhere in the record | ||
-- | -- excluding the Upgrades key. | ||
local function | local function is_base_property(record, prop) | ||
for k, v in pairs(record) do | |||
for | if k == "Upgrades" then | ||
-- skip | |||
elseif k == prop then | |||
if type(v) == "table" then v = v["Value"] end | |||
if v ~= nil and v ~= 0 and v ~= "" and v ~= "0" and v ~= "0m" then | |||
return true | |||
-- | |||
if type( | |||
end | end | ||
if v | elseif type(v) == "table" then | ||
if is_base_property(v, prop) then return true end | |||
elseif type(v) == "string" then | |||
if v == prop then return true end | |||
end | end | ||
end | end | ||
return false | |||
end | end | ||
-- | -- ============================================================ | ||
-- | -- Debuff short names (used by RenderDebuff) | ||
-- | -- ============================================================ | ||
local debuff_short_name = { | |||
["StunDuration"] = "stun", | |||
["SleepDuration"] = "sleep", | |||
["SilenceDuration"] = "silence", | |||
["SilenceDebuff"] = "silence", | |||
["PetrifyDuration"] = "petrify", | |||
["HexDuration"] = "hex", | |||
["ImmobilizeDuration"] = "immobilize", | |||
["LiftDuration"] = "lift", | |||
["HoldInPlaceDuration"] = "immobilize", | |||
["RestrictionDuration"] = "immobilize", | |||
["SlowPercent"] = "slow", | |||
["EnemySlowPct"] = "slow", | |||
["MoveSlowPercent"] = "slow", | |||
["GroundDashReductionPercent"] = "dash slow", | |||
["SlowDuration"] = "slow", | |||
["MaxSlowDuration"] = "slow", | |||
["BulletDamageAmpDuration"] = "bullet amp", | |||
["BulletResistReduction"] = "bullet resist reduction", | |||
["TimeSlowDuration"] = "time slow", | |||
["LateCheckoutStun"] = "stun", | |||
["BurnDuration"] = "burn", | |||
["BleedDuration"] = "bleed", | |||
["VenomDuration"] = "venom", | |||
["TossDuration"] = "knockback", | |||
} | |||
-- ============================================================ | -- ============================================================ | ||
-- Generic renderer for a single numeric stat | -- Generic renderer for a single numeric stat column. | ||
-- | -- col_name: column header string | ||
-- | -- fields: ordered list of property names (first non-zero wins) | ||
-- | -- suffix: string appended to values in cell and upgrade notes | ||
-- ============================================================ | -- ============================================================ | ||
local function | local function val_stat_cells(ability, col_name, fields, suffix) | ||
suffix = suffix or "" | suffix = suffix or "" | ||
local cells = {} | local cells = {} | ||
| Line 151: | Line 137: | ||
table.insert(notes, "T" .. i .. " upgrade increases " | table.insert(notes, "T" .. i .. " upgrade increases " | ||
.. col_name:lower() .. " by " .. delta .. suffix | .. col_name:lower() .. " by " .. delta .. suffix | ||
.. " (total " .. total .. suffix .. ")") | .. " (total " .. total .. suffix .. ").") | ||
running = total | running = total | ||
end | end | ||
| Line 169: | Line 155: | ||
local p = {} | local p = {} | ||
-- | -- Charges: shows base charges, time between charges, and upgrade notes. | ||
function p.RenderCharges(ability) | function p.RenderCharges(ability) | ||
local base_charges = unwrap_value(ability["AbilityCharges"]) | local base_charges = unwrap_value(ability["AbilityCharges"]) | ||
local base_cooldown = | local base_cooldown = ability["AbilityCooldownBetweenCharge"] | ||
local has_base = base_charges ~= nil and base_charges > 0 | local has_base = base_charges ~= nil and base_charges > 0 | ||
local upgrade_info = find_in_upgrades(ability, "AbilityCharges") | local upgrade_info = find_in_upgrades(ability, "AbilityCharges") | ||
| Line 237: | Line 189: | ||
local charge_str = n .. " charge" .. (n ~= 1 and "s" or "") | local charge_str = n .. " charge" .. (n ~= 1 and "s" or "") | ||
if has_base then | if has_base then | ||
table.insert(note_parts, "+" .. charge_str .. " on T" .. upgrade_info.tier .. " upgrade") | table.insert(note_parts, "+" .. charge_str .. " on T" .. upgrade_info.tier .. " upgrade.") | ||
else | else | ||
local cd_str = upgrade_cooldown and (upgrade_cooldown .. "s") or "unknown" | local cd_str = upgrade_cooldown and (upgrade_cooldown .. "s") or "unknown" | ||
table.insert(note_parts, "Becomes charged on T" .. upgrade_info.tier | table.insert(note_parts, "Becomes charged on T" .. upgrade_info.tier | ||
.. " upgrade with " .. charge_str | .. " upgrade with " .. charge_str | ||
.. " and " .. cd_str .. " time between charges") | .. " and " .. cd_str .. " time between charges.") | ||
end | end | ||
end | end | ||
if ability["AbilityChargesConditionally"] ~= nil then | if ability["AbilityChargesConditionally"] ~= nil then | ||
table.insert(note_parts, "Has a conditional charge") | table.insert(note_parts, "Has a conditional charge.") | ||
end | end | ||
if #note_parts > 0 then | if #note_parts > 0 then | ||
| Line 255: | Line 207: | ||
end | end | ||
-- | -- Melee: shows light melee scaling and upgrade notes. | ||
function p.RenderMelee(ability) | function p.RenderMelee(ability) | ||
local | local function find_base_melee_scale(ability) | ||
for _, v in pairs(ability) do | |||
if type(v) == "table" then | |||
local scale = v["Scale"] | |||
if type(scale) == "table" | |||
and scale["Type"] == "melee" | |||
and type(scale["Value"]) == "number" | |||
and scale["Value"] > 0 | |||
then | |||
return scale["Value"] | |||
end | |||
end | end | ||
end | end | ||
return nil | |||
end | end | ||
local cells = {} | local cells = {} | ||
local base_scale = find_base_melee_scale(ability) | |||
if base_scale then | if base_scale then cells["Light Melee Scaling"] = "×" .. base_scale end | ||
local upgrades = ability["Upgrades"] | local upgrades = ability["Upgrades"] | ||
if type(upgrades) ~= "table" then return cells end | if type(upgrades) ~= "table" then return cells end | ||
local notes = {} | local notes = {} | ||
for i, tier in ipairs(upgrades) do | for i, tier in ipairs(upgrades) do | ||
if type(tier) == "table" then | if type(tier) == "table" then | ||
for _, v in pairs(tier) do | |||
for | |||
if type(v) == "table" then | if type(v) == "table" then | ||
local scale = v["Scale"] | local scale = v["Scale"] | ||
if type(scale) == "table" and | if type(scale) == "table" and type(scale["Value"]) == "number" then | ||
local stype = scale["Type"] | |||
local delta = scale["Value"] | |||
if stype == "heavy_melee" and delta > 0 then | |||
table.insert(notes, "T" .. i .. " upgrade changes scaling to ×" | |||
.. delta .. " of Heavy Melee damage.") | |||
elseif stype == "melee" and delta ~= 0 and base_scale then | |||
local final = math.floor((base_scale + delta) * 1000 + 0.5) / 1000 | |||
table.insert(notes, "T" .. i .. " upgrade adds ×" | |||
.. delta .. " scaling (total ×" .. final .. ").") | |||
end | end | ||
end | end | ||
end | end | ||
end | end | ||
end | |||
end | |||
if #notes > 0 then cells["Notes"] = table.concat(notes, " ") end | |||
return cells | |||
end | |||
-- HealReduce: shows heal reduction percentage and upgrade notes. | |||
function p.RenderHealReduce(ability) | |||
local cells = {} | |||
local notes = {} | |||
local stat_props = Lists.props["healreduce"] | |||
local first_pct, first_tier, first_is_disable = nil, nil, false | |||
for _, prop in ipairs(stat_props) do | |||
local v = unwrap_value(ability[prop]) | |||
if v and v ~= 0 then | |||
if prop == "DisableHealing" then | |||
first_is_disable = true | |||
else | |||
first_pct = math.abs(v) | |||
if | |||
end | end | ||
break | |||
end | end | ||
end | end | ||
if not first_pct and not first_is_disable then | |||
if | local upgrades = ability["Upgrades"] | ||
if type(upgrades) == "table" then | |||
for i, tier in ipairs(upgrades) do | |||
if type(tier) == "table" then | |||
for _, prop in ipairs(stat_props) do | |||
local v = unwrap_value(tier[prop]) | |||
if v and v ~= 0 then | |||
if prop == "DisableHealing" then | |||
first_is_disable, first_tier = true, i | |||
else | |||
first_pct, first_tier = math.abs(v), i | |||
end | |||
break | |||
end | |||
end | |||
end | |||
break | if first_pct or first_is_disable then break end | ||
end | end | ||
end | end | ||
end | end | ||
if first_is_disable then | |||
cells["Heal Reduction"] = "100%" .. (first_tier and " (T" .. first_tier .. ")" or "") | |||
elseif first_pct then | |||
if | cells["Heal Reduction"] = tostring(first_pct) .. "%" .. (first_tier and " (T" .. first_tier .. ")" or "") | ||
cells[" | |||
cells[" | |||
end | end | ||
local upgrades = ability["Upgrades"] | local upgrades = ability["Upgrades"] | ||
if type(upgrades) | if type(upgrades) == "table" then | ||
local running = first_pct | |||
local first_pct_seen = (first_tier == nil) | |||
for i, tier in ipairs(upgrades) do | |||
if type(tier) == "table" then | |||
if first_tier == i then | |||
first_pct_seen = true | |||
if first_pct then running = first_pct end | |||
elseif first_pct_seen then | |||
local delta = nil | |||
for _, prop in ipairs(stat_props) do | |||
if prop ~= "DisableHealing" then | |||
local v = unwrap_value(tier[prop]) | |||
if v and v ~= 0 then delta = math.abs(v); break end | |||
end | |||
end | |||
if delta then | |||
local total = (running or 0) + delta | |||
table.insert(notes, "T" .. i .. " upgrade increases heal reduction by " | |||
.. delta .. "% (total " .. total .. "%).") | |||
running = total | |||
end | end | ||
end | end | ||
| Line 401: | Line 332: | ||
end | end | ||
if #notes > 0 then cells["Notes"] = table.concat(notes, " ") end | |||
return cells | return cells | ||
end | end | ||
-- | -- Debuff: shared by debuffresist and dispelmagic. | ||
-- | -- Outputs upgrade-tier notes for debuffs added only in upgrades (not at base). | ||
function p. | function p.RenderDebuff(ability, stat) | ||
local | local stat_props = Lists.props[stat] | ||
local | local cells = {} | ||
-- | -- If any prop exists in base, this is a base-level debuff — no note needed. | ||
for | for _, prop in ipairs(stat_props) do | ||
if | if is_base_property(ability, prop) then | ||
return cells | |||
end | end | ||
end | end | ||
| Line 434: | Line 352: | ||
if type(upgrades) ~= "table" then return cells end | if type(upgrades) ~= "table" then return cells end | ||
local | local tier_debuffs = {} | ||
local seen_names = {} | |||
for i, tier in ipairs(upgrades) do | for i, tier in ipairs(upgrades) do | ||
if type(tier) == "table" then | if type(tier) == "table" then | ||
local | local names_this_tier = {} | ||
local seen_this_tier = {} | |||
for | for _, prop in ipairs(stat_props) do | ||
if type(v) == "table" then | local v = tier[prop] | ||
if v ~= nil then | |||
if | if type(v) == "table" then v = v["Value"] end | ||
if v ~= nil and v ~= 0 and v ~= "" and v ~= "0" and v ~= "0m" then | |||
local short = debuff_short_name[prop] or "debuff" | |||
if not seen_this_tier[short] then | |||
seen_this_tier[short] = true | |||
if not seen_names[short] then | |||
seen_names[short] = true | |||
table.insert(names_this_tier, short) | |||
end | |||
end | |||
end | end | ||
end | end | ||
end | end | ||
if #names_this_tier > 0 then | |||
if | tier_debuffs[i] = names_this_tier | ||
end | end | ||
end | end | ||
end | end | ||
local tier_indices = {} | |||
for i in pairs(tier_debuffs) do table.insert(tier_indices, i) end | |||
table.sort(tier_indices) | |||
local parts = {} | |||
for _, i in ipairs(tier_indices) do | |||
table.insert(parts, "T" .. i .. " " .. table.concat(tier_debuffs[i], " + ")) | |||
end | end | ||
if #parts > 0 then cells["Notes"] = table.concat(parts, ", ") end | |||
return cells | return cells | ||
end | end | ||
-- | -- MoveSlow: shows move slow percentage and caster slow note if present. | ||
function p.RenderMoveSlow(ability) | function p.RenderMoveSlow(ability) | ||
local cells = | local cells = val_stat_cells(ability, "Move Slow", Lists.props["moveslow"], "%") | ||
local channel_slow = unwrap_value(ability["ChannelSlowPercent"]) | local channel_slow = unwrap_value(ability["ChannelSlowPercent"]) | ||
if channel_slow and channel_slow ~= 0 then | if channel_slow and channel_slow ~= 0 then | ||
local note = "Also slows the caster for " .. math.abs(channel_slow) .. "%" | local note = "Also slows the caster for " .. math.abs(channel_slow) .. "%." | ||
cells["Notes"] = cells["Notes"] and (cells["Notes"] .. "; " .. note) or note | cells["Notes"] = cells["Notes"] and (cells["Notes"] .. "; " .. note) or note | ||
end | end | ||
| Line 502: | Line 407: | ||
end | end | ||
-- | -- DashSlow: shows dash slow percentage, auto-detects caster self-slow. | ||
- | |||
function p.RenderDashSlow(ability) | function p.RenderDashSlow(ability) | ||
local stat_props = Lists. | local stat_props = Lists.props["dashslow"] | ||
local props = {} | local props = {} | ||
for _, | for _, p in ipairs(stat_props) do table.insert(props, p) end | ||
local caster_dash_slow = nil | local caster_dash_slow = nil | ||
| Line 548: | Line 451: | ||
end | end | ||
local cells = | local cells = val_stat_cells(ability, "Dash Slow", props, "%") | ||
if caster_dash_slow then | if caster_dash_slow then | ||
local note = "Gives " .. caster_dash_slow .. "% dash slow for the caster" | local note = "Gives " .. caster_dash_slow .. "% dash slow for the caster." | ||
cells["Notes"] = cells["Notes"] and (cells["Notes"] .. "; " .. note) or note | cells["Notes"] = cells["Notes"] and (cells["Notes"] .. "; " .. note) or note | ||
end | end | ||
return cells | return cells | ||
end | end | ||
return p | return p | ||