We are currently performing an upgrade to our software. This upgrade will bring MediaWiki from version 1.31 to 1.33. While the upgrade is being performed on your wiki it will be in read-only mode. For more information check here.


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

This module implements Template:Current Events.

  • maps (t0 = nil)
Displays a list of banners for Special Maps active at the given time (defaults to the current time if not given).
  • events (t0 = nil)
Displays a list of banners for events active at the given time (defaults to the current time if not given).
local cargo = mw.ext.cargo
local Util = require 'Module:Util'
local CargoUtil = require 'Module:CargoUtil'
local List = require 'Module:ListUtil'
local Hash = require 'Module:HashUtil'

-- sort_id1 == 3000
local GHB_REVIVAL = List.to_set {
	'T0003', 'T0001', 'T0004', 'T0007', 'T0002', 'T0005', 'T0006',
	'T0009', 'T0008', 'T0010', 'T0011', 'T0012', 'T0014', 'T0019',

local RIVAL_DOMAINS_CYCLE = {'Infantry', 'Armored', 'Cavalry', 'Flying'}

-- reverse appearance order for special maps
-- hero battles -> ghb revivals -> special training -> rival domains -> ghb / bhb / lhb / mhb -> event maps
local MAP_ORDER_FN = {
	function (v) return v.MapGroup == 'Hero Battle' end,
	function (v) return GHB_REVIVAL[v.Map] end,
	function (v) return v.MapGroup == 'Special Training' end,
	function (v) return v.MapGroup == 'Rival Domains' end,
	function (v) return v.MapGroup == 'Grand Hero Battle' or v.MapGroup == 'Bound Hero Battle' or
		v.MapGroup == 'Legendary Hero Battle' or v.MapGroup == 'Mythic Hero Battle' end,
	function (v) return true end,

	{'VotingGauntlets', "CONCAT('Voting Gauntlet')=kind,   _pageName=page,Name=text,CONCAT('Voting Gauntlet: ',Name)=name"},
	{'TempestTrials',   "CONCAT('Tempest Trials')=kind,    _pageName=page,Name=text,FullName=name"},
	{'TapBattles',      "CONCAT('Tap Battle')=kind,        _pageName=page,Name=text,CONCAT('Illusory Dungeon: ',Name)=name"},
	{'GrandConquests',  "CONCAT('Grand Conquests')=kind,   _pageName=page,          _pageName=name"},
	{'ForgingBonds',    "CONCAT('Forging Bonds')=kind,     _pageName=page,Name=text,CONCAT('Forging Bonds: ',Name)=name"},
	{'RokkrSieges',     "CONCAT('Røkkr Sieges')=kind,      _pageName=page,          _pageName=name"},
	{'LostLore',        "CONCAT('Lost Lore')=kind,         _pageName=page,Name=text,CONCAT('Lost Lore: ',Name)=name"},
	{'HallOfForms',     "CONCAT('Hall of Forms')=kind,     _pageName=page,          _pageName=name"},
	{'MjolnirsStrike',  "CONCAT('Mjölnir\\'s Strike')=kind,_pageName=page,          _pageName=name"},

local countdown = function (dt)
	if dt >= 86400 then
		return ('Days left: %d'):format(math.floor(dt / 86400))
	elseif dt >= 3600 then
		return ('Hrs. left: %d'):format(math.floor(dt / 3600))
		return ('Mins. left: %d'):format(math.floor(dt / 60))

local eventEntry = function (banner, dt)
	local eventbox = mw.html.create('div'):addClass('panel-eventbox'):css('vertical-align', 'middle')
	return eventbox

local maps = function (args, frame)
	local now = args[1] and tonumber(frame:callParserFunction('#time', 'U', args[1])) or os.time()
	local allMaps = cargo.query('Maps,MapDates', 'Map,MapGroup,Maps._pageName=Page,StartTime,EndTime,CycleTime,AvailTime', {
		join = 'Maps._pageName=MapDates._pageName',
		where = ("MapGroup!='Tempest Trials' AND ('%s' BETWEEN StartTime AND EndTime) AND NOT ((CycleTime IS NULL OR AvailTime IS NULL) AND EndTime='%s')"):format(
			Util.formatTime(now), Util.MAX_TIME),
		orderBy = 'StartTime,Map',
		limit = 100,
	for _, v in ipairs(allMaps) do
		v.StartTime = CargoUtil.parse_datetime(v.StartTime)
		v.EndTime = CargoUtil.parse_datetime(v.EndTime)
		v.CycleTime = tonumber(v.CycleTime)
		v.AvailTime = tonumber(v.AvailTime)
	List.keep_if(allMaps, function (v)
		if not v.CycleTime or not v.AvailTime then
			return true
		v.StartTime = math.max(v.StartTime, v.StartTime + math.floor((now - v.StartTime) / v.CycleTime) * v.CycleTime)
		v.EndTime = math.min(v.EndTime, v.StartTime + v.AvailTime - 1)
		return now >= v.StartTime and now <= v.EndTime

	local sortedMaps = {}
	for _, fn in ipairs(MAP_ORDER_FN) do
		List.concat_self(sortedMaps, List.delete_if(allMaps, fn))

	-- existing special map banners
	local vbanners = Hash.from_pairs(cargo.query('_pageData', '_pageName', {
		where = "_pageName RLIKE '^File:Banner V[[:digit:]]+\\.(webp|png)$'",
		limit = 200,
	}), function (v) return mw.ustring.match(v._pageName, 'Banner (V%d+)'), true end)

	return table.concat(List.map(sortedMaps, function (v)
		local bannerName = nil
		if mw.ustring.sub(v.Map, 1, 1) == 'V' then
			bannerName = vbanners[v.Map] and v.Map or 'V0001'
		elseif v.MapGroup == 'Rival Domains' then
			bannerName = 'Rival Domains'
			local num = tonumber(mw.ustring.match(v.Map, '^Q(%d+)$'))
			if num and num >= 9 then
				bannerName = ('Q%04d'):format((num - 1) % 4 + 1)
		elseif v.MapGroup == 'Relay Defense' then
			bannerName = 'R0001'
		local banner = frame:expandTemplate {title = 'Banner HB', args = {v.Page, bannerName = bannerName}}
		return tostring(eventEntry(banner, v.EndTime - now))

local events = function (args, frame)
	local allEvents = {}
	local now = args[1] and tonumber(frame:callParserFunction('#time', 'U', args[1])) or os.time()
	local queryArgs = {where = ("'%s' BETWEEN StartTime AND EndTime"):format(Util.formatTime(now)), groupBy = '_pageName'}
	for _, v in ipairs(EVENT_SOURCES) do
		List.concat_self(allEvents, cargo.query(v[1], v[2] .. ',EndTime', queryArgs))
	for _, v in ipairs(allEvents) do
		v.EndTime = CargoUtil.parse_datetime(v.EndTime)
--	table.sort(allEvents, function (x, y) return x.EndTime < y.EndTime end)

	return table.concat(List.map(allEvents, function (v)
		local banner = v.kind == 'Tempest Trials' and
			frame:expandTemplate {title = 'Banner TT', args = {bannerName = v.text}} or
			frame:expandTemplate {title = 'Banner Event', args = {bannerType = v.kind, fontSize = 4, text1 = v.text, link = v.page}}
		return tostring(eventEntry(('%s<br>[[%s|%s]]'):format(banner, v.page, v.name), v.EndTime - now))

return require 'Module:MakeMWModule'.makeMWModule {
	maps = maps,
	events = events,