Module:Franchise

From Zelda Wiki, the Zelda encyclopedia
Revision as of 16:08, 29 September 2022 by PhantomCaleb (talk | contribs)
Jump to navigation Jump to search

Allows modules to access the information stored in franchise code templates.

These functions return nil if passed an invalid code. This is not shown in the examples because it would add invalid codes to Special:WantedTemplates.

Codes are case-sensitive.

Lua error in package.lua at line 80: module 'Module:Franchise/Cache' not found.


local p = {}
local h = {}

local Error = require("Module:Error")
local utilsString = require("Module:UtilsString")
local utilsTable = require("Module:UtilsTable")

local Constants = mw.loadData("Module:Constants/Data")
local cache = mw.loadData("Module:Franchise/Cache")

-- Many templates need "Series" as if it were a game. Since it does not fit into the data model of Data:Franchise, it is manually defined here.
local series = {
	article = "The Legend of Zelda (Series)",
	shortName = "The Legend of Zelda Series",
	logo = "File:Zelda Logo TP.png",
	link = "[[The Legend of Zelda (Series)|''The Legend of Zelda'' series]]",
	display = "''The Legend of Zelda'' series",
	canonicity = "canon",
	code = "Series",
}

-- [[Template:Game Link]]
function p.Link(frame)
	local args = frame.args
	local game, nowarn = args[1], args.nowarn
	if game == nil or game == "" then
		return Error.printError("No game provided", "Category:"..Constants.catInvalidArgs, nowarn)
	end
	game = utilsString.trim(game)
	local link = p.link(game)
	if not link then
		return Error.printError(string.format("Invalid game <code>%s</code>", game), "Category:"..Constants.catInvalidArgs, nowarn)
	end
	local correctGameCode = p.code(game)
	if game ~= correctGameCode then
		link = link..Error.warn(string.format("<code>%s</code> should be written as <code>%s</code>.", game, correctGameCode), "Category:"..Constants.catInvalidArgs)
	end
	return link
end

-- Template:Franchise/Store *
function p.AddToPreview(frame)
	-- Performance optimization
	local utilsArg = require("Module:UtilsArg")
	local utilsVar = require("Module:UtilsVar")
	local utilsMarkup = require("Module:UtilsMarkup")
	local doc = mw.loadData("Module:Franchise/Documentation/Data")
	local orderCounter = utilsVar.counter("canonOrder")
	
	local entryType = frame.args[1]
	local args, err = utilsArg.parse(frame:getParent().args, doc.Templates["Franchise/Store " .. entryType])
	if err then
		return utilsMarkup.categories(err.categories)
	end
	args = utilsTable.merge({}, args, {
		entryType = entryType,
		link = p.deriveLink(entryType, args),
		display = p.deriveDisplay(entryType, args),
	})
	if entryType == "Game" or entryType == "Book" or entryType == "TV" then
		args.canonOrder = orderCounter.value()
	else
		args.canonOrder = "—"
	end
	if type == "Book" then
		args.phraseLink = p.derivePhraseLink(args)
	end
	utilsVar.add("rows", args)
end
function p.StoreOrder(frame)
	-- Performance optimization
	local utilsVar = require("Module:UtilsVar")
	local orderCounter = utilsVar.counter("canonOrder")
	return orderCounter.increment()
end
function p.StoreLink(frame)
	return p.deriveLink(frame.args[1], frame:getParent().args)
end
function p.StoreDisplay(frame)
	return p.deriveDisplay(frame.args[1], frame:getParent().args)
end
function p.StorePhraseLink(frame)
	return p.derivePhraseLink(frame:getParent().args)
end
function p.deriveDisplay(entryType, args)
	if args.display ~= nil and args.display ~= "" then
		return args.display
	elseif entryType == "Book" then
		return h.deriveBookFields(args).display
	else
		return ("''%s''"):format(args.shortName)
	end
end
function p.deriveLink(entryType, args)
	if args.display ~= nil and args.display ~= "" then
		return args.link
	elseif entryType == "Book" then
		return h.deriveBookFields(args).link
	else
		return ("''[[%s|%s]]''"):format(args.article, args.shortName)
	end
end
function p.derivePhraseLink(args)
	return h.deriveBookFields(args).phraseLink
end

