PC Gaming Shelter
An archive dedicated to preserving PC Gaming history and more

Module:Universe: Difference between revisions

From PC Gaming Shelter
No edit summary
No edit summary
 
(88 intermediate revisions by the same user not shown)
Line 1: Line 1:
local utils = require( 'Module:Utils' )
local utils = require("Module:Utils")


local p = {}
local p = {}
Line 5: Line 5:
local function getCurrentGame()
local function getCurrentGame()
local title = mw.title.getCurrentTitle()
local title = mw.title.getCurrentTitle()
local game = title.rootText
local game = title.rootText
local resource = title.subpageText
local resource = title.subpageText
local data = {
local data = {
['game'] = game,
["game"] = game,
['resource'] = resource,
["resource"] = resource,
}
}
return data
return data
Line 15: Line 15:


local function setDisplayTitle(frame, title)
local function setDisplayTitle(frame, title)
local displayTitle = frame:preprocess(string.format('{{DISPLAYTITLE:%s|noreplace, noerror}}', title))
local displayTitle = frame:preprocess(string.format("{{DISPLAYTITLE:%s|noreplace, noerror}}", title))
return displayTitle
return displayTitle
end
end


local function escapePattern(value)
local function escapePattern(value)
return value:gsub('([^%w])', '%%%1')
return value:gsub("([^%w])", "%%%1")
end
end


Line 27: Line 27:


for index, link in ipairs(results) do
for index, link in ipairs(results) do
results[index] = link:gsub(
results[index] = link:gsub("|" .. gamePattern .. "%s+", "|")
'|' .. gamePattern .. '%s+',
'|'
)
end
end


Line 38: Line 35:
local function renameLabels(results)
local function renameLabels(results)
local labels = {
local labels = {
Mods = 'Mods Archive',
Mods = "Mod Archive",
Players = 'Player Roster',
Players = "Player Roster",
Clans = 'Clan History',
Clans = "Clan History",
}
}


for i, link in ipairs(results) do
for i, link in ipairs(results) do
results[i] = link:gsub('|([^%]]+)%]%]', function(label)
local target = link:match("^%[%[([^|%]]+)")
return '|' .. (labels[label] or label) .. ']]'
local subpage = target and target:match("/([^/]+)$")
end)
local label = subpage and labels[subpage]
 
if label then
results[i] = string.format("[[%s|%s]]", target, label)
else
results[i] = link:gsub("|([^%]]+)%]%]", function(existingLabel)
return "|" .. (labels[existingLabel] or existingLabel) .. "]]"
end)
end
end
end


