Module:SpecialsMapList

local cargo = mw.ext.cargo local escq = require 'Module:EscQ'.main1 local toboolean = require 'Module:Bool'.toboolean local mf = require 'Module:MF'.main1 local List = require 'Module:ListUtil' local Hash = require 'Module:HashUtil' local Datetime = require 'Module:DatetimeUtil'

local vbanners = Hash.from_ipairs(cargo.query('_pageData', '_pageName', { where = "_pageName RLIKE '^File:Banner Vdigit:'", limit = 200, }), function (v) return mw.ustring.match(v._pageName, 'Banner (V%d*)'), mw.ustring.sub(v._pageName, 6, -1) end)

local focusToMap = function (focus, type) if type == 'Legendary' or type == 'Mythic' then return {name = focus.hero .. " (map)"} elseif type == 'Bound Hero Battle' or type == 'Grand Hero Battle' then return cargo.query("MapDates,Maps", "MapDates._pageName=name", {			join = "Maps._pageName=MapDates._pageName",			where = ("StartTime='%s' AND Maps.MapGroup='%s'"):format(focus.t0, type),			limit = 1,			orderBy = "Map DESC",		})[1] end return nil end

local newMaps = function (args, frame) local maps = cargo.query('Maps,MapDates',		"Maps._pageName=page,StartTime=t0,EndTime=t1,MapGroup=kind,HasReward=reward,Map", {		join = 'Maps._pageName=MapDates._pageName',		where = ("Map NOT LIKE 'W%%' AND EndTime > NOW AND EndTime != '%s' AND StartTime %s NOW"):format( Datetime.MAX_TIME, toboolean(args.upcoming) and '>' or '<'),		orderBy = 't0,Map DESC'	})

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

local row = tbl:tag('tr') row:tag('th'):css("max-width", "350px"):wikitext('Map') row:tag('th'):wikitext('Special Map Type') row:tag('th'):wikitext('Rewards') row:tag('th'):css("min-width", "160px"):wikitext('Availability')

for _, v in ipairs(maps) do		row = tbl:tag('tr') local args = {} args.link = v.page if mw.ustring.sub(v.Map, 1, 1) == 'V' then args.file = vbanners[v.Map] and vbanners[v.Map] or 'Banner V0001.webp' elseif v.kind == 'Rival Domains' then args.bannerName = 'Rival Domains' local num = tonumber(mw.ustring.match(v.Map, '^Q(%d+)$')) if num and num >= 9 then args.bannerName = ('Q%04d'):format((num - 1) % 4 + 1) end elseif v.kind == 'Relay Defense' then args.bannerName = 'R0001' end row:tag('td'):wikitext(frame:expandTemplate {title = 'Banner HB', args = args}) local kind = cargo.query('_pageData', ("CONCAT('%s')=link"):format(escq(v.kind)), {			where = ("_pageName = '%s' OR _pageName = '%s (Event)'"):format(escq(v.kind), escq(v.kind)),			limit = 1		})[1] row:tag('td'):wikitext(kind and kind.link or v.kind) row:tag('td'):wikitext(v.reward) row:tag('td'):wikitext(Datetime.ht_range(v.t0, v.t1)) end return tbl end

