Module:SummoningFocus

From Fire Emblem Heroes Wiki
Jump to: navigation, search
Template-info.svg Documentation

Usage[edit source]

focusPage[edit source]

{{#invoke:SummoningFocus|focusPage
|name=<!--the exact in-game name of the focus goes here-->
|bannerType=
|description=
|youtubeEN=
|youtubeJP=
|rarity5FocusPercent=
|rarity5Percent=
|rarity4Percent=
|rarity3Percent=
|hero1=
|hero2=
|hero3=
|start=
|end=
|stones=
}}

As many Heroes as needed can be specified.

local cargo = mw.ext.cargo
local Util = require 'Module:Util'
local List = require 'Module:ListUtil'
local Datetime = require 'Module:DatetimeUtil'
local Tab = require 'Module:Tab'
local FEHStatUtil = require 'Module:FEHStatUtil'
local FocusRates = require 'Module:FocusRates'
local SummoningEvent = require 'Module:SummoningEvent'
local escq = require 'Module:EscQ'.main1
local toboolean = require 'Module:Bool'.toboolean
local lang = mw.getContentLanguage()
local p = {}

local collectHeroes = function (args)
	local i = 1
	local heroes = {}
	while not Util.isNilOrEmpty(args['hero' .. i]) do
		heroes[i] = args['hero' .. i]
		i = i + 1
	end
	return heroes
end

local makeRarityHeroTable = function (args, focusRarities)
	-- Make a table that includes a Hero table and a Rarity table.
	-- If no rarity* parameter for a Hero were given, that hero will be added
	-- with all rarity values in focusRarities table..
	local i = 1
	local rarityHero = { Heroes = {}, Rarities = {} }
	local addHero = function (rarity, hero)
		table.insert(rarityHero['Heroes'], hero)
		table.insert(rarityHero['Rarities'], rarity)
	end
	while not Util.isNilOrEmpty(args['hero' .. i]) do
		hero = args['hero' .. i]
		if Util.isNilOrEmpty(args['rarity' .. i]) then
			for _, rarity in ipairs(focusRarities) do
				addHero(rarity, hero)
			end
		else
			rarities = mw.text.split(args['rarity' .. i], ',')
			for _, rarity in ipairs(rarities) do
				addHero(rarity, hero)
			end
		end
		i = i + 1
	end
	return rarityHero
end

p.statsTables = function (frame, heroes)
	local heroStats = List.map(heroes, function (h)
		local q = cargo.query('Units,UnitStats', 'Lv1HP5,Lv1Atk5,Lv1Spd5,Lv1Def5,Lv1Res5,HPGR3,AtkGR3,SpdGR3,DefGR3,ResGR3', {
			join = 'Units.WikiName=UnitStats.WikiName',
			where = ("Units._pageName='%s' AND IFNULL(Properties__full,'') NOT LIKE '%%enemy%%'"):format(escq(h)),
			groupBy = 'Units._pageName',
		})[1]
		if not q then
			return {'?/?/?', '?/?/?', '?/?/?', '?/?/?', '?/?/?'}
		end
		local stats = {tonumber(q.Lv1HP5), tonumber(q.Lv1Atk5), tonumber(q.Lv1Spd5), tonumber(q.Lv1Def5), tonumber(q.Lv1Res5)}
		local rates = {tonumber(q.HPGR3), tonumber(q.AtkGR3), tonumber(q.SpdGR3), tonumber(q.DefGR3), tonumber(q.ResGR3)}
		local rarity = 5
		return List.zip(stats, rates, function (base, rate)
			return {
				('%d/%d/%d'):format(base - 1, base, base + 1),
				('%d/%d/%d'):format(
					base + FEHStatUtil.getGrowthValue(rarity, rate - 5) - 1,
					base + FEHStatUtil.getGrowthValue(rarity, rate),
					base + FEHStatUtil.getGrowthValue(rarity, rate + 5) + 1),
			}
		end)
	end)

	local doMakeTable = function (isLv40)
		local tbl = mw.html.create('table'):addClass('wikitable'):addClass('unsortable'):css('text-align', 'center')

		local row = tbl:tag('tr')
		row:tag('th'):css('width', '25%'):wikitext('Hero')
		row:tag('th'):css('width', '15%'):wikitext('HP')
		row:tag('th'):css('width', '15%'):wikitext('Atk')
		row:tag('th'):css('width', '15%'):wikitext('Spd')
		row:tag('th'):css('width', '15%'):wikitext('Def')
		row:tag('th'):css('width', '15%'):wikitext('Res')

		for i, hero in ipairs(heroes) do
			local ss = heroStats[i]

			local row = tbl:tag('tr')
			row:tag('td'):css('text-align', 'left'):wikitext(('%s [[%s]]'):format(
				frame:expandTemplate {title = 'UnitIcon', args = {name = hero, size = '50px'}}, hero))
			for _, v in ipairs(ss) do
				row:tag('td'):wikitext(v[isLv40 and 2 or 1])
			end
		end

		return tostring(tbl)
	end

	return doMakeTable(false), doMakeTable(true)
