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
 
(32 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')
tableNode
:addClass('wikitable')
 
local header = tableNode:tag('tr')


header:tag('th'):wikitext('Game')
local tableNode = wrapper:tag("table")
header:tag('th'):wikitext('Asset')
tableNode:addClass("smwtable-clean")
header:tag('th'):wikitext('Type')
header:tag('th'):wikitext('Subtype')
header:tag('th'):wikitext('Mode')
header:tag('th'):wikitext('Developer')
header:tag('th'):wikitext('Released')
header:tag('th'):wikitext('Status')
header:tag('th'):wikitext('Description')
header:tag('th'):wikitext('Version')
header:tag('th'):wikitext('Files')


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


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


Line 176: Line 183:
end
end


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


local header = tableNode:tag('tr')
local tableNode = wrapper:tag("table")
 
tableNode:addClass("smwtable-clean")
header:tag('th'):wikitext('Player Name')
header:tag('th'):wikitext('Games')
header:tag('th'):wikitext('Clans')
header:tag('th'):wikitext('Roles')
header:tag('th'):wikitext('Clan (Role)')
header:tag('th'):wikitext('Status')


for _, row in ipairs(content or {}) do
for _, row in ipairs(content or {}) do
Line 200: Line 195:
local clanAndRoleValues = {}
local clanAndRoleValues = {}


local member = row['Member of']
local member = row["Member of"]


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


for _, clan in ipairs(memberships) do
for _, clan in ipairs(memberships) do
local formattedClan = formatCellValue(clan)
local formattedClan = formatCellValue(clan)
 
table.insert(clanValues, string.format("<div>%s</div>", formattedClan))


table.insert(
local query = {
clanValues,
"[[-Has subobject::" .. row[1] .. "]]",
string.format('<div>%s</div>', formattedClan)
"[[Member of::" .. clan .. "]]",
)
"?Has role#-=role",
mainlabel = "-",
}


local query = {
local results = mw.smw.ask(query)
'[[-Has subobject::' .. row[1] .. ']]',
local clanRoles = {}
'[[Member of::' .. clan .. ']]',
'?Has role#-=role',
mainlabel = '-',
}


local results = mw.smw.ask(query)
local function addRoles(value)
local clanRoles = {}
if value == nil or value == "" then
return
end


local function addRoles(value)
for role in mw.text.gsplit(tostring(value), ",", true) do
if value == nil or value == '' then
local normalizedRole = mw.text.trim(role)
return
end


for role in mw.text.gsplit(tostring(value), ',', true) do
if normalizedRole ~= "" then
role = mw.text.trim(role)
table.insert(clanRoles, formatCellValue(normalizedRole))
end
end
end


if role ~= '' then
for _, resultRow in ipairs(results or {}) do
table.insert(
local resultRoles = resultRow.role
clanRoles,
formatCellValue(role)
)
end
end
end


for _, resultRow in ipairs(results or {}) do
if type(resultRoles) == "table" then
local resultRoles = resultRow.role
for _, roleValue in ipairs(resultRoles) do
addRoles(roleValue)
end
else
addRoles(resultRoles)
end
end


if type(resultRoles) == 'table' then
if #clanRoles > 0 then
for _, roleValue in ipairs(resultRoles) do
local roleDivs = "<div>" .. table.concat(clanRoles, "</div><div>") .. "</div>"
addRoles(roleValue)
end
else
addRoles(resultRoles)
end
end


if #clanRoles > 0 then
table.insert(roleValues, roleDivs)
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(
table.insert(clanAndRoleValues, string.format("<div>%s</div>", formattedClan))
clanAndRoleValues,
end
string.format(
end
'<div>%s (%s)</div>',
end
formattedClan,
table.concat(clanRoles, ', ')
)
)
else
table.insert(roleValues, '<div>—</div>')


table.insert(
local hasStatus = row["Has status"]
clanAndRoleValues,
local activeClass = "text-danger"
string.format('<div>%s</div>', formattedClan)
if hasStatus == "Active" then
)
activeClass = "text-success"
end
end
end
end
 
local tr = tableNode:tag('tr')
local tr = tableNode:tag("tr")
tr:tag('td'):wikitext(formatCellValue(row[1]))
tr:tag("td"):wikitext(formatCellValue(row[1])):addClass(activeClass)
tr:tag('td'):wikitext(formatCellValue(row['Plays game']))
tr:tag("td"):wikitext(formatCellValue(row["Plays game"]))
tr:tag('td'):wikitext(table.concat(clanValues, ''))
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(table.concat(clanAndRoleValues, ''))
tr:tag("td"):wikitext(table.concat(clanAndRoleValues, ""))
tr:tag('td'):wikitext(formatCellValue(row['Has status']))
tr:tag("td"):wikitext(hasStatus)
end
end


Line 294: Line 277:
end
end


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


local function renderClanPlayerTable(content)
local wrapper = mw.html.create("div")
wrapper:attr("id", "players"):attr("data-pane-columns", "3, 5")
local wrapper = mw.html.create('div')
wrapper
:attr('id', 'players')
local tableNode = wrapper:tag('table')
tableNode
:addClass('wikitable')
 
-- local header = tableNode:tag('tr')


-- header:tag('th'):wikitext('Player Name')
local tableNode = wrapper:tag("table")
-- header:tag('th'):wikitext('Games')
tableNode:addClass("smwtable-clean")
-- header:tag('th'):wikitext('Clans')
-- header:tag('th'):wikitext('Roles')
-- header:tag('th'):wikitext('Clan (Role)')
-- header:tag('th'):wikitext('Status')


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


local member = row['Member of']
local member = row["Member of"]


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


for _, clan in ipairs(memberships) do
for _, clan in ipairs(memberships) do
local formattedClan = formatCellValue(clan)
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 = {}


table.insert(
local function addRoles(value)
clanValues,
if value == nil or value == "" then
string.format('<div>%s</div>', formattedClan)
return
)
end


local query = {
for role in mw.text.gsplit(tostring(value), ",", true) do
'[[-Has subobject::' .. row[1] .. ']]',
local normalizedRole = mw.text.trim(role)
'[[Member of::' .. clan .. ']]',
'?Has role#-=role',
mainlabel = '-',
}


local results = mw.smw.ask(query)
if normalizedRole ~= "" then
local clanRoles = {}
table.insert(clanRoles, formatCellValue(normalizedRole))
end
end
end


local function addRoles(value)
for _, resultRow in ipairs(results or {}) do
if value == nil or value == '' then
local resultRoles = resultRow.role
return
end


for role in mw.text.gsplit(tostring(value), ',', true) do
if type(resultRoles) == "table" then
role = mw.text.trim(role)
for _, roleValue in ipairs(resultRoles) do
addRoles(roleValue)
end
else
addRoles(resultRoles)
end
end


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


for _, resultRow in ipairs(results or {}) do
local hasStatus = row["Has status"]
local resultRoles = resultRow.role
local activeClass = "text-danger"
if hasStatus == "Active" then
activeClass = "text-success"
end


if type(resultRoles) == 'table' then
local tr = tableNode:tag("tr")
for _, roleValue in ipairs(resultRoles) do
tr:tag("td"):wikitext(formatCellValue(row[1])):addClass(activeClass)
addRoles(roleValue)
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
else
addRoles(resultRoles)
end
end
end
end


if #clanRoles > 0 then
return wrapper
local roleDivs =
end
'<div>' ..
 
table.concat(clanRoles, '</div><div>') ..
local function renderClanTable(content)
'</div>'
local wrapper = mw.html.create("div")
wrapper:attr("id", "clans")


table.insert(roleValues, roleDivs)
local tableNode = wrapper:tag("table")
tableNode:addClass("smwtable-clean")


table.insert(
for _, row in ipairs(content or {}) do
clanAndRoleValues,
local hasStatus = formatCellValue(row["Has status"])
string.format(
local statusClass = hasStatus == "Active" and "text-success" or "text-danger"
'<div>%s (%s)</div>',
local tr = tableNode:tag("tr")
formattedClan,
table.concat(clanRoles, ', ')
)
)
else
table.insert(roleValues, '<div>—</div>')


table.insert(
tr:tag("td"):wikitext(formatCellValue(row[1])):addClass(statusClass)
clanAndRoleValues,
tr:tag("td"):wikitext(formatCellValue(row["Related game"]))
string.format('<div>%s</div>', formattedClan)
tr:tag("td"):wikitext(formatCellValue(row["Community links"]))
)
tr:tag("td"):wikitext(formatCellValue(row["Has short description"]))
end
tr:tag("td"):wikitext(hasStatus)
end
end
local tr = tableNode:tag('tr')
tr:tag('td'):wikitext(formatCellValue(row[1]))
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(formatCellValue(row['Has status']))
end
end


Line 414: 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 .. "]]",
'?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'
"?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'
"?Download links",
}
}
local content = mw.smw.ask(query)
local content = mw.smw.ask(query)
Line 479: Line 460:
local html = mw.html.create()
local html = mw.html.create()
html:node(renderModTable(content, true))
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',
"?Plays game",
'?Member of',
"?Member of",
'?Has status'
"?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))
html:node(renderPlayerTable(content, "2, 3, 5"))
 
return html
return html
end
end


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


local html = mw.html.create()
local html = mw.html.create()
html:node(renderPlayerTable(content))
html:node(renderPlayerTable(content, "1, 2, 3, 5"))
 
return html
return html
end
end
Line 539: Line 525:
local title = mw.title.getCurrentTitle()
local title = mw.title.getCurrentTitle()
local clan = title.fullText
local clan = title.fullText
 
local query = {
local query = {
'[[Member of::' .. clan .. ']]',
"[[Member of::" .. clan .. "]][[Modification date::+]]",
'?Plays game',
"?Plays game",
'?Has status'
"?Member of",
"?Has status",
}
}
 
local content = mw.smw.ask(query)
local content = mw.smw.ask(query)


local html = mw.html.create()
local html = mw.html.create()
html:node(renderClanPlayerTable(content))
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
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
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