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
Line 341: Line 341:


local function addRoles(value)
local function addRoles(value)
if value == nil or value == '' then
if value == nil or value == '' then
return
return
end
 
for role in mw.text.gsplit(tostring(value), ',', true) do
role = mw.text.trim(role)
 
if role ~= '' then
table.insert(
clanRoles,
formatCellValue(role)
)
end
end
end
end


for role in mw.text.gsplit(tostring(value), ',', true) do
for _, resultRow in ipairs(results or {}) do
role = mw.text.trim(role)
local resultRoles = resultRow.role


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


if #clanRoles > 0 then
table.insert(roleValues, roleDivs)
local roleDivs =
else
'<div>' ..
table.insert(roleValues, '<div>—</div>')
table.concat(clanRoles, '</div><div>') ..
end
'</div>'
 
table.insert(roleValues, roleDivs)
else
table.insert(roleValues, '<div>—</div>')
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]))
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(roleValues, ''))
tr:tag('td'):wikitext(table.concat(roleValues, ''))
tr:tag('td'):wikitext(formatCellValue(row['Has status']))
tr:tag('td'):wikitext(formatCellValue(row['Has status']))
end
end
end
end

Revision as of 18:14, 5 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
		results[i] = link:gsub('|([^%]]+)%]%]', function(label)
			return '|' .. (labels[label] or label) .. ']]'
		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 renderModTable(content)
	local wrapper = mw.html.create('div')
	wrapper
		:attr('id', 'assets')
	
	local tableNode = wrapper:tag('table')
	tableNode
		:addClass('wikitable')

	local header = tableNode:tag('tr')

	header:tag('th'):wikitext('Game')
	header:tag('th'):wikitext('Asset')
	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
		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(formatCellValue(row['Download links']))
		tr:tag('td'):wikitext(formatCellValue(row['Related game']))
	end

	return wrapper
end

local function renderPlayerTable(content)
	
	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')
	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
		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
			role = mw.text.trim(role)

			if role ~= '' then
				table.insert(
					clanRoles,
					formatCellValue(role)
				)
			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 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

	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')
	
	local tableNode = wrapper:tag('table')
	tableNode
		:addClass('wikitable')

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

		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)

				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
							role = mw.text.trim(role)

							if role ~= '' then
								table.insert(
									clanRoles,
									formatCellValue(role)
								)
							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)
					else
						table.insert(roleValues, '<div>—</div>')
					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(roleValues, ''))
				tr:tag('td'):wikitext(formatCellValue(row['Has status']))
			end
		end
	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))
	
	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))
	
	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 .. ']]'		
	}
	
	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 html = mw.html.create()
	html:wikitext(getSubpages(game))
	html:wikitext(forminput)
	
	return html
end

return p