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 date = require('Dev:Date') local util = require('Module:Utility') -- This specifies the UTC time the events start -- Used to be 15:00:00 UTC, but currently has been changed to 16:00:00 UTC local dayStart = " 16:00:00"

-- local functions --

-- getArgs -- if no frame then local call -- otherwise frame arguments take precedence over parent arguments local function getArgs(frame) local tArgs = {}

if frame == mw.getCurrentFrame then tArgs = frame.args if not frame.args[1] then fParent = frame:getParent if (fParent.args[1]) then tArgs = fParent.args end end else tArgs = frame end

return tArgs end

-- Date Functions local function strDate(sDate, days, noYear, cardinal, whenTo) local theDate = date(sDate)

local nDays = tonumber(days) or 0

local day = "" local retText = {}

if whenTo then if theDate < date then table.insert(retText, " started on ") else table.insert(retText, " starts on ") end end

if nDays > 0 then local start = theDate:copy theDate:adddays(nDays) table.insert(retText, util.ordinal(start:getday, cardinal))

if whenTo or start:getmonth ~= theDate:getmonth then table.insert(retText, start:fmt(" %B")) end

if whenTo or (not noYear and start:getyear ~= theDate:getyear) then table.insert(retText, start:fmt(" %Y")) end

if whenTo then if theDate < date then table.insert(retText, " and ended on ") else table.insert(retText, " and will end on ") end else table.insert(retText, " - ") end end

table.insert(retText, util.ordinal(theDate:getday, cardinal))

table.insert(retText, theDate:fmt(" %B"))

if not noYear then table.insert(retText, theDate:fmt(" %Y")) end

if whenTo and nDays > 0 then table.insert(retText, ", lasting ") table.insert(retText, tostring(nDays)) if nDays > 1 then table.insert(retText, " days") else table.insert(retText, " day") end end return table.concat(retText) end

local function strMultiDate(tDates, cardinal) table.sort(tDates)

local multiDates = {}

local prevDate = nil

local monAnd = nil local yearAnd = nil

for i = #tDates, 1, -1 do       local curDate = date(tDates[i]) local parts = {util.ordinal(curDate:getday, cardinal), "", "", ""}

if prevDate then if prevDate:getyear ~= curDate:getyear then parts[2] = curDate:fmt(" %B") parts[3] = curDate:fmt(" %Y") if yearAnd then parts[4] = ", " else if monAnd then parts[4] = "; and " else parts[4] = " and " yearAnd = true end end monAnd = nil else if prevDate:getmonth ~= curDate:getmonth then parts[2] = curDate:fmt(" %B")

if monAnd then parts[4] = "; and " else parts[4] = " and " end monAnd = nil else if monAnd then parts[4] = ", " else parts[4] = " and " monAnd = true end end end else parts[2] = curDate:fmt(" %B") parts[3] = curDate:fmt(" %Y") end

multiDates[i] = parts prevDate = curDate end

prevDate = nil

monAnd = nil yearAnd = nil

for i = 1, #tDates do       local curDate = date(tDates[i])

if prevDate then if prevDate:getyear == curDate:getyear then if prevDate:getmonth == curDate:getmonth then if multiDates[i][4] == " and " and monAnd then multiDates[i][4] = "; and " end end end end

if multiDates[i][4] == " and " then monAnd = true end prevDate = curDate end

local retText = {}

for _, sDate in ipairs(multiDates) do       table.insert(retText, table.concat(sDate)) end

return table.concat(retText) end

-- function get single event local function singleEvent(sName, sNumber, sOption, altText, sReward) if sName == "ttc" then return "" end

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 string.match(sOption, "[ETKLGRU]") then -- Fixed Formats if string.match(sOption, "E") then -- Event Page Format outText[1] = "|-\n| 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] = strDate(eventDate, eventDays, true) end

elseif string.match(sOption, "T") then -- TimeLine Page Format 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 outText[17] = " #" outText[18] = tostring(eventNumb[1]) end

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

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

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

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

outText[6] = "\n| colspan=\"4\" 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 eventDate ~= "" then outText[22] = " " outText[23] = strDate(eventDate, 0, nil, true) outText[24] = " " end

elseif string.match(sOption, "G") then -- Latest Info Main Page Format outText[1] = "* " outText[2] = mw.title.getCurrentTitle.text outText[3] = " generated " outText[6] = " during the "

if nameNPC ~= "" then outText[5] = npc.getNPC({nameNPC, "N"}) end if eventNumb[1] then outText[7] = util.ordinal(eventNumb[1]) if eventRuns then outText[3] = " 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[1] = "* " if altText and altText ~= "" then outText[2] = altText:gsub("%%(.)", vars) else outText[2] = mw.title.getCurrentTitle.text end

if 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 " outText[17] = " Mini Event"

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] = strMultiDate(tDates) end

if eventMain then outText[24] = ", during " outText[25] = lte({eventMain}) end outText[30] = "." elseif string.match(sOption, "U") then -- Single Event on ME Page outText[1] = "'''" 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 outText[17] = " #" outText[18] = tostring(eventNumb[1]) outText[23] = strDate(eventDate, eventDays, nil, nil, true) end if eventMain then local today = date local startDate = date(eventDate) 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 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

tSpawn[9] = tostring(nCount) end

outText[28] = table.concat(tSpawn) 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] = 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 = 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, "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 = 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 k ~= "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

-- return all ME during Limited Time Event formatted for Event Page function p.getEventME(frame) local tArgs = 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 k ~= "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 k ~= "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 k ~= "ttc" and data[3] then if not tMainEvents[data[3]] then tMainEvents[data[3]] = 0 end tMainEvents[data[3]] = tMainEvents[data[3]] + 1 end

if k ~= "ttc" and 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 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[2] == "ttc" then table.insert(outText,                   "| colspan=\"3\"|"                    .. lte({data[4], "T", "P"})                    .. "\n") prevMain = "" else if data[4] then if data[4] ~= prevMain and tMainEvents[data[4]] then table.insert(outText,                           "| rowspan=\""                            .. tMainEvents[data[4]]                            .. "\" |"                            .. lte({data[4], "N", "P"})                            .. "\n")

prevMain = data[4] end else table.insert(outText, "|\n") prevMain = "" end

if data[5] then table.insert(outText, singleEvent(data[2], data[3], "T", "'''")) else table.insert(outText, singleEvent(data[2], data[3], "T", "")) end 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 = 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 k ~= "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.getMEHeader(frame) local tArgs = 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

-- --