Line 61: Line 66:
table.sort(results, function(a, b)
table.sort(results, function(a, b)
-- Extract the final subpage name from the link target.
-- Extract the final subpage name from the link target.
local targetA = a:match('^%[%[:.-/([^/|%]]+)|')
local targetA = a:match("^%[%[:.-/([^/|%]]+)|")
local targetB = b:match('^%[%[:.-/([^/|%]]+)|')
local targetB = b:match("^%[%[:.-/([^/|%]]+)|")


local rankA = order[targetA] or math.huge
local rankA = order[targetA] or math.huge
Line 80: Line 85:
local function getSubpages(page)
local function getSubpages(page)
local subpages = mw.smw.ask({
local subpages = mw.smw.ask({
'[[Has parent page::' .. page .. ']][[Modification date::+]]'
"[[Has parent page::" .. page .. "]][[Modification date::+]]",
}) or {}
}) or {}
 
local subpageTable = {}
local subpageTable = {}
 
for _, subpage in ipairs(subpages) do
for _, subpage in ipairs(subpages) do
local link = subpage[1]
local link = subpage[1]
Line 92: Line 97:
end
end
end
end
local allGames = string.format("[[%s|%s]]", "Games", "All Games")
table.insert(subpageTable, allGames)


removeGameFromLabels(subpageTable, page)
removeGameFromLabels(subpageTable, page)
renameLabels(subpageTable)
renameLabels(subpageTable)
sortLabels(subpageTable)
sortSubpages(subpageTable)


if #subpageTable == 0 then
if #subpageTable == 0 then
return ''
return ""
end
end


local html = mw.html.create()
local html = mw.html.create()
 
local universe = html:tag('div'):attr('id', 'universeMenu'):wikitext(' | ' .. table.concat(subpageTable, ' | '))
local universe = html:tag("div"):attr("id", "universeMenu"):wikitext(" | " .. table.concat(subpageTable, " | "))
 
return tostring(html)
return tostring(html)
end
local function formatCellValue(value)
if value == nil then
return ""
end
if type(value) == "table" then
local values = {}
for _, item in ipairs(value) do
table.insert(values, tostring(item))
end
return table.concat(values, "<br>")
end
return tostring(value)
end
local function formatDownloadLinks(value)
if type(value) ~= "table" then
return formatCellValue(value)
end
local links = {}
for _, link in ipairs(value) do
local formattedLink = formatCellValue(link)
if formattedLink ~= "" then
table.insert(links, formattedLink)
end
end
return table.concat(links, "<br>")
end
local function renderModTable(content)
local wrapper = mw.html.create("div")
wrapper:attr("id", "assets")
local tableNode = wrapper:tag("table")
tableNode:addClass("smwtable-clean")
for _, row in ipairs(content or {}) do
local tr = tableNode:tag("tr")
tr:tag("td"):wikitext(formatCellValue(row[1]))
tr:tag("td"):wikitext(formatCellValue(row["Category"]))
tr:tag("td"):wikitext(
formatCellValue(row["Has map type"]),
formatCellValue(row["Has skin type"]),
formatCellValue(row["Has mod type"]),
formatCellValue(row["Has utility type"])
)
tr:tag("td"):wikitext(formatCellValue(row["Has game mode"]))
tr:tag("td"):wikitext(formatCellValue(row["Has developer"]))
tr:tag("td"):wikitext(formatCellValue(row["Has release date"]))
tr:tag("td"):wikitext(formatCellValue(row["Has status"]))
tr:tag("td"):wikitext(formatCellValue(row["Has short description"]))
tr:tag("td"):wikitext(formatCellValue(row["Works on game version"]))
tr:tag("td"):wikitext(formatDownloadLinks(row["Download links"]))
tr:tag("td"):wikitext(formatCellValue(row["Related game"]))
end
return wrapper
end
local function renderPlayerTable(content, panes)
local wrapper = mw.html.create("div")
wrapper:attr("id", "players"):attr("data-pane-columns", panes)
local tableNode = wrapper:tag("table")
tableNode:addClass("smwtable-clean")
for _, row in ipairs(content or {}) do
local clanValues = {}
local roleValues = {}
local clanAndRoleValues = {}
local member = row["Member of"]
if member then
-- SMW may return either one value or a table of values.
local memberships = type(member) == "table" and member or { member }
for _, clan in ipairs(memberships) do
local formattedClan = formatCellValue(clan)
table.insert(clanValues, string.format("<div>%s</div>", formattedClan))
local query = {
"[[-Has subobject::" .. row[1] .. "]]",
"[[Member of::" .. clan .. "]]",
"?Has role#-=role",
mainlabel = "-",
}
local results = mw.smw.ask(query)
local clanRoles = {}
local function addRoles(value)
if value == nil or value == "" then
return
end
for role in mw.text.gsplit(tostring(value), ",", true) do
local normalizedRole = mw.text.trim(role)
if normalizedRole ~= "" then
table.insert(clanRoles, formatCellValue(normalizedRole))
end
end
end
for _, resultRow in ipairs(results or {}) do
local resultRoles = resultRow.role
if type(resultRoles) == "table" then
for _, roleValue in ipairs(resultRoles) do
addRoles(roleValue)
end
else
addRoles(resultRoles)
end
end
if #clanRoles > 0 then
local roleDivs = "<div>" .. table.concat(clanRoles, "</div><div>") .. "</div>"
table.insert(roleValues, roleDivs)
table.insert(
clanAndRoleValues,
string.format("<div>%s (%s)</div>", formattedClan, table.concat(clanRoles, ", "))
)
else
table.insert(roleValues, "<div>—</div>")
table.insert(clanAndRoleValues, string.format("<div>%s</div>", formattedClan))
end
end
end
local hasStatus = row["Has status"]
local activeClass = "text-danger"
if hasStatus == "Active" then
activeClass = "text-success"
end
local tr = tableNode:tag("tr")
tr:tag("td"):wikitext(formatCellValue(row[1])):addClass(activeClass)
tr:tag("td"):wikitext(formatCellValue(row["Plays game"]))
tr:tag("td"):wikitext(table.concat(clanValues, ""))
tr:tag("td"):wikitext(table.concat(roleValues, ""))
tr:tag("td"):wikitext(table.concat(clanAndRoleValues, ""))
tr:tag("td"):wikitext(hasStatus)
end
return wrapper
end
local function renderClanPlayerTable(content, clanname)
local title = mw.title.getCurrentTitle()
local clanpage = clanname
local wrapper = mw.html.create("div")
wrapper:attr("id", "players"):attr("data-pane-columns", "3, 5")
local tableNode = wrapper:tag("table")
tableNode:addClass("smwtable-clean")
for _, row in ipairs(content or {}) do
local clanValues = {}
local roleValues = {}
local member = row["Member of"]
if member then
local memberships = { clanpage }
for _, clan in ipairs(memberships) do
local formattedClan = formatCellValue(clan)
if formattedClan == clanpage then
table.insert(clanValues, string.format("<div>%s</div>", formattedClan))
local query = {
"[[-Has subobject::" .. row[1] .. "]]",
"[[Member of::" .. clan .. "]]",
"?Has role#-=role",
mainlabel = "-",
}
local results = mw.smw.ask(query)
local clanRoles = {}
local function addRoles(value)
if value == nil or value == "" then
return
end
for role in mw.text.gsplit(tostring(value), ",", true) do
local normalizedRole = mw.text.trim(role)
if normalizedRole ~= "" then
table.insert(clanRoles, formatCellValue(normalizedRole))
end
end
end
for _, resultRow in ipairs(results or {}) do
local resultRoles = resultRow.role
if type(resultRoles) == "table" then
for _, roleValue in ipairs(resultRoles) do
addRoles(roleValue)
end
else
addRoles(resultRoles)
end
end
if #clanRoles > 0 then
local roleDivs = table.concat(clanRoles, ", ")
table.insert(roleValues, roleDivs)
else
table.insert(roleValues, "—")
end
end
local hasStatus = row["Has status"]
local activeClass = "text-danger"
if hasStatus == "Active" then
activeClass = "text-success"
end
local tr = tableNode:tag("tr")
tr:tag("td"):wikitext(formatCellValue(row[1])):addClass(activeClass)
tr:tag("td"):wikitext(formatCellValue(row["Plays game"]))
tr:tag("td"):wikitext(table.concat(clanValues, ""))
tr:tag("td"):wikitext(table.concat(roleValues, ""))
tr:tag("td"):wikitext(table.concat(roleValues, ""))
tr:tag("td"):wikitext(hasStatus)
end
end
end
return wrapper
end
local function renderClanTable(content)
local wrapper = mw.html.create("div")
wrapper:attr("id", "clans")
local tableNode = wrapper:tag("table")
tableNode:addClass("smwtable-clean")
for _, row in ipairs(content or {}) do
local hasStatus = formatCellValue(row["Has status"])
local statusClass = hasStatus == "Active" and "text-success" or "text-danger"
local tr = tableNode:tag("tr")
tr:tag("td"):wikitext(formatCellValue(row[1])):addClass(statusClass)
tr:tag("td"):wikitext(formatCellValue(row["Related game"]))
tr:tag("td"):wikitext(formatCellValue(row["Community links"]))
tr:tag("td"):wikitext(formatCellValue(row["Has short description"]))
tr:tag("td"):wikitext(hasStatus)
end
return wrapper
end
end


function p.Mods(frame)
function p.Mods(frame)
local data     = getCurrentGame()
local data = getCurrentGame()
local game     = data.game
local game = data.game or "+"
local resource = data.resource
local resource = data.resource
 
mw.smw.set({
mw.smw.set({
['Has parent page'] = game
["Has parent page"] = game,
})
})


local query = {
local query = {
'[[Category:Mods||Maps||Skins||Utilities]][[Related game::' .. game .. ']]'
"[[Category:Mods||Maps||Skins||Utilities]][[Related game::" .. game .. "]]",
"?Related game",
"?Category",
"?Has mod type",
"?Has map type",
"?Has skin type",
"?Has utility type",
"?Has game mode",
"?Has developer",
"?Has release date#-F[Y]",
"?Has status",
"?Has short description",
"?Works on game version",
"?Download links",
}
}
local content = mw.smw.ask(query)
local content = mw.smw.ask(query)
local forminput = frame:callParserFunction({ name = '#forminput', '', args = {
'form=Mod',
'query string=Mod[Related game]=' .. game,
'button text=Create or Edit Mod',
'autofocus=no'
}})


setDisplayTitle(frame, string.format('%s %s', game, resource))
--[[
local forminput = frame:callParserFunction({
name = "#forminput",
"",
args = {
"form=Mod",
"query string=Mod[Related game]=" .. game,
"button text=Create or Edit Mod",
"autofocus=no",
},
})
]]--
 
setDisplayTitle(frame, string.format("%s %s", game, resource))
 
local html = mw.html.create()
local html = mw.html.create()
html:wikitext(getSubpages(game))
html:wikitext(getSubpages(game))
html:wikitext(forminput)
-- html:wikitext(forminput)
html:node(renderModTable(content))
 
return html
end
 
function p.allMods(frame)
local query = {
"[[Category:Mods||Maps||Skins||Utilities]][[Related game::+]]",
"?Related game",
"?Category",
"?Has mod type",
"?Has map type",
"?Has skin type",
"?Has utility type",
"?Has game mode",
"?Has developer",
"?Has release date#-F[Y]",
"?Has status",
"?Has short description",
"?Works on game version",
"?Download links",
}
local content = mw.smw.ask(query)
 
local html = mw.html.create()
html:node(renderModTable(content, true))
 
return html
return html
end
end


function p.Players(frame)
function p.Players(frame)
local data     = getCurrentGame()
local data = getCurrentGame()
local game     = data.game
local game = data.game
local resource = data.resource
local resource = data.resource


mw.smw.set({
mw.smw.set({
['Has parent page'] = game
["Has parent page"] = game,
})
})


local query = {
local query = {
'[[Category:Players]][[Plays game::' .. game .. ']]'
"[[Category:Players]][[Plays game::" .. game .. "]]",
"?Plays game",
"?Member of",
"?Has status",
}
}
 
local content = mw.smw.ask(query)
local content = mw.smw.ask(query)
local forminput = frame:callParserFunction({ name = '#forminput', '', args = {
'form=Player',
'query string=Player[Plays game]=' .. game,
'button text=Create or Edit Player Entry',
'autocomplete on category=Players',
'autofocus=no'
}})


setDisplayTitle(frame, string.format('%s %s', game, resource))
--[[
local forminput = frame:callParserFunction({
name = "#forminput",
"",
args = {
"form=Player",
"query string=Player[Plays game]=" .. game,
"button text=Create or Edit Player Entry",
"autocomplete on category=Players",
"autofocus=no",
},
})
]]--
 
setDisplayTitle(frame, string.format("%s %s", game, resource))


local html = mw.html.create()
local html = mw.html.create()
html:wikitext(getSubpages(game))
html:wikitext(getSubpages(game))
html:wikitext(forminput)
-- html:wikitext(forminput)
html:node(renderPlayerTable(content, "2, 3, 5"))
 
return html
end
 
function p.allPlayers(frame)
local query = {
"[[Category:Players]][[Plays game::+]]",
"?Plays game",
"?Member of",
"?Has status",
}
 
local content = mw.smw.ask(query)
 
local html = mw.html.create()
html:node(renderPlayerTable(content, "1, 2, 3, 5"))
 
return html
end
 
function p.clanPlayers(frame)
local title = mw.title.getCurrentTitle()
local clan = title.fullText
 
local query = {
"[[Member of::" .. clan .. "]][[Modification date::+]]",
"?Plays game",
"?Member of",
"?Has status",
}
 
local content = mw.smw.ask(query)
 
local html = mw.html.create()
html:node(renderClanPlayerTable(content, clan))
 
return html
return html
end
end


function p.Clans(frame)
function p.Clans(frame)
local data     = getCurrentGame()
local data = getCurrentGame()
local game     = data.game
local game = data.game
local resource = data.resource
local resource = data.resource


mw.smw.set({
mw.smw.set({
['Has parent page'] = game
["Has parent page"] = game,
})
})


local query = {
local query = {
'[[Category:Communities]][[Related game::' .. game .. ']]'
"[[Category:Communities]][[Related game::" .. game .. "]]",
"?Related game",
"?Community links",
"?Has short description",
"?Has status",
}
}
 
local content = mw.smw.ask(query)
local content = mw.smw.ask(query)
local forminput = frame:callParserFunction({ name = '#forminput', '', args = {
'form=Community',
'query string=Community[Related game]=' .. game,
'button text=Create or Edit Community Entry',
'autocomplete on category=Communities',
'autofocus=no'
}})


setDisplayTitle(frame, string.format('%s %s', game, resource))
--[[
local forminput = frame:callParserFunction({
name = "#forminput",
"",
args = {
"form=Community",
"query string=Community[Related game]=" .. game,
"button text=Create or Edit Community Entry",
"autocomplete on category=Communities",
"autofocus=no",
},
})
]]--


local html = mw.html.create()
local html = mw.html.create()
html:wikitext(getSubpages(game))
html:wikitext(getSubpages(game))
html:wikitext(forminput)
-- html:wikitext(forminput)
html:node(renderClanTable(content))
 
return html
return html
end
end


function p.test()
function p.allClans(frame)
local page = 'Half-Life'
local args = frame.args
local hasStatus = args.hasStatus or "+"


return getSubpages(page)
local query = {
"[[Category:Communities]][[Has status::" .. hasStatus .. "]]",
"?Related game",
"?Community links",
"?Has short description",
"?Has status",
}
 
local content = mw.smw.ask(query)
 
--[[
local forminput = frame:callParserFunction({
name = "#forminput",
"",
args = {
"form=Community",
"query string=Community[Has status]=" .. hasStatus,
"button text=Create or Edit Community Entry",
"autocomplete on category=Communities",
"autofocus=no",
},
})
]]--
 
local html = mw.html.create()
-- html:wikitext(forminput)
html:node(renderClanTable(content))
 
return html
end
end


return p
return p

Latest revision as of 09:05, 11 August 2026

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

local utils = require("Module:Utils")

local p = {}

local function getCurrentGame()
	local title = mw.title.getCurrentTitle()
	local game = title.rootText
	local resource = title.subpageText
	local data = {
		["game"] = game,
		["resource"] = resource,
	}
	return data
end

local function setDisplayTitle(frame, title)
	local displayTitle = frame:preprocess(string.format("{{DISPLAYTITLE:%s|noreplace, noerror}}", title))
	return displayTitle
end

local function escapePattern(value)
	return value:gsub("([^%w])", "%%%1")
end

local function removeGameFromLabels(results, game)
	local gamePattern = escapePattern(game)

	for index, link in ipairs(results) do
		results[index] = link:gsub("|" .. gamePattern .. "%s+", "|")
	end

	return results
end

local function renameLabels(results)
	local labels = {
		Mods = "Mod Archive",
		Players = "Player Roster",
		Clans = "Clan History",
	}

	for i, link in ipairs(results) do
		local target = link:match("^%[%[([^|%]]+)")
		local subpage = target and target:match("/([^/]+)$")
		local label = subpage and labels[subpage]

		if label then
			results[i] = string.format("[[%s|%s]]", target, label)
		else
			results[i] = link:gsub("|([^%]]+)%]%]", function(existingLabel)
				return "|" .. (labels[existingLabel] or existingLabel) .. "]]"
			end)
		end
	end

	return results
end

local function sortSubpages(results)
	local order = {
		Mods = 1,
		Players = 2,
		Clans = 3,
	}

	table.sort(results, function(a, b)
		-- Extract the final subpage name from the link target.
		local targetA = a:match("^%[%[:.-/([^/|%]]+)|")
		local targetB = b:match("^%[%[:.-/([^/|%]]+)|")

		local rankA = order[targetA] or math.huge
		local rankB = order[targetB] or math.huge

		if rankA ~= rankB then
			return rankA < rankB
		end

		-- Keep all remaining subpages alphabetically ordered.
		return (targetA or a) < (targetB or b)
	end)

	return results
end

local function getSubpages(page)
	local subpages = mw.smw.ask({
		"[[Has parent page::" .. page .. "]][[Modification date::+]]",
	}) or {}

	local subpageTable = {}

	for _, subpage in ipairs(subpages) do
		local link = subpage[1]

		if link then
			table.insert(subpageTable, link)
		end
	end

	local allGames = string.format("[[%s|%s]]", "Games", "All Games")
	table.insert(subpageTable, allGames)

	removeGameFromLabels(subpageTable, page)
	renameLabels(subpageTable)
	sortSubpages(subpageTable)

	if #subpageTable == 0 then
		return ""
	end

	local html = mw.html.create()

	local universe = html:tag("div"):attr("id", "universeMenu"):wikitext(" | " .. table.concat(subpageTable, " | "))

	return tostring(html)
end

local function formatCellValue(value)
	if value == nil then
		return ""
	end

	if type(value) == "table" then
		local values = {}

		for _, item in ipairs(value) do
			table.insert(values, tostring(item))
		end

		return table.concat(values, "<br>")
	end

	return tostring(value)
end

local function formatDownloadLinks(value)
	if type(value) ~= "table" then
		return formatCellValue(value)
	end

	local links = {}

	for _, link in ipairs(value) do
		local formattedLink = formatCellValue(link)

		if formattedLink ~= "" then
			table.insert(links, formattedLink)
		end
	end

	return table.concat(links, "<br>")
end

local function renderModTable(content)
	local wrapper = mw.html.create("div")
	wrapper:attr("id", "assets")

	local tableNode = wrapper:tag("table")
	tableNode:addClass("smwtable-clean")

	for _, row in ipairs(content or {}) do
		local tr = tableNode:tag("tr")

		tr:tag("td"):wikitext(formatCellValue(row[1]))
		tr:tag("td"):wikitext(formatCellValue(row["Category"]))
		tr:tag("td"):wikitext(
			formatCellValue(row["Has map type"]),
			formatCellValue(row["Has skin type"]),
			formatCellValue(row["Has mod type"]),
			formatCellValue(row["Has utility type"])
		)
		tr:tag("td"):wikitext(formatCellValue(row["Has game mode"]))
		tr:tag("td"):wikitext(formatCellValue(row["Has developer"]))
		tr:tag("td"):wikitext(formatCellValue(row["Has release date"]))
		tr:tag("td"):wikitext(formatCellValue(row["Has status"]))
		tr:tag("td"):wikitext(formatCellValue(row["Has short description"]))
		tr:tag("td"):wikitext(formatCellValue(row["Works on game version"]))
		tr:tag("td"):wikitext(formatDownloadLinks(row["Download links"]))
		tr:tag("td"):wikitext(formatCellValue(row["Related game"]))
	end

	return wrapper
end

local function renderPlayerTable(content, panes)
	local wrapper = mw.html.create("div")
	wrapper:attr("id", "players"):attr("data-pane-columns", panes)

	local tableNode = wrapper:tag("table")
	tableNode:addClass("smwtable-clean")

	for _, row in ipairs(content or {}) do
		local clanValues = {}
		local roleValues = {}
		local clanAndRoleValues = {}

		local member = row["Member of"]

		if member then
			-- SMW may return either one value or a table of values.
			local memberships = type(member) == "table" and member or { member }

			for _, clan in ipairs(memberships) do
				local formattedClan = formatCellValue(clan)

				table.insert(clanValues, string.format("<div>%s</div>", formattedClan))

				local query = {
					"[[-Has subobject::" .. row[1] .. "]]",
					"[[Member of::" .. clan .. "]]",
					"?Has role#-=role",
					mainlabel = "-",
				}

				local results = mw.smw.ask(query)
				local clanRoles = {}

				local function addRoles(value)
					if value == nil or value == "" then
						return
					end

					for role in mw.text.gsplit(tostring(value), ",", true) do
						local normalizedRole = mw.text.trim(role)

						if normalizedRole ~= "" then
							table.insert(clanRoles, formatCellValue(normalizedRole))
						end
					end
				end

				for _, resultRow in ipairs(results or {}) do
					local resultRoles = resultRow.role

					if type(resultRoles) == "table" then
						for _, roleValue in ipairs(resultRoles) do
							addRoles(roleValue)
						end
					else
						addRoles(resultRoles)
					end
				end

				if #clanRoles > 0 then
					local roleDivs = "<div>" .. table.concat(clanRoles, "</div><div>") .. "</div>"

					table.insert(roleValues, roleDivs)

					table.insert(
						clanAndRoleValues,
						string.format("<div>%s (%s)</div>", formattedClan, table.concat(clanRoles, ", "))
					)
				else
					table.insert(roleValues, "<div>—</div>")

					table.insert(clanAndRoleValues, string.format("<div>%s</div>", formattedClan))
				end
			end
		end

		local hasStatus = row["Has status"]
		local activeClass = "text-danger"
		if hasStatus == "Active" then
			activeClass = "text-success"
		end

		local tr = tableNode:tag("tr")
		tr:tag("td"):wikitext(formatCellValue(row[1])):addClass(activeClass)
		tr:tag("td"):wikitext(formatCellValue(row["Plays game"]))
		tr:tag("td"):wikitext(table.concat(clanValues, ""))
		tr:tag("td"):wikitext(table.concat(roleValues, ""))
		tr:tag("td"):wikitext(table.concat(clanAndRoleValues, ""))
		tr:tag("td"):wikitext(hasStatus)
	end

	return wrapper
end

local function renderClanPlayerTable(content, clanname)
	local title = mw.title.getCurrentTitle()
	local clanpage = clanname

	local wrapper = mw.html.create("div")
	wrapper:attr("id", "players"):attr("data-pane-columns", "3, 5")

	local tableNode = wrapper:tag("table")
	tableNode:addClass("smwtable-clean")

	for _, row in ipairs(content or {}) do
		local clanValues = {}
		local roleValues = {}

		local member = row["Member of"]

		if member then
			local memberships = { clanpage }

			for _, clan in ipairs(memberships) do
				local formattedClan = formatCellValue(clan)

				if formattedClan == clanpage then
					table.insert(clanValues, string.format("<div>%s</div>", formattedClan))

					local query = {
						"[[-Has subobject::" .. row[1] .. "]]",
						"[[Member of::" .. clan .. "]]",
						"?Has role#-=role",
						mainlabel = "-",
					}

					local results = mw.smw.ask(query)

					local clanRoles = {}

					local function addRoles(value)
						if value == nil or value == "" then
							return
						end

						for role in mw.text.gsplit(tostring(value), ",", true) do
							local normalizedRole = mw.text.trim(role)

							if normalizedRole ~= "" then
								table.insert(clanRoles, formatCellValue(normalizedRole))
							end
						end
					end

					for _, resultRow in ipairs(results or {}) do
						local resultRoles = resultRow.role

						if type(resultRoles) == "table" then
							for _, roleValue in ipairs(resultRoles) do
								addRoles(roleValue)
							end
						else
							addRoles(resultRoles)
						end
					end

					if #clanRoles > 0 then
						local roleDivs = table.concat(clanRoles, ", ")
						table.insert(roleValues, roleDivs)
					else
						table.insert(roleValues, "—")
					end
				end

				local hasStatus = row["Has status"]
				local activeClass = "text-danger"
				if hasStatus == "Active" then
					activeClass = "text-success"
				end

				local tr = tableNode:tag("tr")
				tr:tag("td"):wikitext(formatCellValue(row[1])):addClass(activeClass)
				tr:tag("td"):wikitext(formatCellValue(row["Plays game"]))
				tr:tag("td"):wikitext(table.concat(clanValues, ""))
				tr:tag("td"):wikitext(table.concat(roleValues, ""))
				tr:tag("td"):wikitext(table.concat(roleValues, ""))
				tr:tag("td"):wikitext(hasStatus)
			end
		end
	end

	return wrapper
end

local function renderClanTable(content)
	local wrapper = mw.html.create("div")
	wrapper:attr("id", "clans")

	local tableNode = wrapper:tag("table")
	tableNode:addClass("smwtable-clean")

	for _, row in ipairs(content or {}) do
		local hasStatus = formatCellValue(row["Has status"])
		local statusClass = hasStatus == "Active" and "text-success" or "text-danger"
		local tr = tableNode:tag("tr")

		tr:tag("td"):wikitext(formatCellValue(row[1])):addClass(statusClass)
		tr:tag("td"):wikitext(formatCellValue(row["Related game"]))
		tr:tag("td"):wikitext(formatCellValue(row["Community links"]))
		tr:tag("td"):wikitext(formatCellValue(row["Has short description"]))
		tr:tag("td"):wikitext(hasStatus)
	end

	return wrapper
end

function p.Mods(frame)
	local data = getCurrentGame()
	local game = data.game or "+"
	local resource = data.resource

	mw.smw.set({
		["Has parent page"] = game,
	})

	local query = {
		"[[Category:Mods||Maps||Skins||Utilities]][[Related game::" .. game .. "]]",
		"?Related game",
		"?Category",
		"?Has mod type",
		"?Has map type",
		"?Has skin type",
		"?Has utility type",
		"?Has game mode",
		"?Has developer",
		"?Has release date#-F[Y]",
		"?Has status",
		"?Has short description",
		"?Works on game version",
		"?Download links",
	}
	local content = mw.smw.ask(query)

	--[[
	local forminput = frame:callParserFunction({
		name = "#forminput",
		"",
		args = {
			"form=Mod",
			"query string=Mod[Related game]=" .. game,
			"button text=Create or Edit Mod",
			"autofocus=no",
		},
	})
	]]--

	setDisplayTitle(frame, string.format("%s %s", game, resource))

	local html = mw.html.create()
	html:wikitext(getSubpages(game))
	-- html:wikitext(forminput)
	html:node(renderModTable(content))

	return html
end

function p.allMods(frame)
	local query = {
		"[[Category:Mods||Maps||Skins||Utilities]][[Related game::+]]",
		"?Related game",
		"?Category",
		"?Has mod type",
		"?Has map type",
		"?Has skin type",
		"?Has utility type",
		"?Has game mode",
		"?Has developer",
		"?Has release date#-F[Y]",
		"?Has status",
		"?Has short description",
		"?Works on game version",
		"?Download links",
	}
	local content = mw.smw.ask(query)

	local html = mw.html.create()
	html:node(renderModTable(content, true))

	return html
end

function p.Players(frame)
	local data = getCurrentGame()
	local game = data.game
	local resource = data.resource

	mw.smw.set({
		["Has parent page"] = game,
	})

	local query = {
		"[[Category:Players]][[Plays game::" .. game .. "]]",
		"?Plays game",
		"?Member of",
		"?Has status",
	}

	local content = mw.smw.ask(query)

	--[[
	local forminput = frame:callParserFunction({
		name = "#forminput",
		"",
		args = {
			"form=Player",
			"query string=Player[Plays game]=" .. game,
			"button text=Create or Edit Player Entry",
			"autocomplete on category=Players",
			"autofocus=no",
		},
	})
	]]--

	setDisplayTitle(frame, string.format("%s %s", game, resource))

	local html = mw.html.create()
	html:wikitext(getSubpages(game))
	-- html:wikitext(forminput)
	html:node(renderPlayerTable(content, "2, 3, 5"))

	return html
end

function p.allPlayers(frame)
	local query = {
		"[[Category:Players]][[Plays game::+]]",
		"?Plays game",
		"?Member of",
		"?Has status",
	}

	local content = mw.smw.ask(query)

	local html = mw.html.create()
	html:node(renderPlayerTable(content, "1, 2, 3, 5"))

	return html
end

function p.clanPlayers(frame)
	local title = mw.title.getCurrentTitle()
	local clan = title.fullText

	local query = {
		"[[Member of::" .. clan .. "]][[Modification date::+]]",
		"?Plays game",
		"?Member of",
		"?Has status",
	}

	local content = mw.smw.ask(query)

	local html = mw.html.create()
	html:node(renderClanPlayerTable(content, clan))

	return html
end

function p.Clans(frame)
	local data = getCurrentGame()
	local game = data.game
	local resource = data.resource

	mw.smw.set({
		["Has parent page"] = game,
	})

	local query = {
		"[[Category:Communities]][[Related game::" .. game .. "]]",
		"?Related game",
		"?Community links",
		"?Has short description",
		"?Has status",
	}

	local content = mw.smw.ask(query)

	--[[
	local forminput = frame:callParserFunction({
		name = "#forminput",
		"",
		args = {
			"form=Community",
			"query string=Community[Related game]=" .. game,
			"button text=Create or Edit Community Entry",
			"autocomplete on category=Communities",
			"autofocus=no",
		},
	})
	]]--

	local html = mw.html.create()
	html:wikitext(getSubpages(game))
	-- html:wikitext(forminput)
	html:node(renderClanTable(content))

	return html
end

function p.allClans(frame)
	local args = frame.args
	local hasStatus = args.hasStatus or "+"

	local query = {
		"[[Category:Communities]][[Has status::" .. hasStatus .. "]]",
		"?Related game",
		"?Community links",
		"?Has short description",
		"?Has status",
	}

	local content = mw.smw.ask(query)

	--[[
	local forminput = frame:callParserFunction({
		name = "#forminput",
		"",
		args = {
			"form=Community",
			"query string=Community[Has status]=" .. hasStatus,
			"button text=Create or Edit Community Entry",
			"autocomplete on category=Communities",
			"autofocus=no",
		},
	})
	]]--

	local html = mw.html.create()
	-- html:wikitext(forminput)
	html:node(renderClanTable(content))

	return html
end

return p