end

local SKILL_CATEGORIES = {'weapon', 'assist', 'special', 'passivea', 'passiveb', 'passivec'}

p.skillsTable = function (frame, heroes)
	local heroSkills = List.map(heroes, function (h)
		return List.group_by(cargo.query('Units,UnitSkills,Skills', 'Skills._pageName=page,Skills.Name=name,Scategory', {
			join = 'Units.WikiName=UnitSkills.WikiName,UnitSkills.skill=Skills.WikiName',
			where = ("Units._pageName='%s' AND IFNULL(Units.Properties__full,'') NOT LIKE '%%enemy%%'"):format(escq(h)),
			groupBy = 'UnitSkills.skill',
			orderBy = 'skillPos',
		}), function (v) return v.Scategory end)
	end)

	local tbl = mw.html.create('table'):addClass('wikitable'):addClass('unsortable'):css('text-align', 'center')

	local row = tbl:tag('tr')
	row:tag('th'):css('width', '18em'):wikitext('Hero')
	row:tag('th'):css('width', '9em'):wikitext('Weapon')
	row:tag('th'):css('width', '9em'):wikitext('Assist')
	row:tag('th'):css('width', '9em'):wikitext('Special')
	row:tag('th'):css('width', '9em'):wikitext('Passive A')
	row:tag('th'):css('width', '9em'):wikitext('Passive B')
	row:tag('th'):css('width', '9em'):wikitext('Passive C')

	for i, hero in ipairs(heroes) do
		local ss = heroSkills[i]

		local row = tbl:tag('tr')
		row:tag('td'):tag('div'):css('display', 'inline-block'):wikitext(frame:expandTemplate {title = 'UnitText with Type', args = {name = hero}})
		for _, cat in ipairs(SKILL_CATEGORIES) do
			local s = ss[cat]
			row:tag('td'):wikitext(s and table.concat(List.map_self(s, function (skill)
				return ('[[%s|%s]]'):format(skill.page, skill.name)
			end), '<br>') or '—')
		end
	end

	return tostring(tbl)
end

