Site News
Warning: This wiki contains spoilers. Read at your own risk!

Social media: If you would like, please join our Discord server, and/or follow us on Twitter (X) or Tumblr!

Module:CYL results

From Fire Emblem Wiki, your source on Fire Emblem information. By fans, for fans.

A function for {{CYL results}}. Has only two functions:

  • main, the function that caries out most of the work
  • comma_value, which adds commas separating four or more digits and converts hyphen-minuses (-) into minus signs (−).

local p = {}
local cargo = mw.ext.cargo

function comma_value(n)	--Adds thousands separators to numbers; credit to http://richard.warburton.it
	local left, num, right = string.match(n,'^(%D*%d)(%d*)(.-)$')
	return (left .. (num:reverse():gsub('(%d%d%d)','%1,'):reverse()) .. right)
		:gsub('-', '−') -- replaces hyphen-minus with minus sign
end

function p.main(frame)
	in_args = frame:getParent().args
	
	-- Character name; initialized as article name
	Name = mw.title.getCurrentTitle().text
	
	--[[ turns all template-level input into comma-separated list,
		 used for cargo where IN condition (faster than many ORs)]]
	for k, v in pairs(in_args) do
		if (k == 1) then
			Name = v
		else
			Name = Name .. '","' .. v
		end
	end
	
	-- List of game titles; converts shorthand stored for images into full title
	GameList = {
		fe11 = "''[[Fire Emblem: Shadow Dragon & the Blade of Light|" ..
			"Shadow Dragon & the Blade of Light]]'' / " ..
			"''[[Fire Emblem: Shadow Dragon|Shadow Dragon]]''",
		fe02 = "''[[Fire Emblem Gaiden|Gaiden]]''",
		fe15 = "''[[Fire Emblem Gaiden|Gaiden]]'' / " ..
			"''[[Fire Emblem Echoes: Shadows of Valentia|" ..
			"Echoes: Shadows of Valentia]]''",
		fe12 = "''[[Fire Emblem: Mystery of the Emblem|" ..
			"Mystery of the Emblem]]'' / " ..
			"''[[Fire Emblem: New Mystery of the Emblem: " ..
			"Heroes of Light and Shadow|New Mystery of the Emblem]]''",
		fe04 = "''[[Fire Emblem: Genealogy of the Holy War|" ..
			"Genealogy of the Holy War]]''",
		fe05 = "''[[Fire Emblem: Thracia 776|Thracia 776]]''",
		fe06 = "''[[Fire Emblem: The Binding Blade|The Binding Blade]]''",
		fe07 = "''[[Fire Emblem: The Blazing Blade|The Blazing Blade]]''",
		fe08 = "''[[Fire Emblem: The Sacred Stones|The Sacred Stones]]''",
		fe09 = "''[[Fire Emblem: Path of Radiance|Path of Radiance]]''",
		fe10 = "''[[Fire Emblem: Radiant Dawn|Radiant Dawn]]''",
		fe13 = "''[[Fire Emblem Awakening|Awakening]]''",
		fe14 = "''[[Fire Emblem Fates|Fates]]''",
		fe16a = "''[[Fire Emblem: Three Houses|Three Houses]]'' (Academy arc)",
		fe16w = "''[[Fire Emblem: Three Houses|Three Houses]]'' (War arc)",
		fewa2 = "''[[Fire Emblem Warriors: Three Hopes|" ..
			"Warriors: Three Hopes]]''",
		fe17 = "''[[Fire Emblem Engage|Engage]]''",
		feh = "''[[Fire Emblem Heroes|Heroes]]''",
		tmsfee = "''[[Tokyo Mirage Sessions ♯FE Encore]]''",
	}
	
	-- Start and end table bits
	TableStart = '\n{| class="cellpadding-3 basic" style="border-width: 2px;' ..
		' border-top-width: 0px; border-radius: 0px; width: 65%"\n|-'
	TableEnd = '\n|}'
	
	-- characters who won, but had other variations still votable
	winners = {
		Edelgard = 4,
		Byleth = 6,
		Tiki = 6,
		Robin = 7,
		Corrin = 7,
	}
	
	-- Additional arguments for cargo query
	local args = {	
		where = "namearticle IN (\"" .. Name .. "\")",
		orderBy = '_pageName, placement'
	}
	
	-- Cargo query
	local query = cargo.query('FEH_CYL_results', '_pageName, placement, ' ..
		'placementchange, name, nameextra, name2extra, name3extra, image, ' ..
		'game, game2, game3, votes, voteschange, mirage', args)
	
	Stats = {} -- table to stort data into
	
	-- First round votable
	first = query[1]["_pageName"]:sub(21, 24) - 2016
	
	--[[ num is round number;
		 last is last round votable
		 t saves number of last row
		 final is final output
		]]
	num = 0; last = 0; t = 1; final = ''
	
	-- Loops through cargo query and sorts it into Stats table
	for k, v in ipairs(query) do
		num = v["_pageName"]:sub(21, 24) - 2016
		
		-- if two or more variations are votable in same round, increments t
		if num == last then
			t = t + 1
		else -- otherwise, t becomse 1
			t = 1
		end
		
		-- creates sub-table for new data if it does not exist already
		if Stats[num] == nil then
			Stats[num] = {}
		end
	
		-- stores data for one variation at one round
		Stats[num][t] = v
		
		last = num -- updates last round
	end
	
	--[[ total is the total number of votes
		 entries is the total number of entries
		 year is the year the round took place
		 collapsible determines if at least one entry is hidden
		]]
	total = 0; entries = 0; year = 0; collapsible = false
	
	-- iterates through Stats; i corresponds to round number
	for i = first, last do
		year = i + 2016
		
		-- start of row; gives round number and year
		row = '\n| rowspan="' .. #Stats[i] .. '" style="width: 15%" | ' ..
			"'''[[Choose Your Legends/" .. year .." results|CYL " .. i ..
			"]]'''<br>(" .. year ..")"
			
		-- iterates through one variation's stats from round num
		for k, v in ipairs(Stats[i]) do
			name = v['name']
			place = tonumber(v['placement'])
			pchange = tonumber(v['placementchange'])
			vote = tonumber(v['votes'])
			vchange = tonumber(v['voteschange'])
			
			-- adds placement; links to relevant results page
			row = row .. '\n| style="width: 15%" | ' ..
				"'''[[Choose Your Legends/" .. year .. " results#" .. place ..
				"|" .. comma_value(place) .. "]]'''"
			
			-- adds placement change if it is declared and not 9999999
			if pchange ~= nil and pchange ~= 9999999 then
				if pchange > 0 then -- if increased (bad)
					row = row .. '<br><small><span style="color: #f00">(+' ..
						comma_value(pchange) .. ')</span></small>'
				elseif pchange < 0 then -- if decreased (good)
					row = row .. '<br><small><span style="color: #0a0">(' ..
						comma_value(pchange) .. ')</span></small>'
				else -- if no change
					row = row .. '<br><small>(No change)</span></small>'
				end
			end
			
			-- adds image and name
			row = row .. '\n| style="width: 45%" | [[File:Portrait ' ..
				v['image'] .. " cyl.png|70px]]<br>'''" .. name .. "'''"
			
			-- adds mirage if present
			if v['mirage'] ~= 'None' and v['mirage'] ~= nil then
				row = row .. " & '''" .. v['mirage'] .. "'''"
			
			-- adds extra names (e.g. "male", "Early Story")
			elseif v['name3extra'] ~= 'None' and v['name3extra'] ~= nil then
				if v['nameextra'] == 'None' then
				row = row .. "<br>'''" ..
					name .. "''' (" .. v['name2extra'] .. ")<br>'''" .. name ..
					"''' (" .. v['name3extra'] .. ")"
				else 
				row = row .. " (" .. v['nameextra'] .. ")" .. "<br>'''" ..
					name .. "''' (" .. v['name2extra'] .. ")<br>'''" .. name ..
					"''' (" .. v['name3extra'] .. ")"
				end
			elseif v['name2extra'] ~= 'None' and v['name2extra'] ~= nil then
				row = row .. " (" .. v['nameextra'] .. ')' .. "<br>'''" ..
					name .. "''' (" .. v['name2extra'] .. ')'
			elseif v['nameextra'] ~= 'None' then
				row = row .. " (" .. v['nameextra'] .. ')'
			end
			
			-- adds game
			row = row .. '<br><small>' .. GameList[v['game']]
			
			-- adds extra games
			if v['game3'] ~= 'None' and v['game3'] ~= nil then
				row = row .. "<br>" .. GameList[v['game2']] .. "<br>" ..
				GameList[v['game3']]
			elseif v['game2'] ~= 'None' and v['game2'] ~= nil then
				row = row .. "<br>" .. GameList[v['game2']]
			end
			
			-- adds vote count
			row = row .. '</small>\n| style="width: 25%" |' .. comma_value(vote)
			
			-- adds vote change if it is declared and not 9999999
			if vchange ~= nil and vchange ~= 9999999 then
				if vchange > 0 then -- if increased (good)
					row = row .. '<br><small><span style="color: #0a0">(+' ..
						comma_value(vchange) .. ')</span></small>'
				elseif vchange < 0 then -- if decreased (bad)
					row = row .. '<br><small><span style="color: #f00">(' ..
						comma_value(vchange) .. ')</span></small>'
				else -- if no change
					row = row .. '<br><small>(No change)</span></small>'
				end
			end
			
			total = total + vote -- increases total vote count
			entries = entries + 1 -- increases entry count
			
			row = row .. "\n|-" -- appends row separator
		end
		
		row = TableStart .. row .. TableEnd -- adds start and end table bits
		
		-- if not final votable round or a variation won this round
		if not(i == last or winners[Name] == i) then
			-- makes content collapsible
			row = '\n<div class="mw-collapsible-content">' .. row .. '</div>'
			
			collapsible = true
		end
		
		final = final .. row -- adds round data to final output
	end
	
	-- assembles total results footer
	local TotalResults = '\n{| class="cellpadding-3 basic" ' ..
		'style="border-width: 0 2px 2px; border-radius: 0 0 15px 15px; ' ..
		'width: 65%"\n' .. 
		'! class="roundbl" style="width: 15%" | Total results\n' .. 
		'| style="width: 15%" |\n' .. 
		'| style="width: 45%" | This character has ' .. entries
	
	--pluralizes "entry" if relevant
	if entries == 1 then
		TotalResults = TotalResults .. ' entry '
	else
		TotalResults = TotalResults .. ' entries '
	end
	
	TotalResults = TotalResults .. ' across all Choose Your Legends polls.\n' .. 
		'| class="roundbr" style="width: 25%" | ' .. comma_value(total) ..
		'\n|}'
	
	-- assembles overal table start
	Header = '{| class="cellpadding-3 basic" style="border-width: 2px; ' ..
		'border-radius: 15px 15px 0 0; width: 65%"' .. 
		'\n|-\n! class="roundtl" style="width: 15%" | Round' ..
		'\n! style="width: 15%" | Placement' ..
		'\n! style="width: 45%" | Character' ..
		'\n! class="roundtr" style="width: 25%" | Votes\n|}'
	
	-- adds collapsible button, unless all votable rounds are visible
	if collapsible then
		Header = '<div class="mw-collapsible mw-collapsed" '..
			'style="width: 100%" data-expandtext="Show all placements">\n' ..
			'<div style="clear: both"></div>\n' .. Header
		TotalResults = TotalResults .. "</div>"
	end
	
	winner = '' -- "ineligible" tag
	
	if last < 8 then
		winner = TableStart .. '\n! style="width: 15%" | ' ..
			' [[Choose Your Legends/' .. year + 1 .. ' results|CYL ' ..
			last + 1 .. ']] onwards\n' .. "| colspan=\"3\" | ''Ineligible''\n|}"
	end
	
	return Header .. final .. winner .. TotalResults -- final output
end

return p