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

Module:Universe

From PC Gaming Shelter
Revision as of 13:43, 5 August 2026 by WikiVisor (talk | contribs)

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')
		:addClass('sortable')

	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')
		:addClass('sortable')

	local header = tableNode:tag('tr')

	header:tag('th'):wikitext('Player Name')
	header:tag('th'):wikitext('Associated Games')
	header:tag('th'):wikitext('Clan / Community')
	header:tag('th'):wikitext('Contact')
	
	for _, row in ipairs(content or {}) do
		local roles = {}
		local member = row['Member of']

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

			for _, clan in ipairs(clans) do
				local query = {
					'[[-Has subobject::' .. row[1] .. ']]',
					'[[Member of::' .. clan .. ']]',
					'?Has role#-=role',
					mainlabel = '-',
				}

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

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

					if type(roleValues) == 'table' then
						for _, role in ipairs(roleValues) do
							table.insert(clanRoles, tostring(role))
						end
					elseif roleValues ~= nil and roleValues ~= '' then
						table.insert(clanRoles, tostring(roleValues))
					end
				end

				if #clanRoles > 0 then
					table.insert(
						roles,
						string.format('<div>%s</div>', 
						formatCellValue(clan) ..
							' (' .. table.concat(clanRoles, ', ') .. ')'
						)
					)
				else
					table.insert(roles, string.format('<div>%s</div>', formatCellValue(clan)))
				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(roles, ''))
		tr:tag('td'):wikitext(formatCellValue(row['Contact links']))
	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',
		'?Contact links'
	}
	
	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.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

function p.test()
	local page = 'Star Trek: Voyager - Elite Force'	

	local query = {
		'[[Category:Players]][[Plays game::' .. page .. ']]',
		'?Plays game',
		'?Member of',
		'?Contact links',
	}
	
	local content = mw.smw.ask(query)

	for _, row in ipairs(content or {}) do
		local clans = row['Member of']
		local roles = {}
		if type(clans) == 'table' then
			for _, clan in ipairs(clans)	do
				local q = {
				'[[-Has subobject::' .. row[1] .. ']][[Member of::' .. clan .. ']]',
				'?Has role#-',
				'mainlabel=-',
			}	
			local getRoles = mw.smw.ask(q)
			table.insert(roles, tostring(getRoles))
		end
		else
			table.insert(roles, clans)
		end
	end
	
	return roles
end

return p