Module:MiniEvent

--[[ LUA script to get Mini Event information

getME: get Mini Event based on input parameters Parameters: 1 = name    : Mini Event name 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 Mine Event Page If the format Option also includes '9', then ME link is created instead of text only. --- 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 --]]

local p = {}

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

-- 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 ordinal(n) local numb = tonumber(n) or 0 local idd = math.fmod(numb,10) return (idd==1 and numb~=11 and "st") or (idd==2 and numb~=12 and "nd") or (idd==3 and numb~=13 and "rd") or (numb~=0 and "th") or "" end

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) local day = string.gsub(start:fmt("%d"), "^0", "") table.insert(retText, day)

if not cardinal then table.insert(retText, ordinal(day)) end

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

day = string.gsub(theDate:fmt("%d"), "^0", "") table.insert(retText, day) if not cardinal then table.insert(retText, ordinal(day)) end

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 day = string.gsub(curDate:fmt("%d"), "^0", "") local parts = { day, (not cardinal and ordinal(day) or ""), "", "", ""}

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

if monAnd then parts[5] = "; and " else parts[5] = " and " end monAnd = nil else if monAnd then parts[5] = ", " else parts[5] = " and " monAnd = true end end end else parts[3] = curDate:fmt(" %B") parts[4] = 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][5] == " and " and monAnd then multiDates[i][5] = "; and " end end end end

if multiDates[i][5] == " 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({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({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({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({nameNPC, "N"}) end if eventNumb[1] then outText[7] = eventNumb[1] .. 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] = eventNumb[2] .. 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)

local ordTable = {" first", " second", " third", " fourth", }

if pos1 and ordTable[pos1] then outText[5] = " the" outText[6] = ordTable[pos1]

if pos2 and ordTable[pos2] then outText[7] = ((posTo == "-") and " to" or " and") .. ordTable[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 outText[1] = "'''" outText[20] = "'''"

local basePage = mw.title.getCurrentTitle.baseText

if basePage == linkText 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 outText[24] = ". This mini event was part of the " 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({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 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 = os.date("%Y/%m/%d") if meData[sName] then for idx, data in ipairs(meData[sName]) do           if today >= data[1] then nCount = nCount + 1 end end end

return "This mini event has occurred " .. nCount .. " time" .. ((nCount == 1) and "" or "s")

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 ""

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

if string.match(sOption, "C") then return getCount(sName) else return singleEvent(sName, sNumber, sOption, altText, sReward) end 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 = os.date("%Y/%m/%d") for k, v in pairs(meData) do       if k ~= "ttc" then for idx, data in ipairs(v) do               if today >= data[1] 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 local t = {k, k, 0} table.insert(tIndex, t)       end end

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

local outText = {} local space = ""

for _, ver in ipairs(tIndex) do       table.insert(outText, space .. singleEvent(ver[2], 0, "S", ""))

space = " · " end

return table.concat(outText) end

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

for k, v in pairs(meData) do       for idx, data in ipairs(v) do            local t = {data[1], k, idx, data[3], nil} table.insert(tIndex, t)

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" then nCount = nCount + 1 if data[3] then nEventCount = nEventCount + 1 end end end tIndex[#tIndex][5] = true end

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

local prevMain = ""

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       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

local lte = require("Module:LTEvent").getLTE

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({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

return p

-- --