Module:CurrentEvents

local cargo = mw.ext.cargo local Datetime = require 'Module:DatetimeUtil' 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 v.MapGroup == 'Limited Hero Battle' end, function (v) return true end, }

local EVENT_SOURCES = { {'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,IF(World='World of Heroes',1,0)=isSpoil"}, {'HallOfForms',    "CONCAT('Hall of Forms')=kind,     _pageName=page,          _pageName=name"}, {'MjolnirsStrike', "CONCAT('Mjölnir\\'s Strike')=kind,_pageName=page,          _pageName=name"}, {'FrontlinePhalanx',"CONCAT('Frontline Phalanx')=kind, _pageName=page,         _pageName=name"}, {'PawnsOfLoki',    "CONCAT('Pawns of Loki')=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)) else return ('Mins. left: %d'):format(math.floor(dt / 60)) end end

local eventEntry = function (banner, dt) local eventbox = mw.html.create('div'):css('vertical-align', 'middle'):css("margin","5px 35px"):css("display","inline-block") eventbox:tag('div'):addClass('img-responsive'):wikitext(banner) eventbox:tag('b'):wikitext(countdown(dt)) return eventbox end

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( Datetime.to_cargo(now), Datetime.MAX_TIME),		orderBy = 'StartTime,Map',		limit = 100,	}) List.concat_self(allMaps, cargo.query('LimitedMaps=LM,LimitedMaps__Entry=LME', "Map,CONCAT('Limited Hero Battle')=MapGroup,LM._pageName=Page,StartTime,EndTime,GROUP_CONCAT(LME._value SEPARATOR ',')=Entry", { join = 'LM._ID=LME._rowID', where = ("'%s' BETWEEN StartTime AND EndTime"):format(Datetime.to_cargo(now)), orderBy = 'StartTime,Map', groupBy = 'LM._ID', limit = 100, }))	for _, v in ipairs(allMaps) do		v.StartTime = Datetime.from_cargo(v.StartTime) v.EndTime = Datetime.from_cargo(v.EndTime) v.CycleTime = tonumber(v.CycleTime) v.AvailTime = tonumber(v.AvailTime) end List.keep_if(allMaps, function (v)		if not v.CycleTime or not v.AvailTime then			return true		end		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	end)

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

-- existing special map banners local vbanners = Hash.from_ipairs(cargo.query('_pageData', '_pageName', { where = "_pageName RLIKE '^File:Banner Vdigit:+\\.(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 local limited = 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) end elseif v.MapGroup == 'Relay Defense' then bannerName = 'R0001' elseif v.MapGroup == 'Limited Hero Battle' then limited = v.Entry end local banner = frame:expandTemplate {title = 'Banner HB', args = {v.Page, bannerName = bannerName, limited = limited}} return tostring(eventEntry(banner, v.EndTime - now)) end)) end

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(Datetime.to_cargo(now)), groupBy = '_pageName'} for _, v in ipairs(EVENT_SOURCES) do List.concat_self(allEvents, cargo.query(v[1], v[2] .. ',EndTime', queryArgs)) end for _, v in ipairs(allEvents) do		v.EndTime = Datetime.from_cargo(v.EndTime) end --	table.sort(allEvents, function (x, y) return x.EndTime < y.EndTime end)

return table.concat(List.map(allEvents, function (v) if v.kind == 'Lost Lore' and v.isSpoil == '1' then v.kind = 'Lost Lore Spoils' end 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 %s'):format(banner, v.page, v.name), v.EndTime - now)) end)) end

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