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: Reverted
Line 1: Line 1:
local p = {}
local p = {}


-- Memoize category lookups within a single parse
-- Simple memo table so repeated checks in one render are cheap
local _catMemo = {}
local _catMemo = {}


-- Resolve a raw ability name to a proper Title in the Ability namespace
-- Robust category check using the title object, following redirects
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
local t = mw.title.new(raw)
if t and t.exists then return t end

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

-- Fallback: explicit prefix (covers wikis where namespace lookup differs)
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)
local function pageInCategory(pagetitle, cat)
if not pagetitle or pagetitle == "" then return false end
if not pagetitle or pagetitle == "" or not cat or cat == "" then return false end

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


local title = mw.title.new(pagetitle)
local title = resolveAbilityTitle(pagetitle)
if not title or not title.exists then
if not title then
_catMemo[key] = false
_catMemo[memoKey] = false
return false
return false
end
end


-- Follow redirects
-- If this is a redirect, follow it
if title.isRedirect then
if title.isRedirect and title.redirectTarget and title.redirectTarget.exists then
local target = title.redirectTarget
title = title.redirectTarget
if target and target.exists then
title = target
end
end
end


-- parent categories is a map like ["Category:Foo"] = sortkey
local parents = title:getParentCategories()
local parents = title:getParentCategories()
if not parents then
if not parents then
_catMemo[key] = false
_catMemo[memoKey] = false
return false
return false
end
end


-- We match exact category name (without namespace)
-- Build "Category:<name>" and compare keys case-sensitively as MW does
local want = "Category:" .. cat
local want = "Category:" .. cat
for parentCat, _ in pairs(parents) do
for parentCat in pairs(parents) do
if parentCat == want then
if parentCat == want then
_catMemo[key] = true
_catMemo[memoKey] = true
return true
return true
end
end
end
end


_catMemo[key] = false
_catMemo[memoKey] = false
return false
return false
end
end



-- Build AE template from an ability object
-- Build AE template from an ability object

Revision as of 02:09, 12 August 2025

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

local p = {}

-- 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
	local t = mw.title.new(raw)
	if t and t.exists then return t end

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

	-- Fallback: explicit prefix (covers wikis where namespace lookup differs)
	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


-- 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

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

	-- Prepopulate defaults
	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
		ability = mw.text.trim(ability)
		if ability ~= "" then
			local name = ability
			local limited, improved = "", ""
			local gifted, adept = false, false

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

			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 and 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)

			addAbility({
				name = name,
				limited = limited,
				improved = improved,
				gifted = gifted,
				adept = adept
			}, false)

			table.insert(incomingNames, name)
		end
	end

	-- If any incoming ability is in the mapped category, remove the corresponding default
	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",
	}

	local shouldRemove = {
		["Normal Intelligence"] = false,
		["Normal Strength"]     = false,
		["Normal Movement"]     = false,
		["Normal Sneak"]        = false,
		["Normal Perception"]   = false,
	}

	for _, inName in ipairs(incomingNames) do
		for normalName, cat in pairs(catMap) do
			if not shouldRemove[normalName] and pageInCategory(inName, cat) then
				shouldRemove[normalName] = true
			end
		end
	end

	for normalName, flag in pairs(shouldRemove) do
		if flag then
			removeByName(normalName)
		end
	end

	-- Sort alphabetically by name (case-insensitive, but 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>'

	return out
end

return p