function p.focusPage(frame)
	local ytText
	if not (Util.isNilOrEmpty(frame.args.youtubeEN) and Util.isNilOrEmpty(frame.args.youtubeJP)) then
		ytText = tostring(Tab.tabber {
			{'English', frame:callParserFunction {name = "#ev:youtube", args = {frame.args.youtubeEN}}},
			{'Japanese', frame:callParserFunction {name = "#ev:youtube", args = {frame.args.youtubeJP}}},
		})
	else
		ytText = ""
	end

	local rateTbl = mw.html.create("table")
		:addClass("wikitable")
		:addClass("default")
		:css("text-align","center")
	local headers = rateTbl:tag("tr")
	headers:tag("th"):wikitext("Rarity")
	headers:tag("th"):wikitext("Appearance rate")

	local focusRarities = {}
	for i = 5,1,-1 do
		if not Util.isNilOrEmpty(frame.args["rarity" .. i .."FocusPercent"]) then
			table.insert(focusRarities, tostring(i))
			local row = rateTbl:tag("tr")
			row:tag("td"):wikitext(i.."★ Focus")
			row:tag("td"):wikitext(frame.args["rarity" .. i .."FocusPercent"])
		end
		if not Util.isNilOrEmpty(frame.args["rarity" .. i .."Percent"]) then
			local row = rateTbl:tag("tr")
			row:tag("td"):wikitext(i.."★")
			row:tag("td"):wikitext(frame.args["rarity" .. i .."Percent"])
		end
	end

	local notesSection
	if Util.isNilOrEmpty(frame.args.notes) then
		notesSection = ""
	else
		notesSection="<h2>Notes</h2>" .. frame.args.notes
	end

	--Determine the pool
	local isNew = false
	local startDate
	if not Util.isNilOrEmpty(frame.args.start) then -- this check is required because lang:formatDate will return the current date if it receives no string parameter
		startDate = lang:formatDate('Y-m-d', frame.args.start)
	end
	local endDate
	if not Util.isNilOrEmpty(frame.args['end']) then
		endDate = lang:formatDate('Y-m-d', frame.args['end'])
	end

	if startDate and startDate >= '2019-04-05' then
		if frame.args.bannerType == 'New Heroes' then
			isNew = true
		end
		if frame.args.bannerType == 'Special' then
			local dates = cargo.query('SummoningEvents', 'DATE(StartTime)=Date', {
				where = "Name='" .. escq(frame.args.name) .. "'",
				groupBy = 'Date',
				orderBy = 'Date'
			})
			if #dates == 0 or dates[1].Date == startDate then
				isNew = true
			end
		end
	end

	local heroTable = collectHeroes(frame.args)
	local lv1stats, lv40stats = p.statsTables(frame, heroTable)
	local rarityHeroTable = makeRarityHeroTable(frame.args, focusRarities)
	local canFocusRatesEnabled = Util.isNilOrEmpty(frame.args['rarity4FocusPercent']) or startDate < '2020-02-07'
	local canFocusRatesOverride = toboolean(frame.args['showFocusRates'])
	if canFocusRatesOverride ~= nil then
		canFocusRatesEnabled = canFocusRatesOverride
	end

	local name = not Util.isNilOrEmpty(frame.args.name) and frame.args.name or mw.title.getCurrentTitle().fullText
	local wikiname = not Util.isNilOrEmpty(frame.args.wikiname) and frame.args.wikiname or
		frame:expandTemplate {title = 'SummoningEventWikiName', args = {name = name, startDate = frame.args.start}}
	local summoningEventFocusArgs = List.map(List.zip(rarityHeroTable.Heroes, rarityHeroTable.Rarities, function (a, b) return {a, b} end), function (v)
		return {title = 'SummoningEventFocusDefinition', args = {wikiname = wikiname, unit = v[1], rarity = v[2]}}
	end)

	local contents = {
		--[[
		frame:expandTemplate {
			title = 'SummoningFocusDefinition',
			args = {
				wikiname = frame.args.wikiname,
				name = frame.args.name,
				start = frame.args.start,
				['end'] = frame.args["end"],
				bannerType = frame.args.bannerType,
				heroList = table.concat(rarityHeroTable['Heroes'], ";"),
				rarity = table.concat(rarityHeroTable['Rarities'], ';'),
			}
		},
		]]
		frame:expandTemplate {
			title = 'SummoningEventDefinition',
			args = {
				name = name,
				wikiname = wikiname,
				from = frame.args.from or frame.args.start == '' and '' or
					Datetime.is_iso8601(frame.args.start) and frame.args.start or (frame.args.start .. ' 07:00:00'),
				to = frame.args.to or frame.args['end'] == '' and '' or
					Datetime.is_iso8601(frame.args['end']) and frame.args['end'] or (frame.args['end'] .. ' 06:59:59'),
				eventType = frame.args.eventType or frame.args.bannerType,
			}
		},
		table.concat(List.map(summoningEventFocusArgs, function (targs) return frame:expandTemplate(targs) end)),
		'__NOTOC__',
		SummoningEvent._table {
			page = mw.title.getCurrentTitle().text,
			name = frame.args.name,
			startDate = startDate,
			endDate = endDate,
			heroes = table.concat(heroTable, ";"),
			extratitle = 'Description',
			extratext = frame.args.description,
		},
		ytText,
		'<h2>Appearance rates</h2>',
		frame:expandTemplate {title = 'Hatnote', args = {"See [[Summonable Heroes]] for a full table of the regular pool of summonable heroes."}},
		tostring(rateTbl),
		canFocusRatesEnabled and FocusRates.focusRates(frame, isNew) or '',
		frame:expandTemplate {title = 'Hatnote', args = {"For more detailed information, see [[Summon#Color Distribution]]."}},
		'<h2>Skills</h2>', p.skillsTable(frame, heroTable),
		'<h2>Level 40 stats</h2>', lv40stats,
		notesSection,
		frame:expandTemplate {title = 'Summoning Focus Navbox'},
	}
	
	return table.concat(contents)
end

return p