function p.Preview(frame)
	-- Performance optimization
	local utilsMarkup = require("Module:UtilsMarkup")
	local utilsLayout = require("Module:UtilsLayout")
	local utilsVar = require("Module:UtilsVar")
	
	local previewColumns = {
		common = {"canonOrder", "code", "link", "display", "logo", "releaseDate", "canonicity"},
		Game = {"type", "graphics", "family", "remakeOf", "supersededBy"},
		Book = {"type", "phraseLink", "authors", "basedOn"},
		TV = {"type"},
		Compilation = {"titles"},
		Group = {"games"},
	}
	previewColumns.Game = utilsTable.concat(previewColumns.common, previewColumns.Game)
	previewColumns.Book = utilsTable.concat(previewColumns.common, previewColumns.Book)
	previewColumns.TV = utilsTable.concat(previewColumns.common, previewColumns.TV)
	previewColumns.Compilation = utilsTable.concat(previewColumns.common, previewColumns.Compilation)
	previewColumns.Group = utilsTable.concat(previewColumns.common, previewColumns.Group)
	
	local rows = utilsVar.get("rows")
	for _, row in ipairs(rows) do
		row.logo = utilsMarkup.link(row.logo)
	end
	local rowGroups = utilsTable.groupBy(rows, "entryType")
	local titles = utilsLayout.table({
		sortable = true,
		headers = previewColumns.common,
		rows = utilsTable.map(rows, utilsTable._toArray(previewColumns.common, ""))
	})
	local games = utilsLayout.table({
		sortable = true,
		headers = previewColumns.Game,
		rows = utilsTable.map(rowGroups.Game, utilsTable._toArray(previewColumns.Game, ""))
	})
	local books = utilsLayout.table({
		sortable = true,
		headers = previewColumns.Book,
		rows = utilsTable.map(rowGroups.Book, utilsTable._toArray(previewColumns.Book, ""))
	})
	local tv = utilsLayout.table({
		sortable = true,
		headers = previewColumns.TV,
		rows = utilsTable.map(rowGroups.TV, utilsTable._toArray(previewColumns.TV, ""))
	})
	local compilations = utilsLayout.table({
		sortable = true,
		headers = previewColumns.Compilation,
		rows = utilsTable.map(rowGroups.Compilation or {}, utilsTable._toArray(previewColumns.Compilation, ""))
	})
	local groups = utilsLayout.table({
		sortable = true,
		headers = previewColumns.Group,
		rows = utilsTable.map(rowGroups.Group or {}, utilsTable._toArray(previewColumns.Group, ""))
	})
	local preview = utilsLayout.tabs({
		{
			label = "All Titles",
			content = titles,
		},
		{
			label = "Games",
			content = games,
		},
		{
			label = "Books",
			content = books,
		},
		{
			label = "TV Shows",
			content = tv,
		},
		{
			label = "Compilations",
			content = compilations
		},
		{
			label = "Groups",
			content = groups,
		}
	}, { columns = 15 })
	return preview
end

function p.UploadField(frame)
	-- Performance optimization
	local utilsMarkup = require("Module:UtilsMarkup")
	local utilsVar = require("Module:UtilsVar")
	
	local rows = utilsVar.get("rows")
	local groups = utilsTable.groupBy(rows, "entryType")
	local mainGames, otherGames = utilsTable.partition(groups["Game"], {
		canonicity = "canon"
	})
	local remakes = utilsTable.groupBy(mainGames, "remakeOf")
	local books = groups["Book"]
	local tvShows = groups["TV"]
	
	local sortedMainGames = {}
	for _, mainGame in ipairs(utilsTable.reverse(mainGames)) do
		if not mainGame.remakeOf then
			table.insert(sortedMainGames, mainGame)
			for _, remake in ipairs(remakes[mainGame.code] or {}) do
				table.insert(sortedMainGames, remake)
			end
		end
	end
	
	local result = ""
	result = result .. "**|None\n"
	result = result .. "**Series|The Legend of Zelda Series\n"
	
	result = h.append(result, "Main Series", sortedMainGames)
	result = h.append(result, "Other Games", otherGames)
	result = h.append(result, "Books, Comics, and Manga", books)
	result = h.append(result, "TV Shows", tvShows)
	return utilsMarkup.pre(result)
end
function h.append(result, title, entries)
	result = result .. "\n*"..title.."\n"
	for _, entry in ipairs(entries) do
		result = result .. string.format("**%s|%s\n", entry.code, entry.shortName)
	end
	return result