local byYear = function (args, frame) local year = args[1] local maps = cargo.query('Maps,MapDates',	"Maps._pageName=page,StartTime=t0,EndTime=t1,Maps.MapGroup=kind", {		join = 'Maps._pageName=MapDates._pageName',		where = ("(MapDates.StartTime BETWEEN '%d-01-01T07:00:00Z' AND '%d-01-01T06:59:59Z') AND MapDates.EndTime!='%s' AND (Maps.Map LIKE 'T%%' OR Maps.Map LIKE 'L%%')"):format( year, year + 1, Datetime.MAX_TIME),		orderBy = "t0 DESC,Maps.Map DESC",		limit = 1000,	}) local firstAppearance = cargo.query('Maps,MapDates',	"Maps._pageName=page,StartTime=t0", {		join = 'Maps._pageName=MapDates._pageName',		where = "Maps.Map LIKE 'T%%' OR Maps.Map LIKE 'L%%'",		groupBy = "Maps._pageName",		orderBy = "t0",		limit = 1000,	}) local summoningFocuses = cargo.query('SummoningEvents', "_pageName=page,StartTime=t0,EventType=kind", {		where = "EventType IN ('Bound Hero Battle','Grand Hero Battle','Legendary','Mythic')",	}) for _, v in ipairs(maps) do		for _, vs in ipairs(firstAppearance) do			if v.page == vs.page then v.firstAppearance = vs.t0			end end for _, vs in ipairs(summoningFocuses) do			if vs.kind == 'Legendary' or vs.kind == 'Mythic' then vs.kind = vs.kind .. ' Hero Battle' end if v.t0 == vs.t0 and vs.kind == v.kind and ((v.kind ~= 'Legendary Hero Battle' and v.kind ~= 'Mythic Hero Battle') or v.t0 == v.firstAppearance) then v.focus = 'Focus' end end if v.firstAppearance ~= v.t0 and v.kind ~= 'Mythic Hero Battle' and v.kind ~= 'Legendary Hero Battle' then local y, m = Datetime.parse_cargo(v.t0) v.quest = 'Quests' end end local tbl = mw.html.create('table'):addClass('wikitable'):addClass('default'):css('text-align', 'center')

local row = tbl:tag('tr') row:tag('th'):css("max-width", "350px"):wikitext('Map') row:tag('th'):css("min-width", "160px"):wikitext('Availability') row:tag('th'):wikitext(frame:expandTemplate {title = 'Hover', args = {'Additional feature', 'Additional feature related to the event'}}) for _, v in ipairs(maps) do		row = tbl:tag('tr') if v.firstAppearance == v.t0 then row:tag('td'):wikitext(frame:expandTemplate {title = 'Banner HB', args = {link = v.page}}) elseif Datetime.from_cargo(v.t1) - Datetime.from_cargo(v.t0) < 86400 then row:tag('td'):wikitext(frame:expandTemplate {title = 'Banner HB', args = {link = v.page, optionalText = 'Challenge'}}) else row:tag('td'):wikitext(frame:expandTemplate {title = 'Banner HB', args = {link = v.page, optionalText = 'Revival'}}) end row:tag('td'):wikitext(Datetime.ht_range(v.t0, v.t1)) local cell = row:tag('td') if v.focus then cell:wikitext(v.focus) end if v.quest then cell:wikitext(v.quest) end if not v.focus and not v.quest then cell:wikitext('—') end end return tbl end

local byGroup = function (args, frame) local group = args[1] local showHero = toboolean(args.hero) local showDesc = toboolean(args.desc) local showFocus = args.focus

