Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

Module:C: Difference between revisions

From Teriock
Content deleted Content added
// via Wikitext Extension for VSCode
Tag: Reverted
// via Wikitext Extension for VSCode
Tag: Manual revert
Line 1: Line 1:
local p = {}
local p = {}


local p = {}
-- ========= Category helpers =========

-- Memoize category lookups within a single parse
local _catMemo = {}

-- Resolve a raw ability name to a proper Title in the Ability namespace
local function resolveAbilityTitle(raw)
if not raw or raw == "" then return nil end

-- If caller already gave a full title and it exists, use it as-is
local t = mw.title.new(raw)
if t and t.exists then return t end

-- Otherwise, try in the Ability namespace by id
local ns = mw.site.namespaces and mw.site.namespaces["Ability"]
if ns and ns.id then
local t2 = mw.title.new(raw, ns.id) -- "Ability:raw"
if t2 and t2.exists then return t2 end
end

-- Fallback: explicit "Ability:" prefix
if not mw.ustring.find(raw, "^Ability:") then
local t3 = mw.title.new("Ability:" .. raw)
if t3 and t3.exists then return t3 end
end

return nil
end

-- Robust category check using parent categories (handles categories added via templates)
local function pageInCategory(pagetitle, cat)
if not pagetitle or pagetitle == "" or not cat or cat == "" then return false end

local memoKey = pagetitle .. "@@" .. cat
if _catMemo[memoKey] ~= nil then
return _catMemo[memoKey]
end

local title = resolveAbilityTitle(pagetitle)
if not title then
_catMemo[memoKey] = false
return false
end

-- Follow redirects
if title.isRedirect and title.redirectTarget and title.redirectTarget.exists then
title = title.redirectTarget
end

local parents = title:getParentCategories()
if not parents then
_catMemo[memoKey] = false
return false
end

local want = "Category:" .. cat
for parentCat in pairs(parents) do
if parentCat == want then
_catMemo[memoKey] = true
return true
end
end

_catMemo[memoKey] = false
return false
end

-- ========= Rendering helper =========

-- Build AE template from an ability object
local function aeTemplate(a)
local parts = {"{{AE|", a.name}
if a.limited and a.limited ~= "" then table.insert(parts, "|limited=" .. a.limited) end
if a.improved and a.improved ~= "" then table.insert(parts, "|improved=" .. a.improved) end
if a.gifted then table.insert(parts, "|gifted=1") end
if a.adept then table.insert(parts, "|adept=1") end
table.insert(parts, "}}")
return table.concat(parts)
end

-- ========= Main entry =========


function p.c(frame)
function p.c(frame)
Line 87: Line 7:
local rawAbilities = args['a'] or ''
local rawAbilities = args['a'] or ''


-- Parse abilities into a structured table
-- Prepopulate defaults
local abilities = {}
local abilities = {}
local indexByName = {}

local function addAbility(obj, isDefault)
local key = mw.text.trim(obj.name or "")
if key == "" then return end
obj.name = key
if indexByName[key] then
abilities[indexByName[key]] = obj
else
table.insert(abilities, obj)
indexByName[key] = #abilities
end
if isDefault then abilities[indexByName[key]]._isDefault = true end
end

local function removeByName(name)
local i = indexByName[name]
if not i then return end
table.remove(abilities, i)
-- rebuild index
indexByName = {}
for idx, ab in ipairs(abilities) do
indexByName[ab.name] = idx
end
end

-- Defaults
local defaults = {
"Normal Intelligence",
"Normal Strength",
"Normal Movement",
"Normal Sneak",
"Normal Perception",
}
for _, n in ipairs(defaults) do
addAbility({
name = n, limited = "", improved = "", gifted = false, adept = false
}, true)
end

-- Parse incoming abilities
local incomingNames = {} -- keep a list to check categories against (only incoming)
for ability in mw.text.gsplit(rawAbilities, ";;", true) do
for ability in mw.text.gsplit(rawAbilities, ";;", true) do
ability = mw.text.trim(ability)
ability = mw.text.trim(ability)
Line 138: Line 16:
local gifted, adept = false, false
local gifted, adept = false, false


-- flags
if mw.ustring.find(ability, "%-g%-") then gifted = true end
if mw.ustring.find(ability, "%-g%-") then gifted = true end
if mw.ustring.find(ability, "%-a%-") then adept = true end
if mw.ustring.find(ability, "%-a%-") then adept = true end


-- Notes: order-insensitive and tolerant of following markers
-- notes (order-insensitive, tolerate any following marker)
local limitedMatch = mw.ustring.match(ability, "%-l%-(.-)(%-[liag]%-|$)")
local limitedMatch = mw.ustring.match(ability, "%-l%-(.-)(%-[liag]%-|$)")
or mw.ustring.match(ability, "%-l%-(.+)$")
or mw.ustring.match(ability, "%-l%-(.+)$")
Line 150: Line 29:
if improvedMatch then improved = mw.text.trim(improvedMatch) end
if improvedMatch then improved = mw.text.trim(improvedMatch) end


-- Clean name (strip markers and payloads/flags)
-- clean name (strip markers + their payloads/flags)
name = mw.ustring.gsub(name, "%-l%-.+", "")
name = mw.ustring.gsub(name, "%-l%-.+", "")
name = mw.ustring.gsub(name, "%-i%-.+", "")
name = mw.ustring.gsub(name, "%-i%-.+", "")
Line 157: Line 36:
name = mw.text.trim(name)
name = mw.text.trim(name)


