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
 
(73 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 = 'Mod 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 93: Line 98:
end
end


local allGames = string.format('[[%s|%s]]', 'Games', 'All Games')
local allGames = string.format("[[%s|%s]]", "Games", "All Games")
table.insert(subpageTable, allGames)
table.insert(subpageTable, allGames)
 
removeGameFromLabels(subpageTable, page)
removeGameFromLabels(subpageTable, page)
renameLabels(subpageTable)
renameLabels(subpageTable)
Line 101: Line 106:


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
end
Line 113: Line 118:
local function formatCellValue(value)
local function formatCellValue(value)
if value == nil then
if value == nil then
return ''
return ""
end
end


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


Line 123: Line 128:
end
end


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


return tostring(value)
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
end


local function renderModTable(content)
local function renderModTable(content)
local wrapper = mw.html.create('div')
local wrapper = mw.html.create("div")
wrapper
wrapper:attr("id", "assets")
:attr('id', 'assets')
 
local tableNode = wrapper:tag("table")
local tableNode = wrapper:tag('table')
tableNode:addClass("smwtable-clean")
tableNode
 
:addClass('wikitable')
for _, row in ipairs(content or {}) do
:addClass('sortable')
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


local header = tableNode:tag('tr')
return wrapper
end


header:tag('th'):wikitext('Asset')
local function renderClanTable(content)
header:tag('th'):wikitext('Type')
local wrapper = mw.html.create("div")
header:tag('th'):wikitext('Subtype')
wrapper:attr("id", "clans")
header:tag('th'):wikitext('Mode')
 
header:tag('th'):wikitext('Developer')
local tableNode = wrapper:tag("table")
header:tag('th'):wikitext('Released')
tableNode:addClass("smwtable-clean")
header:tag('th'):wikitext('Status')
header:tag('th'):wikitext('Description')
header:tag('th'):wikitext('Version')


for _, row in ipairs(content or {}) do
for _, row in ipairs(content or {}) do
local tr = tableNode:tag('tr')
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]))
tr:tag("td"):wikitext(formatCellValue(row[1])):addClass(statusClass)
tr:tag('td'):wikitext(formatCellValue(row['Category']))
tr:tag("td"):wikitext(formatCellValue(row["Related game"]))
tr:tag('td'):wikitext(
tr:tag("td"):wikitext(formatCellValue(row["Community links"]))
  formatCellValue(row['Has map type'])
tr:tag("td"):wikitext(formatCellValue(row["Has short description"]))
, formatCellValue(row['Has skin type'])
tr:tag("td"):wikitext(hasStatus)
, 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']))
end
end


Line 174: Line 390:


function p.Mods(frame)
function p.Mods(frame)
local data     = getCurrentGame()
local data = getCurrentGame()
local game     = data.game or '+'
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 .. "]]",
'?Category',
"?Related game",
'?Has mod type',
"?Category",
'?Has map type',
"?Has mod type",
'?Has skin type',
"?Has map type",
'?Has utility type',
"?Has skin type",
'?Has game mode',
"?Has utility type",
'?Has developer',
"?Has game mode",
'?Has release date#-F[Y]',
"?Has developer",
'?Has status',  
"?Has release date#-F[Y]",
'?Has short description',
"?Has status",
'?Works on game version',
"?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))
html:node(renderModTable(content))
 
return html
return html
end
end


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


local html = mw.html.create()
local html = mw.html.create()
html:node(renderModTable(content))
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 = 'Star Trek: Voyager - Elite Force'
local args = frame.args
local hasStatus = args.hasStatus or "+"


local query = {
local query = {
'[[Category:Mods||Maps||Skins||Utilities]][[Related game::' .. page .. ']]',
"[[Category:Communities]][[Has status::" .. hasStatus .. "]]",
'?Related game',
"?Related game",
'?Has asset type',
"?Community links",
'?Has game mode',
"?Has short description",
'?Has developer',
"?Has status",
'?Has release date#-F[Y]',
'?Has status',
'?Has short description',
'?Works on game version',
}
}
 
local content = mw.smw.ask(query)
local content = mw.smw.ask(query)
 
return content
--[[
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