local maps = cargo.query('Maps=M,MapDescriptions=D,MapDates=MD', 'M._pageName=page,M.Map=Map,M.HasReward=reward,D.Description=desc', {		join = 'M._pageName=D.Map,M._pageName=MD._pageName',		where = ("M.MapGroup='%s'"):format(escq(group)),		groupBy = 'M._pageName',		orderBy = 'MD.StartTime,M.Map',		limit = 100,	}) local mapDates = List.group_by(cargo.query('Maps=M,MapDates=D', "M._pageName=page,StartTime=t0,EndTime=t1,CycleTime=cycle,AvailTime=avail", { join = 'M._pageName=D._pageName', where = ("M.MapGroup='%s'"):format(escq(group)), orderBy = 't0', limit = 500, }), function (v) return v.page end) local unitsByMap = showHero and List.group_by(cargo.query( 'Maps=M,MapUnits=MU,Units=U', "M._pageName=mpage,U._pageName=upage,U.Name=uname", { join = 'M._pageName=MU._pageName,MU.Unit=U.WikiName', where = ("M.MapGroup='%s' AND U.WikiName IS NOT NULL AND (U.Properties__full IS NULL OR U.Properties__full NOT LIKE '%s')"):format(escq(group), '%generic%'), groupBy = 'MU._pageName,MU.Unit', orderBy = 'MU.Slot,uname', limit = 2000, }), function (v) return v.mpage end) or nil local summoningFocuses = showFocus and List.group_by(cargo.query( 'SummoningEvents=SE,SummoningEventFocuses=SEF,Units=U', "SE._pageName=page,SE.Name=name,IFNULL(CONCAT(U.Name,': ',U.Title),U.Name)=hero,SE.StartTime=t0", { join = 'SE.WikiName=SEF.WikiName,SEF.Unit=U.WikiName', where = (showFocus == 'Mythic' or showFocus == 'Legendary') and ("SE.EventType='%s' AND DATE(SE.StartTime)=DATE(U.ReleaseDate)"):format(showFocus) or ("SE.EventType='%s'"):format(showFocus), groupBy = "hero,page", limit = 500, }), function (v) return v.page, v end) or nil if summoningFocuses then for i, v in pairs(summoningFocuses) do			v.mpage = focusToMap(v[1], showFocus) end end

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

local row = tbl:tag('tr') row:tag('th'):css("max-width", "350px"):wikitext('Map') row:tag('th'):wikitext('Rewards') if showHero then row:tag('th'):wikitext('Heroes') end if showDesc then row:tag('th'):wikitext('Description') end row:tag('th'):css("min-width", "160px"):wikitext('Availability') if showFocus then row:tag('th'):wikitext('Summoning event') end for _, v in ipairs(maps) do		row = tbl:tag('tr') if mw.ustring.sub(v.Map, 1, 1) == 'V' then row:tag('td'):wikitext(frame:expandTemplate {title = 'Banner HB', args = {link = v.page, file = vbanners[v.Map] and vbanners[v.Map] or 'Banner V0001.webp'}}) else row:tag('td'):wikitext(frame:expandTemplate {title = 'Banner HB', args = {link = v.page}}) end row:tag('td'):wikitext(v.reward) if showHero then local cell = row:tag('td') if unitsByMap[v.page] then for _, unit in ipairs(unitsByMap[v.page]) do					cell:tag('div'):css('display', 'inline-block'):css('width', '70px'):css('vertical-align', 'top'):css('word-wrap', 'break-word') :wikitext((' %s'):format( mf(unit.upage), unit.upage, unit.upage, unit.uname)) end end end if showDesc then desc = string.gsub(v.desc, '(%[%[Fire Emblem.-%]%])', "%1") row:tag('td'):wikitext(desc) end row:tag('td'):wikitext(table.concat(List.map(mapDates[v.page] or {}, function (v)			if Datetime.from_cargo(v.t1) == 0x7FFFFFFF then				local cycle_count = 1 + math.floor((os.time - Datetime.from_cargo(v.t0) - v.avail) / v.cycle)				local next_t0 = Datetime.from_cargo(v.t0) + v.cycle * cycle_count				return ('Available cyclically since %s Next availability: %s'):format( Datetime.ht(v.t0), Datetime.ht_range(next_t0, next_t0 + v.avail - 1))			else				return Datetime.ht_range(v.t0, v.t1)			end		end), ' ')) if showFocus then local cell = row:tag('td') if summoningFocuses then local focusFound = false for _, focus in pairs(summoningFocuses) do					if focus.mpage and focus.mpage.name == v.page then focusFound = true for _, unit in ipairs(focus) do							cell:wikitext(frame:expandTemplate {title = 'UnitIcon', args = {name = unit.hero, size = '63px'}}, ' ') end cell:wikitext(' ', focus[1].name, '') end end if not focusFound then cell:wikitext('None') end end end end

return tostring(tbl) end

return require 'Module:MakeMWModule'.makeMWModule { byGroup = byGroup, byYear = byYear, newMaps = newMaps }