table.insert(abilities, {
addAbility({
name = name,
name = name,
limited = limited,
limited = limited,
Line 163: Line 42:
gifted = gifted,
gifted = gifted,
adept = adept
adept = adept
}, false)
})

table.insert(incomingNames, name)
end
end
end
end


-- Sort alphabetically by name (stable)
-- If any incoming ability is in the mapped category, remove the corresponding default
table.sort(abilities, function(a, b) return a.name < b.name end)
local catMap = {
["Normal Intelligence"] = "INT setting abilities",
["Normal Strength"] = "STR setting abilities",
["Normal Movement"] = "MOV setting abilities",
["Normal Sneak"] = "SNK setting abilities",
["Normal Perception"] = "PER setting abilities",
}


-- Build the out string (NO visible separators between AE templates)
local shouldRemove = {
local transclusions = {}
["Normal Intelligence"] = false,
for _, a in ipairs(abilities) do
["Normal Strength"] = false,
local t = {"{{AE|", a.name}
["Normal Movement"] = false,
if a.limited ~= "" then table.insert(t, "|limited=" .. a.limited) end
["Normal Sneak"] = false,
if a.improved ~= "" then table.insert(t, "|improved=" .. a.improved) end
["Normal Perception"] = false,
if a.gifted then table.insert(t, "|gifted=1") end
}
if a.adept then table.insert(t, "|adept=1") end

table.insert(t, "}}")
for _, inName in ipairs(incomingNames) do
table.insert(transclusions, table.concat(t))
for normalName, cat in pairs(catMap) do
if not shouldRemove[normalName] and pageInCategory(inName, cat) then
shouldRemove[normalName] = true
end
end
end
end


-- Use "" (or "\n") so no semicolons are emitted
for normalName, flag in pairs(shouldRemove) do
local inner = table.concat(transclusions, "") -- <- changed from ";; "
if flag then
removeByName(normalName)
end
end


-- Sort alphabetically by name (case-insensitive, stable on original)
table.sort(abilities, function(a, b)
local aa, bb = mw.ustring.lower(a.name), mw.ustring.lower(b.name)
if aa == bb then return a.name < b.name end
return aa < bb
end)

-- Assemble output and expand the AE templates right here
local outParts = {}
for _, a in ipairs(abilities) do
table.insert(outParts, aeTemplate(a))
end
local inner = table.concat(outParts, "")
local out = '<div class="expandable-table"><div>' .. frame:preprocess(inner) .. '</div></div>'
local out = '<div class="expandable-table"><div>' .. frame:preprocess(inner) .. '</div></div>'

return out
return out
end
end

Revision as of 06:04, 12 August 2025

Documentation for this module may be created at Module:C/doc

local p = {}

local p = {}

function p.c(frame)
	local args = frame.args
	local rawAbilities = args['a'] or ''

	-- Parse abilities into a structured table
	local abilities = {}
	for ability in mw.text.gsplit(rawAbilities, ";;", true) do
		ability = mw.text.trim(ability)
		if ability ~= "" then
			local name = ability
			local limited, improved = "", ""
			local gifted, adept = false, false

			-- flags
			if mw.ustring.find(ability, "%-g%-") then gifted = true end
			if mw.ustring.find(ability, "%-a%-") then adept = true end

			-- notes (order-insensitive, tolerate any following marker)
			local limitedMatch = mw.ustring.match(ability, "%-l%-(.-)(%-[liag]%-|$)")
				or mw.ustring.match(ability, "%-l%-(.+)$")
			if limitedMatch then limited = mw.text.trim(limitedMatch) end

			local improvedMatch = mw.ustring.match(ability, "%-i%-(.-)(%-[liag]%-|$)")
				or mw.ustring.match(ability, "%-i%-(.+)$")
			if improvedMatch then improved = mw.text.trim(improvedMatch) end

			-- clean name (strip markers + their payloads/flags)
			name = mw.ustring.gsub(name, "%-l%-.+", "")
			name = mw.ustring.gsub(name, "%-i%-.+", "")
			name = mw.ustring.gsub(name, "%-g%-", "")
			name = mw.ustring.gsub(name, "%-a%-", "")
			name = mw.text.trim(name)

			table.insert(abilities, {
				name = name,
				limited = limited,
				improved = improved,
				gifted = gifted,
				adept = adept
			})
		end
	end

	-- Sort alphabetically by name (stable)
	table.sort(abilities, function(a, b) return a.name < b.name end)

	-- Build the out string (NO visible separators between AE templates)
	local transclusions = {}
	for _, a in ipairs(abilities) do
		local t = {"{{AE|", a.name}
		if a.limited ~= ""  then table.insert(t, "|limited=" .. a.limited) end
		if a.improved ~= "" then table.insert(t, "|improved=" .. a.improved) end
		if a.gifted        then table.insert(t, "|gifted=1") end
		if a.adept         then table.insert(t, "|adept=1") end
		table.insert(t, "}}")
		table.insert(transclusions, table.concat(t))
	end

	-- Use "" (or "\n") so no semicolons are emitted
	local inner = table.concat(transclusions, "")  -- <- changed from ";; "

	local out = '<div class="expandable-table"><div>' .. frame:preprocess(inner) .. '</div></div>'
	return out
end

return p