end

function h.deriveBookFields(args)
	local ListPages = require("Module:List Pages")
	local utilsString = require("Module:UtilsString")

	local subtitle, display, link, phraseLink
	local parens = string.find(args.shortName, "%s%([^)]+%)")
	if parens then
		subtitle = string.sub(args.shortName, 1, parens - 1)
		local descriptor = string.sub(args.shortName, parens)
		display = ("''%s''%s"):format(subtitle, descriptor)
		link = ("[[%s|%s]]"):format(args.article, display)
		local authors = ListPages.main(utilsString.split(args.authors))
		phraseLink = ("[[%s|''%s'' %s]] by %s"):format(args.article, subtitle, args.type, authors)
	else
		display = ("''%s''"):format(args.shortName)
		link = ("''[[%s|%s]]''"):format(args.article, args.shortName)
		phraseLink = link
	end
	return {
		display = display,
		link = link,
		phraseLink = phraseLink,
	}
end

-- QUERIES: ALL

function p.enum(options)
	if not options then
		return cache.enum
	end
	enum = utilsTable.clone(cache.enum) -- clone the read-only cache item so that we can modify it
	if options.includeSeries then
		table.insert(enum, 1, "Series")
	end
	if options.includeCompilations then
		local codes = utilsTable.map(cache.compilations, "code")
		enum = utilsTable.concat(codes, enum)
	end
	if options.includeGroups then
		-- insert "groups" so as to not disrupt the release order. This matters for Template:Media (e.g. the Figher page, which uses SSB4)
		for _, group in ipairs(cache.groups) do
			local i = 1
			repeat
				i = i + 1
			until i == #enum or p.isCanon(enum[i]) == p.isCanon(group.code) and p.releaseDate(enum[i]) >= p.releaseDate(group.code)
			table.insert(enum, i, group.code)
		end
	end
	enum.reference = "[[Data:Franchise]]"
	return enum
end

function p.article(code)
	return h.get(code, "article")
end

function p.canonicity(code)
	return h.get(code, "canonicity")
end

function p.code(code)
	return h.get(code, "code")
end

function p.display(code)
	return h.get(code, "display")
end

function p.isCanon(code)
	return p.canonicity(code) == "canon"
end

function p.link(code)
	return h.get(code, "link")
end

function p.logo(code)
	return h.get(code, "logo")
end

function p.releaseDate(code)
	return h.get(code, "releaseDate")
end

function p.ShortName(frame)
	-- Performance optimization
	local utilsArg = require("Module:UtilsArg")
	local utilsMarkup = require("Module:UtilsMarkup")

	local args, err = utilsArg.parse(frame.args, {
		params = {
			[1] = {
				name = "code",
				enum = p.enum({
					includeSeries = true,
					includeCompilations = true,
					includeGroups = true,
				}),
			}
		}
	})
	if err then
		return utilsMarkup.categories(err.categories)
	end
	return p.shortName(args.code)
end
function p.shortName(code)
	return h.get(code, "shortName")
end

function p.type(code)
	return h.get(code, "type")
end

-- QUERIES: GAMES

function p.enumGames(includeSeries)
	if includeSeries then
		local enum = utilsTable.concat({"Series"}, cache.enumGames)
		enum.reference = "[[Data:Franchise]]"
		return enum
	end
	return cache.enumGames
end

function p.baseGame(code)
	local baseGame = h.get(code, "remakeOf")
	local hasBaseGame = baseGame ~= nil and baseGame ~= ""
	return hasBaseGame and baseGame or code
end

function p.family(code)
	return h.get(code, "family")
end

function p.graphics(code)
	return h.get(code, "graphics")
end

function p.hasRemakes(code)
	return utilsTable.hasKey(cache.remakes, string.lower(code))
end

function p.isRemake(code)
	return p.type(code) == "remake"
end

function p.remakes(code)
	return utilsTable.clone(cache.remakes[string.lower(code)]) or {}
end

-- QUERIES: BOOKS

function p.phraseLink(code)
	return h.get(code, "phraseLink")
end

function h.get(code, prop)
	code = string.lower(code)
	if code == "series" then
		return series[prop]
	end
	local title = cache.titlesByCode[code]
	return title and title[prop]
end

return p