Module:MiniEvent

--[[ Lua script to get Mini Event information

getME: get Mini Event based on input parameters Parameters: 1 = name    : Mini Event name (defaults to PAGENAME) 2 = number  : Event Number (multiple Numbers can be specified                    separated with ",") 3 = options : One or more Output Options Some Options are mutually exclusive --- Fixed Formats The priority is in the below order Only the first ME number will be displayed C = Fixed Text ME Count Till Today E = Event Page Format (Table Row: Columns= Icon, Short Name, Dates w/o year) T = Timeline Page Format (Table Columns: Icon + Short Name #, Dates with year) (Parameter 4 can be  for italic and ' for bold') K = toKen Page Format (Table Row: Columns= Icon, Full Name Sub Text) (sub text in parameter 4) L = Latest Info Main Page Format (Short Name with small Date) G = NPC Generated for Attraction Page R = Rewards (milestone/leaderboard) Parameter 5 specifies the reward type First character M for milestone, L for leaderboard Second character/digit for milestone # / leaderboard pos If the ME is part of LTE, it is automatically added U = Single Event Number entry on the Mini Event Page If the Current Page is not the Mini Event Page, the ME name is displayed as link to the ME page. --- Customizable Display Format Options F = Full ME Name 9 = Include ME Number S = Short ME Name D = Append ME Date(s) A = Alternate Text (parameter 4) 4 = alt text : Alternate Text / Sub Text / bold-italic

getLastME: get Last X Events (formatted for main page) Output Format is Fixed ("L") Parameter: 1 = Count : return Last Count Events (default 5)

getEventME: get Mini Events during LTE Output Format is Fixed ("E") Parameters: 1 = LTE name : Limited Time Event Name

getMEList: get a List of Mini Events in horizontal format Output Format is Fixed ("H") Parameters: None

getMETimeLine: get a Complete Time Line of Mini Events Output Format is Fixed ("T") Parameters: None

getMENPCUsage: get a list of Mini Events using NPC Output is Fixed Text By default only Lists ME with special NPC version or during LTE Parameters: 1 = NPC Name : Non-Playable Character name 2 = All : If present include all NPC usage getMEHeader: get the Mini Event page header Mini Event name is the page name Output is Fixed Text Parameters: 1 = ME Objective (Text) %n or %N are replaced with NPC name 2 = ME Name (default PAGENAME) mainly for testing --]]

local p = {}

local meData = mw.loadData( 'Module:MiniEvent/data' ) local npc = require('Module:NPC') local lte = require("Module:LTEvent").getLTE local coll = require("Module:ItemCollection").getCollection local date = require('Dev:Date') local util = require('Module:Utility')

-- Get the Event Start time from the utility module local dayStart = util.dayStart

-- local functions --

-- function get single event local function singleEvent(sName, sNumber, sOption, altText, sReward) local eventName = "Unknown Event" local eventNumb = {} local eventDate = "" local eventDays = nil local eventMain = nil local npcVer   = nil local eventRuns = nil

local linkText = "Work In Progress" local linkPage = "" local linkHash = ""

-- save Event table and first Number table for later local tEvent = {} local tNumb = {}

if meData[sName] then -- ME-Type Data table tEvent = meData[sName]

eventName = tEvent["Name"] or "Data Error" imageNPC = tEvent["Icon"] or "" nameNPC  = tEvent["NPC"] or "" linkText = eventName .. " Mini Event"

for n in string.gmatch(sNumber, "%d+") do           if tonumber(n) == 0 then eventRuns = true end if tEvent[tonumber(n)] then table.insert(eventNumb, tonumber(n)) end end

if tEvent["Disp"] then eventName = tEvent["Disp"] end

if eventNumb[1] then -- ME# data table tNumb = tEvent[eventNumb[1]] eventDate = tNumb[1] or "" eventDays = tNumb[2] or 0 eventMain = tNumb[3] npcVer   = tNumb[4]

-- check if the ME page has parts if tEvent["Page"] then for i, n in ipairs(tEvent["Page"]) do					if eventNumb[1] < n then linkPage = "/Part " .. tostring(i) break end end end -- check if npcVer is "none", in case LTE but no special version if npcVer and npcVer == "none" then npcVer = nil end

-- check for alternate NPC if tNumb[5] and tEvent["NPC" .. tNumb[5]] then nameNPC = tEvent["NPC".. tNumb[5]] end linkHash = "#" .. eventName .. " #"               .. tostring(eventNumb[1]) end end

local outText = {  "", "", "", "", "", "", "", "", "", "",                       "",                        eventName, "", "", "", "", "", "", "", "", "",                       "", "", "", "", "\n" }

if meData[sName]["NPC"] == "TTC" then outText[10] = " prize to win in " outText[12] = meData[sName]["Name"] outText[13] = "" outText[14] = "#Mini Events" outText[16] = coll({meData[sName]["Collection"], "N"}) if not outText[16]:match("s$") then outText[17] = "'s Tower Challenge Chapter" else outText[17] = "' Tower Challenge Chapter" end outText[18] = " Mini Event" end if string.match(sOption, "[ETKLGRU]") then -- Fixed Formats if string.match(sOption, "E") then -- Event Page Format outText[1] = "|-\n| style=\"text-align: center;\" width=\"1\"|"

if nameNPC ~= "" then outText[5] = npc.getNPC({nameNPC, nil, nil, nil, eventMain or npcVer}) end

outText[6] = "\n|" outText[20] = "\n|"

if eventDate ~= "" then outText[23] = util.strDate(eventDate, eventDays, true) end

elseif string.match(sOption, "T") then -- TimeLine Page Format if meData[sName]["NPC"] == "TTC" then outText[10] = "" end outText[1] = "|"

if nameNPC ~= "" then outText[5] = npc.getNPC({nameNPC, nil, nil, nil, eventMain or npcVer}) end

outText[6] = " " outText[7] = altText

if eventNumb[1] and eventDate ~= "" then if meData[sName]["NPC"] == "TTC" then outText[16] = "Chapter" end outText[17] = " #" outText[18] = tostring(eventNumb[1]) end

outText[21] = altText outText[22] = "\n|"

if eventDate ~= "" then outText[23] = util.strDate(eventDate, eventDays) end

elseif string.match(sOption, "K") then -- Token Page Activity Format outText[1] = "|-\n| style=\"text-align: center;\"|"

if nameNPC ~= "" then outText[5] = npc.getNPC({nameNPC, nil, nil, 50, eventMain or npcVer}) end

outText[6] = "\n| colspan=\"5\" align=\"left\"|" outText[17] = " Mini Event" outText[20] = " ''* " outText[21] = altText outText[24] = "'' " outText[30] = ""

elseif string.match(sOption, "L") then -- Latest Info Main Page Format outText[1] = "* "

if meData[sName]["NPC"] == "TTC" then outText[10] = "" outText[17] = outText[17] .. " #"               outText[18] = tostring(eventNumb[1]) end

if eventDate ~= "" then outText[22] = " " outText[23] = util.strDate(eventDate, 0, nil, true) outText[24] = " " end

elseif string.match(sOption, "G") then -- Latest Info Main Page Format outText[2] = npc.getNPC({nameNPC, nil, nil, nil, eventMain or npcVer}) .. " "           outText[3] = mw.title.getCurrentTitle.text outText[4] = " generated " outText[6] = " during the "

if nameNPC ~= "" then outText[5] = npc.getNPC({nameNPC, "L"}) end if eventNumb[1] then outText[7] = util.ordinal(eventNumb[1]) if eventRuns then outText[4] = " generates " outText[6] = " since the " elseif eventNumb[2] then outText[6] = " from the " outText[8] = " to the " outText[9] = util.ordinal(eventNumb[2]) end outText[10] = " " end outText[17] = " Mini Event" outText[30] = "." elseif string.match(sOption, "R") then -- Reward format local mile, pos = string.match(sReward, "^([LM])(.*)") local vars = { ["P"] = mw.title.getCurrentTitle.text:gsub("s?$","s",1), ["p"] = mw.title.getCurrentTitle.text:gsub("s?$","",1), }

outText[2] = npc.getNPC({nameNPC, nil, nil, nil, eventMain or npcVer}) .. " "           if altText and altText ~= "" then outText[3] = altText:gsub("%%(.)", vars) else outText[3] = mw.title.getCurrentTitle.text end local today = os.date("%Y/%m/%d") if eventDate > today then outText[4] = " will be" elseif string.match(outText[2], "s$") then outText[4] = " were" else outText[4] = " was" end

local pos1, posTo, pos2 = string.match((pos or ""), "(%d+)([,%-]?)(%d*)") pos1 = tonumber(pos1) pos2 = tonumber(pos2)

if pos1 then outText[5] = " the " outText[6] = util.wordinal(pos1)

if pos2 then outText[7] = ((posTo == "-") and " to " or " and ") .. util.wordinal(pos2) end if mile == "L" then outText[8] = " tier" end else outText[5] = " a"           end

outText[9] = (mile == "M") and " milestone" or " leaderboard" outText[10] = " prize to win in the " if meData[sName]["NPC"] ~= "TTC" then outText[17] = " Mini Event" end

if eventNumb[1] and eventDate ~= "" then local sep = ""

for idx = #eventNumb, 1, -1 do                    outText[18] = " #" .. eventNumb[idx] .. sep .. outText[18] if sep == "" then sep = " and" else sep = "," end end outText[22] = " on the " local tDates = {} for _, n in ipairs(eventNumb) do                   table.insert(tDates, meData[sName][n][1]) end outText[23] = util.strMultiDate(tDates) end

if eventMain and meData[sName]["NPC"] ~= "TTC" then outText[24] = ", during " outText[25] = lte({eventMain}) end outText[30] = "." elseif string.match(sOption, "U") then -- Single Event on ME Page outText[1] = "'''" if meData[sName]["NPC"] == "TTC" then outText[13] = "" outText[14] = "" end outText[20] = "'''"

-- check for self-link if linkText == mw.title.getCurrentTitle.baseText then outText[11] = "" outText[12] = "" outText[13] = "" outText[14] = "" outText[15] = "" outText[19] = "" end

if eventNumb[1] then if meData[sName]["NPC"] == "TTC" then outText[10] = "" outText[16] = "Chapter #" outText[17] = tostring(eventNumb[1]) outText[18] = " Mini Event" else outText[17] = " #" outText[18] = tostring(eventNumb[1]) end outText[23] = util.strDate(eventDate, eventDays, nil, nil, true) end if eventMain and meData[sName]["NPC"] ~= "TTC" then local today = date local startDate = date(eventDate .. " " .. dayStart) local endDate = startDate:copy:adddays(eventDays) if today < startDate then outText[24] = ". This mini event will be part of the " elseif today < endDate then outText[24] = ". This mini event is part of the " else outText[24] = ". This mini event was part of the " end outText[25] = lte({eventMain}) end if altText and altText ~= "" then outText[26] = ", " outText[27] = altText end

if tNumb[6] then local tSpawn = { ".\n* ", "NPCs", " are spawned in groups of ", (tostring(tNumb[6]) or "?"), " every ", (tostring(tNumb[7]) or "?"), (((tNumb[8] and tNumb[8] == "m") and " minutes") or " hours"), ", a total of ", "?",					" this special event" }           	if nameNPC ~= "" then tSpawn[2] = npc.getNPC({nameNPC, "L"}) end if date < date(eventDate .. " " .. dayStart) then tSpawn[3] = " could spawn in groups of " end

if type(tNumb[7]) == "number" and tNumb[7] > 0 then local nCount = (tonumber(tNumb[6]) or 0) * (tonumber(tNumb[2]) or 0) * 24 if tNumb[8] and tNumb[8] == "m" then nCount = nCount * 60 end nCount = nCount / tNumb[7] nCount = tostring(nCount) while true do 						nCount, k = string.gsub(nCount, "^(-?%d+)(%d%d%d)%.?%d*", '%1,%2') if (k==0) then break end end

if tNumb[9] then local mec = require("Module:EC").getEC tSpawn[8 ] = "" tSpawn[9 ] = ". The re-spawn wait could be skipped by using " tSpawn[10] = mec({"Gems", "N", nil, nil, "15"}) else tSpawn[9] = tostring(nCount) end end

outText[28] = table.concat(tSpawn) end

if (tEvent["Themed"]) and (eventMain or npcVer) and tNumb[4] ~= "none" then local tThemed = { ".\n* ", "",					nameNPC,					"", " were ", "",				}           	if eventMain ~= nil then tThemed[6] = npc.getNPC({nameNPC, "X", nil, nil, eventMain}) end if npcVer ~= nil then tThemed[6] = npc.getNPC({nameNPC, "X", nil, nil, npcVer}) end

outText[28] = table.concat(tThemed) end outText[30] = "." end else -- Normal Mode

-- check for sel-link if linkText == mw.title.getCurrentTitle.baseText then outText[11] = "'''" outText[12] = "" outText[13] = "" outText[14] = "" outText[15] = "" outText[19] = "'''" end

if string.match(sOption, "9") then if not string.match(sOption, "S") then outText[17] = " Mini Event" end

if eventNumb[1] and eventDate ~= "" then local sep = ""

for idx = #eventNumb, 1, -1 do                    outText[18] = " #" .. eventNumb[idx] .. sep .. outText[18] if sep == "" then sep = " and" else sep = "," end end end elseif string.match(sOption, "A") and altText ~= "" then outText[16] = altText else if not string.match(sOption, "S") then outText[17] = " Mini Event" end end

if string.match(sOption, "D") then outText[22] = " on the "

local tDates = {}

for _, n in ipairs(eventNumb) do               table.insert(tDates, meData[sName][n][1]) end

outText[23] = util.strMultiDate(tDates) end outText[30] = "" end

return table.concat(outText) end

local function getCount(sName) local nCount = 0

local today = date:fmt("%Y/%m/%d %H:%M:%S") if meData[sName] then for idx, data in ipairs(meData[sName]) do           if today > (data[1] .. dayStart) then nCount = nCount + 1 end end end

return "* This mini event has occurred " .. nCount .. " time" .. ((nCount > 1) and "s" or "") .. ".\n" end

-- Public Functions --

-- function get Mini Event function p.getME(frame) local tArgs = util.getArgs(frame)

local sName   = tArgs[1] or "" local sNumber = tArgs[2] or "" local sOption = tArgs[3] or "" local altText = tArgs[4] or "" local sReward = tArgs[5] or ""

if sName == "" then sName = mw.title.getCurrentTitle.baseText end sName = sName:lower:gsub("^%s*(.-)%s*$", "%1") or '' sName = sName:gsub(" mini event$", "") sName = mw.text.decode(sName, false)

sOption = tostring(sOption) sOption = sOption:upper

if sOption == "" then if sNumber ~= "" then sOption = "S" end end

local retText = ""

if string.match(sOption, "X") then if meData[sName] and meData[sName]["NPC"] then retText = npc.getNPC({meData[sName]["NPC"], "L"}) .. " ("   				.. npc.getNPC({meData[sName]["NPC"]})    				.. ")" end elseif string.match(sOption, "P") then if meData[sName] and meData[sName]["NPC"] then retText = meData[sName]["NPC"] end elseif string.match(sOption, "N") then if meData[sName] and meData[sName]["NPC"] then retText = npc.getNPC({meData[sName]["NPC"], "", "", "30"}) end elseif string.match(sOption, "C") then retText = getCount(sName) else retText = singleEvent(sName, sNumber, sOption, altText, sReward) end return retText end

-- return last X versions formatted for Main Page function p.getLastME(frame) local tArgs = util.getArgs(frame)

local count = tArgs[1] or 5 count = tonumber(count) or 5

local tIndex = {}

local today = date:fmt("%Y/%m/%d %H:%M:%S")

for k, v in pairs(meData) do       if not (k:lower:match("^ttc")) then for idx, data in ipairs(v) do               if today > (data[1] .. dayStart) then local t = {data[1], k, idx} table.insert(tIndex, t)               end end end end

table.sort(tIndex, function(a,b) return a[1] > b[1] end)

local outText = {}

for i, ver in ipairs(tIndex) do       table.insert(outText, singleEvent(ver[2], ver[3], "L", "")) if i >= count then break end end

return table.concat(outText) end

function p.getUpcomingME(frame) local tArgs = util.getArgs(frame)

local count = tArgs[1] or 5 count = tonumber(count) or 5

local tIndex = {}

local today = date:fmt("%Y/%m/%d %H:%M:%S")

for k, v in pairs(meData) do       for idx, data in ipairs(v) do            if today < (data[1] .. dayStart) then local t = {data[1], k, idx} table.insert(tIndex, t)           end end end

table.sort(tIndex, function(a,b) return a[1] < b[1] end)

local outText = {}

for i, ver in ipairs(tIndex) do       table.insert(outText, singleEvent(ver[2], ver[3], "L", "")) if i >= count then break elseif i == "0" then return "''There is no Upcoming Events for now..." end end

return table.concat(outText) end

-- return all ME during Limited Time Event formatted for Event Page function p.getEventME(frame) local tArgs = util.getArgs(frame)

local event = tArgs[1] or ""

-- Does not make sense if no Event specified so return blank if event == "" then return "" end

event = string.lower(event)

local tIndex = {}

for k, v in pairs(meData) do       if not (k:lower:match("^ttc")) then for idx, data in ipairs(v) do               if event == data[3] then local t = {data[1], k, idx} table.insert(tIndex, t)               end end end end

table.sort(tIndex, function(a,b) return a[1] < b[1] end)

local outText = {}

for i, ver in ipairs(tIndex) do       table.insert(outText, singleEvent(ver[2], ver[3], "E", "")) end

return table.concat(outText) end

-- return all ME Types in horizontal format function p.getMEList local tIndex = {}

for k, v in pairs(meData) do   	if not (k:lower:match("^ttc")) then table.insert(tIndex, k)       end end

table.sort(tIndex)

local outText = {} local space = ""

for _, me in ipairs(tIndex) do       table.insert(outText, space .. singleEvent(me, 0, "S", ""))

space = " · " end

return table.concat(outText) end

function p.getMETimeLine local tIndex = {} local tMainEvents = {} local nCount = 0 local nEventCount = 0

local today = date:fmt("%Y/%m/%d %H:%M:%S") local lastEvent = nil local nextEvent = nil

for k, v in pairs(meData) do   	local lastOfType = nil for idx, data in ipairs(v) do       	if data[1] then if today > (data[1] .. dayStart) then if not lastEvent or data[1] > lastEvent[1] then lastEvent = data end else if not nextEvent or data[1] < nextEvent[1] then nextEvent = data end end end

table.insert(tIndex, {data[1], k, idx, data[3], nil}) if data[3] then if not tMainEvents[data[3]] then tMainEvents[data[3]] = 0 end tMainEvents[data[3]] = tMainEvents[data[3]] + 1 end if data[1] then if today > (data[1] .. dayStart) then lastOfType = #tIndex nCount = nCount + 1 if data[3] then nEventCount = nEventCount + 1 end end end end if lastOfType then tIndex[lastOfType][5] = true end end

table.sort(tIndex, function(a,b) return a[1] > b[1] end)

local prevMain = "" local future = nil local during = nil

if tMainEvents[lastEvent[3]] and nextEvent and lastEvent[3] == nextEvent[3] then tMainEvents[lastEvent[3]] = tMainEvents[lastEvent[3]] + 1 during = true end

local outText = {} table.insert(outText,               "{| class=\"article-table\" style=\"width: 100%;\"\n"                .. "! width=\"27%\"|Major Event\n"                .. "! width=\"33%\"|Mini Event\n"                .. "! width=\"40%\"|Date(s)\n")

for i, data in ipairs(tIndex) do if not future and today > (data[1] .. dayStart) then future = true table.insert(outText, "|-\n! colspan=\"")			if during then				table.insert(outText, "2")			else				table.insert(outText, "3")			end			table.insert(outText, "\" style=\"text-align: center;\"|Upcoming Events...\n") end table.insert(outText, "|-\n")

if data[4] then local eventName = data[4] local eventType = "N" if data[2]:lower:match("^ttc") then eventName = string.gsub(data[4], "ttc ", "") eventType = "T" end if eventName ~= prevMain then table.insert(outText,                           "| rowspan=\""                            .. tMainEvents[data[4]]                            .. "\" |"                            .. lte({eventName, eventType, "P"})                            .. "\n")

prevMain = eventName end else table.insert(outText, "|\n") prevMain = "" end

if data[5] and not (data[2]:lower:match("^ttc")) then table.insert(outText, singleEvent(data[2], data[3], "T", "'''")) else table.insert(outText, singleEvent(data[2], data[3], "T", "")) end end

table.insert(outText, "|}\n\n==Trivia==\n"           .. "* There has been a total of "            .. nCount            .. " mini events, out of which "            .. nEventCount            .. " occurred during a major event, while "            .. (nCount - nEventCount)            .. " occured separately.\n")

return table.concat(outText) end

function p.getMENPCUsage(frame) local tArgs = util.getArgs(frame)

local npcName = tArgs[1] or "" local allVers = (tArgs[2] and tArgs[2] ~= "")

-- return if no NPC specified if npcName == "" then return "No NPC Name Specified\n" end

local tIndex = {} local tMainEvents = {}

local nCount = 0 local nEventCount = 0 local npcThemed = nil

for k, v in pairs(meData) do       if not (k:lower:match("^ttc")) then npcThemed = (v["Themed"] ~= nil)

for idx, data in ipairs(v) do               local meNPC = v["NPC"]

-- check alternate NPC if data[5] and v["NPC" .. data[5]] then meNPC = v["NPC" .. data[5]] end

if npcName == meNPC then local vers = data[3] or data[4]

if vers or allVers then local themed = npcThemed and (data[3] ~= nil or data[4] ~= nil) if (data[4] and data[4]=="none") then themed = nil end

local t = {data[1], k, idx, data[3], vers, themed} table.insert(tIndex, t)                   end end end end end

table.sort(tIndex, function(a,b) return a[1] < b[1] end)

local prevData = tIndex[1] local prevNumb = ""

local outText = {}

local function oneLine local themed = prevData[5] and prevData[6] table.insert(outText, "* The "               .. npc.getNPC({npcName, "D", nil, nil, prevData[5]})                .. " were used for the ") if prevData[4] then table.insert(outText,                    singleEvent(prevData[2], prevNumb, "F", "")                    .. "s during the "                    .. lte({prevData[4], "N", "D"})                    .. ".\n") else table.insert(outText,                   singleEvent(prevData[2], prevNumb, "FD", "")                    .. ".\n") end end for i, data in ipairs(tIndex) do       if data[4] ~= prevData[4] or data[5] ~= prevData[5] then oneLine prevNumb = "" end prevData = data prevNumb = prevNumb .. " " .. data[3] end

if prevNumb ~= "" then oneLine end

return table.concat(outText) end

function p.getGallery(frame) local tArgs = util.getArgs(frame)

local npcName = tArgs[1] or "No NPC Name Specified\n"

local tIndex = {} local tMainEvents = {}

local nCount = 0 local nEventCount = 0 local npcThemed = nil

for k, v in pairs(meData) do       if not (k:lower:match("^ttc")) then npcThemed = (v["Themed"] ~= nil)

for idx, data in ipairs(v) do               local meNPC = v["NPC"]

-- check alternate NPC if data[5] and v["NPC" .. data[5]] then meNPC = v["NPC" .. data[5]] end

if npcName == meNPC then local vers = data[3] or data[4]

if vers then local themed = npcThemed and (data[3] ~= nil or data[4] ~= nil)

if (data[4] and data[4]=="none") then themed = nil end

local t = {data[1], k, idx, data[3], vers, themed} table.insert(tIndex, t)                   end end end end end

table.sort(tIndex, function(a,b) return a[1] < b[1] end)

local prevData = tIndex[1] local prevNumb = "" local outText = {"")

return frame:preprocess(table.concat(outText)) end

function p.getMEHeader(frame) local tArgs = util.getArgs(frame)

local meObj = tArgs[1] or "" local meName = tArgs[2] or ""

if meName == "" then meName = mw.title.getCurrentTitle.baseText end

meName = meName:lower:gsub(" [mM]ini [eE]vent$", "") meName = mw.text.decode(meName, false)

if not meData[meName] or not meData[meName]["Name"] then return "Invalid Mini Event Name" end

local outText = {}

table.insert(outText, "'''") table.insert(outText, (meData[meName]["Disp"] or meData[meName]["Name"])) table.insert(outText, "'''") table.insert(outText, " is a mini event where ")

if meObj ~= "" then meObj = meObj:gsub("%%([nN])", npc.getNPC({meData[meName]["NPC"], "L"})) table.insert(outText, meObj) else table.insert(outText, npc.getNPC({meData[meName]["NPC"], "L"})) table.insert(outText, " have invaded your Kingdom! Clear them to win prizes!") end table.insert(outText, "\n\n== Trivia ==\n") table.insert(outText, getCount(meName)) table.insert(outText, npc.getFirstUse({meData[meName]["NPC"]}))

return table.concat(outText) end

return p