×
Fallout Wiki

Module:Episodes

Module:Episodes is the Lua backend for Template:Episodes. It handles all logic, sorting, validation, and output formatting.

Data source[edit]

All episode metadata is stored in a separate data module:

Module:Episodes/Data

This data module contains only Lua tables and no wiki markup.

Each episode entry defines:

  • Season number
  • Page title
  • Category name
  • Whether the episode is a bonus episode

Each category entry defines:

  • The part of the category that goes after the season and episode information

Responsibilities[edit]

The module:

  • Detects episode parameters (SxEy, SxBy)
  • Sorts episodes numerically and by season
  • Prints season headers automatically
  • Prints the bonus episode header only when needed
  • Adds episode and season categories
  • Ignores unknown or malformed parameters safely
  • Tracks invalid or missing episode data via maintenance categories


Example episode entry[edit]

S1E4 = {
	season = 1,
	page = "S1E4 - The Ghouls",
	category = "Fallout Television Series S1E4"
}

Bonus episodes additionally include:

bonus = true

Example category entry[edit]

	character = "Characters",


Editing and extending[edit]

To add or update episodes:

  1. Edit Module:Episodes/Data
  2. Add or modify the relevant episode entries
  3. No changes to Module:Episodes or {{Episodes}} are required

This design allows new seasons or bonus episodes to be added without altering template or logic code.

Maintenance categories[edit]

The module may automatically add the following tracking categories:

  • Category:Pages with missing episode data
  • Category:Pages with invalid episode parameters

These categories are intended for maintenance and do not affect normal page display.

Technical notes[edit]

  • Uses mw.loadData for performance and caching
  • Output order is deterministic
  • Safe against duplicate headers and malformed input
  • Designed for future expansion without breaking existing pages

local p = {}
local tData = mw.loadData('Module:Episodes/Data')
local data = tData.Episodes
local cData = tData.Categories

-- ======================
-- Helpers
-- ======================

local function hasValue(v)
	return v ~= nil and v ~= ''
end

local function isSeasonToggle(key)
	return key:match('^S%d+$') ~= nil
end

local function parseEpisodeKey(key)
	local season, kind, number = key:match('^S(%d+)([EB])(%d+)$')
	if not season then return nil end
	return tonumber(season), kind, tonumber(number)
end

-- ======================
-- Core renderer
-- ======================

local function renderEpisodes(frame)
	local args = frame:getParent().args
	local output = {}
	local categories = {}
	local catType = 'character'

	local episodesBySeason = {}
	local invalid = {}

	-- ======================
	-- Collect enabled episodes
	-- ======================

	for key, value in pairs(args) do
		if hasValue(value) and not isSeasonToggle(key) then
			if key == 'type' then
				catType = value
			else
				local epData = data[key]
				if epData then
					local season, kind, number = parseEpisodeKey(key)
					if season then
						episodesBySeason[season] = episodesBySeason[season] or { E = {}, B = {} }
						table.insert(episodesBySeason[season][kind], {
							link = epData.link,
							category = epData.category,
							number = number
						})
					end
				else
					table.insert(invalid, key)
				end
			end
		end
	end

	-- ======================
	-- Sort seasons
	-- ======================

	local seasons = {}
	for season in pairs(episodesBySeason) do
		table.insert(seasons, season)
	end
	table.sort(seasons)
	
	if cData[catType] ~= nil then
		catType = cData[catType]
	else
		catType = nil
	end
	
	-- ======================
	-- Render per season
	-- ======================

	for _, season in ipairs(seasons) do
		local seasonData = episodesBySeason[season]

		-- Skip empty seasons entirely
		if #seasonData.E > 0 or #seasonData.B > 0 then

			table.sort(seasonData.E, function(a, b) return a.number < b.number end)
			table.sort(seasonData.B, function(a, b) return a.number < b.number end)

			-- Season header
			table.insert(
				output,
				string.format("'''''Season %d'''''", season)
			)
			table.insert(
				categories,
				string.format(
					"[[Category:Fallout Television Series Season %d ".. catType .. "]]",
					season
				)
			)

			-- Normal episodes
			for _, ep in ipairs(seasonData.E) do
				table.insert(output, string.format('[[%s]]', ep.link))
				if ep.category then
					table.insert(categories, '[[' .. ep.category .. ' ' .. catType .. ']]')
				end
			end

			-- Bonus episodes
			if #seasonData.B > 0 then
				local headerData = data['S' .. season .. 'B']
				if headerData and headerData.header then
					table.insert(output, string.format("'''''%s'''''", headerData.header))
					if headerData.category then
						table.insert(categories, '[[' .. headerData.category .. ' ' .. catType .. ']]')
					end
				end

				for _, ep in ipairs(seasonData.B) do
					table.insert(output, string.format('[[%s]]', ep.link))
					if ep.category then
						table.insert(categories, '[[' .. ep.category .. ' ' .. catType .. ']]')
					end
				end
			end
		end
	end
	--mw.logObject(output)
	--mw.logObject(categories)
	
	-- ======================
	-- Invalid parameter tracking
	-- ======================
	if catType == nil then
		categories = {}
		table.insert(categories, '[[Category:Pages with invalid episode category types]]')
	end
	
	if #invalid > 0 then
		table.insert(categories, '[[Category:Pages with invalid episode parameters]]')
	end
	
	return table.concat(output, '<br />') .. table.concat(categories)
end
-- Public entry points
function p.render(frame)
	return renderEpisodes(frame)
end

function p.main(frame)
	return renderEpisodes(frame)
end

return p