Module:SoS

local p = {} local data = require("Module:Datatable") local sosData = require("Module:Datatable/SoS") local utils = require("Module:Utils") local mPerks = require("Module:Perks") local frame = mw.getCurrentFrame local nl = "\n" local ntl = "|-" -- new table line local _inception = {year = 2016, month = 10, day = 26} local _day = 24 * 60 * 60 --hours * minutes * seconds local _week = 7 * _day local _soSCountTableColumns = 6 local inceptionColor = "3bg" --clr template value local _countOfPerksWithHigherPrice

local strings = { file = "File", -- teachable = "Teachable", -- foreignLang = false, --not used wrongCharType = "Error: There's an invalid CharType in Module:DataTable - table perks", icon = "Icon", name = "Name", weeksOf = "Weeks of", shrineCount = "Shrine Count", week = "Week", weeks = "weeks", weeksAgo = "ago", weekAgoString = "#result #weekString #weeksAgo", --weekAgoString = "#weeksAgo #result #weekString", --example for foreign language with different wording order currentlyInSoS = "Currently in Shrine of Secrets!", lastOcc = "Last Occ.", cost = "Cost", tPerk = "Teachable Perks", uPerkOf = "Perk Owners", refreshes = "refreshes in", hours = "hours", hour = "hour", days = "days", --day = "day", --not used unknownPerk = "Unknown Perk", unknown = "Unknown", }

regexTable = { ["#weeksAgo"] = strings.weeksAgo, ["#week"] = strings.week:lower, ["#weeks"] = strings.weeks }

price = { normal = 2000, high = 2700 }

function p.convertSoSIds -- this is just helping function generating a SoS list increased by 32 common and unused perks (it's not used in any other function) local modulePerks = require("Module:Perks") local result = {} for _, week in ipairs(sos) do			local newWeek = {} for i = 1, 4, 1 do			table.insert(newWeek, week[i] + modulePerks._nonUniquePerksCount) end table.insert(result, newWeek) end for _, week in ipairs(result) do mw.log("{" .. week[1] .. ", " .. week[2] .. ", " .. week[3] .. ", " .. week[4] .. "},") end end

-- Assembling SoS Table --

function p.AssembleSoSTable(sosPerks, showCountdown) showCountdown = showCountdown or false local result = "" SetGlobalCountOfPerksWithHigherPrice result = result .. '{| class = "sosTable"' ..nl .. ntl .. nl result = result .. '! class = "sosHeader" width = "256px" colspan = 2 | ' .. strings.tPerk .. nl result = result .. '! class = "sosHeader" width = "64px" | ' .. strings.cost .. nl result = result .. '! class = "sosHeader" width = "256px" colspan = 2 | ' .. strings.uPerkOf .. nl	for i, perk in ipairs(sosPerks) do result = result .. ntl .. ' class = "sosRow"' .. nl		if type(perk) == "table" and perk.character ~= nil and perk.charType ~= nil then result = result .. '! 64px|frameless|center' .. nl result = result .. '| class = "sosText" | ' .. utils.GetDisplayName(perk) .. '' .. nl result = result .. '| class="BG-All ISACBG-All ISBG SoS-IS" | ' .. GetPerkCostByPerk(perk) .. ' 48px ' .. nl result = result .. '| class = "sosText" | ' .. mPerks.getPerksOwnerNameByPerk(perk) .. '' .. nl result = result .. '! 64px|frameless|center' .. nl		else result = result .. '! 64px|frameless|center' .. nl result = result .. '| class = "sosText" | ' .. strings.unknownPerk .. nl result = result .. '| class="BG-All ISACBG-All ISBG SoS-IS" | - 48px ' .. nl result = result .. '| class = "sosText" | - ' .. nl result = result .. '! - ' .. nl		end end if showCountdown then result = result .. ntl .. nl result = result .. '! class = "sosHeader" colspan= 5 | ' .. strings.refreshes .. ' '.. utils.clr(p.GetSoSRemainingTime, 4) .. nl	end result = result .. '|}'

mw.log(result) return result end

function p.GetSoSRemainingTime local currentWeekDay = tonumber(os.date("%w")) local result = "" mw.log(os.date("Current weekday: %w")) if currentWeekDay == 2 then local remainingHours = 24 - os.date("%H") return remainingHours .. " " .. (remainingHours == 1 and strings.hour or strings.hours) elseif currentWeekDay > 2 then result = 10 - currentWeekDay .. " " .. strings.days --3 4 5 6 --weekday --7 6 5 4 --days remaining else result = 3 - currentWeekDay .. " " .. strings.days --day/days --0 1 --weekday --3 2 --days remaining end mw.log(result) return result end

function GetPerkCostByPerk(perk) if perk == 0 then return "-" end if perk.id <= mPerks._allPerksCount - _countOfPerksWithHigherPrice then return price.normal else return price.high end end

function GetSoSPerkImageByPerk(perk) if perk == 0 then return "Dbd-perks-template.png" end local name = utils.FirstLetterLower(utils.resolveFileName(perk.name, false)) return strings.teachable .. "_" .. name .. ".png" end

function GetSoSPerks(sos) result = {} for i = 1, 4, 1 do		table.insert(result, GetPerkById(sos[i])) end mw.log(mw.dumpObject(result)) return result end

function GetPerkById(id) local dataPerks = require("Module:Datatable/Perks") local modulePerks = require("Module:Perks") --mw.log(mw.dumpObject(modulePerks)) --mw.log('Type: ' .. type(perks)) for _, perk in ipairs(perks) do if perk.id == id then return perk end --TODO: Remove the temporary variable. Before that the IDs in Module:Datatable/SoS must by raised by this constant!! (32)	end return 0 end

function SetGlobalCountOfPerksWithHigherPrice for _,dlc in ipairs(dlcs) do		if not utils.IsFullDateTime(dlc.rDate) then _countOfPerksWithHigherPrice = 24 return 24 end end _countOfPerksWithHigherPrice = 18 return 18 end

-- #END# Assembling SoS Table #END# --

function sos.last return sos[#sos] end

function p.SoS(index) return p.AssembleSoSTable(GetSoSPerks(sos[#sos - tonumber(index.args[1])])) end

function SoS(index) return p.AssembleSoSTable(GetSoSPerks(sos[index])) end

function p.CurrentSoS return p.AssembleSoSTable(GetSoSPerks(sos[1]), true) end

function p.ManualSoS(args) --Unused? args = args.args p.AssembleSoSTable(GetSoSPerks(args)) end

--ISO: https://en.wikipedia.org/wiki/ISO_week_date --If 1 January is on a Monday, Tuesday, Wednesday or Thursday, it is in W01. If it is on a Friday, it is part of W53 of the previous year. --If it is on a Saturday, it is part of the last week of the previous year which is numbered W52 in a common year and W53 in a leap year. --If it is on a Sunday, it is part of W52 of the previous year. function GetWeekNumber(dateStamp) --in milliseconds, os.time firstWeekDayOfYear = tonumber(os.date("%w", os.time({year = os.date("%Y", dateStamp), month = 1, day = 1}))) firstWeekOfYear = tonumber(os.date("%W", os.time({year = os.date("%Y", dateStamp), month = 1, day = 1}))) -- ATTENTION as per description above some years have first week 01 -- because all first weeks of year that starts with Sunday, Saturday, Friday or Thursday must be shifted if firstWeekDayOfYear > 0 and firstWeekDayOfYear < 4 and firstWeekOfYear == 0 then return tonumber(os.date("%W", dateStamp)) + 1 else return tonumber(os.date("%W", dateStamp)) end end

function p.GetCurrentWeekNumber return GetWeekNumber(os.time) end

function GetEmptyPerkTable local result = {} if not emptyTable then for i = 1, #perks do result[i] = 0 end --make empty table with 0 values for every perk in sos table else for i = 1, #perks do result[i] = {} end end return result end

function GetCountOfPerks local perkList = GetEmptyPerkTable

for i, sosWeek in ipairs(sos) do		for j, sosPerk in ipairs(sosWeek) do			if type(sosPerk) ~= "table" then mw.logObject("sosPerk: " .. sosPerk) if perkList[sosPerk] == nil then --sosPerk, the perk ID will be an index of perkList table. Due to Lua logic there must not be a nil between items (that's why there is getEmptyPerkTable) perkList[sosPerk] = 1 --DEV TODO check if possible to remove it				else perkList[sosPerk] = perkList[sosPerk] + 1 end end end end return perkList end

function p.AssembleSoSPerkCountTable local SoSPerkCountTable = GetCountOfPerks local maxOccurrence = math.max(unpack(SoSPerkCountTable)) local result = "" local number utils.sortItemsByName(perks) result = result .. "{| class=\"wikitable unsortable\"" .. nl

local i = 1 for _, perk in ipairs(perks) do		if perk.character ~= nil then if (i - 1) % _soSCountTableColumns == 0 then result = result .. ntl .. nl			end number = SoSPerkCountTable[perk.id] result = result .. "! 64px|center" .. nl result = result .. "! " .. perk.name .. "" .. nl			if number ~= 0 then result = result .. "" .. strings.lastOcc .. ": " .. GetLastPerkOccurrenceById(perk.id) .. " " .. nl			end result = result .. "| '''" .. GetColoredOccurrenceNumber(number, maxOccurrence) .. "''' " .. nl			i = i + 1 end end result = result .. "|}"	--mw.log(mw.dumpObject(result)) return result end

function GetColoredOccurrenceNumber(number, maxOccurrence) if number == maxOccurrence then return frame:expandTemplate{title = "clr", args = {3, number}} elseif number == 0 then return frame:expandTemplate{title = "clr", args = {8, number}} elseif number == 1 then return frame:expandTemplate{title = "clr", args = {2, number}} elseif number == nil then return strings.unknown end return number end

--Old ID is the ID that occurs in old Perk table in Datatable (not in Datatable/Perks) function GetWeeksAgoOfLastOccurrenceById(id) local offset = 0

for i, sosWeek in ipairs(sos) do -- i = weeks ago (to current day) --mw.log(mw.dumpObject(sosWeek)) for j, perk in ipairs(sosWeek) do			if j <= 4 then if perk == id then mw.log("Last Occ.: " .. GetDateFromSoSIndex(i + offset) .. ", Perk: " .. mPerks.GetPerkById(id).name) return i - 1 + offset end else if perk.offset ~= nil then offset = offset + perk.offset end end end end return 0 end

function GetLastPerkOccurrenceById(id) return GetDateFromSoSIndex(GetWeeksAgoOfLastOccurrenceById(id)) end

--sosIndex should be a number of how many weeks ago the perk occurred function GetDateFromSoSIndex(sosIndex) local result = os.time - sosIndex * _week local weekDay = tonumber(os.date("%w", result)) local offset if weekDay < 3 then offset = (-weekDay - 4) * _day -- current day is Sunday, Monday, or Tuesday (WeekDay = 0 - 2) elseif weekDay > 3 then offset = (-weekDay + 3) * _day --current day is Thursday, Friday, or Saturday (WeekDay = 4 - 6) else offset = 0	end -- current day is Wednesday return os.date("%d.%m.%Y", result + offset) --offset is in negative --2 = -6	--1 = -5	--0 = -4	--6 = -3	--5 = -2	--4 = -1	--3 = 0 end

function GetTextWeeksAgoOfLastOccurrenceById(id) --there will be probably needed to make a copy of a replacing string result = GetWeeksAgoOfLastOccurrenceById(id) if result > 1 then strings.weekAgoString = strings.weekAgoString:gsub("#weekString", "#weeks") elseif result == 1 then strings.weekAgoString = strings.weekAgoString:gsub("#weekString", "#week") else return strings.currentlyInSoS end return utils.regularReplace(strings.weekAgoString:gsub("#result", result), regexTable) end

function GetIndexesOfPeriod(year) local currentWeekDate = os.time(_inception) local currentYear local index = #sos local result = {}

for i, sosWeek in ipairs(sos) do		currentYear = tonumber(os.date("%Y", currentWeekDate)) --mw.log("current Year: " .. currentYear .. ", i: " .. i .. ", index: " .. index .. "sos: {" .. (sos[index][1] ~= nil and (sos[index][1] .. ", " .. sos[index][2] .. ", " .. sos [index][3] .. ", " ..sos[index][4]) or "NULL, NULL, NULL, NULL") .. "}") -- sos[index][1] .. ", " .. sos[index][2] .. ", " .. sos [index][3] .. ", " ..sos[index][4] if year == nil or year == currentYear then if result[1] == nil then result[1] = index end --mw.log("[" .. index .. "] Week " ..GetWeekNumber(currentWeekDate) .. ", Year: " .. os.date("%Y", currentWeekDate) .. ": {" .. sos[index][1] .. ", " .. sos[index][2] .. ", " .. sos[index][3] .. ", " .. sos[index][4] .. "} (" .. os.date("%d. %m. %Y",currentWeekDate) .. ")")			result[2] = index end ---		index = #sos - i -- INDEX SHIFT if sos[index] ~= nil and sos[index][5] ~= nil and sos[index][5].offset ~= nil then --need to reorder logic instructions currentWeekDate = currentWeekDate + (_week * (sos[index][5].offset + 1)) else currentWeekDate = currentWeekDate + _week end end --mw.log(mw.dumpObject(result)) if #result == 0 then return {0, 0} end --if pStart and pEnd is nil then return zero values return result end

function ResolveYearParam(year) if type(year) == "table" and year.args[1] ~= nil then return tonumber(year.args[1]) elseif type(year) == "number" then return year else return os.date("%Y") end end

function SoSArchiveCalendar(year, pStart, pEnd) local currentWeekDate = os.time(_inception) local perkList = GetEmptyPerkTable if pStart == nil or pEnd == nil then pStart, pEnd = unpack(GetIndexesOfPeriod(year)) --Period Start and Period End end local offset = 0 local offset2016 = 0 local notices = {} local week local color local index if year == 2016 then offset2016 = tonumber(os.date("%W", os.time(_inception))) - 1 --setting offset by 43 weeks end for i = pStart, pEnd, -1 do		week = (pStart - i + 1 + offset2016) if sos[i][5] ~= nil and type(sos[i][5]) == "table" then offset = offset + (sos[i][5].offset or 0) --offset must be global because we need to shift the rest of all weeks as well color = sos[i][5].color notices[week] = sos[i][5].notice else color = nil end for j, sosPerk in ipairs(sos[i]) do --sosPerk = individual perk in sos Week row, goes 4x - 5x if type(sosPerk) ~= "table" then if sosPerk ~= 0 then if perkList[sosPerk] == 0 then --sosPerk, the perk ID will be an index of perkList table. Due to Lua logic there must not be a nil between items (that's why there is getEmptyPerkTable) perkList[sosPerk] = if color ~= nil then perkList[sosPerk][1].color = color end else perkList[sosPerk][#perkList[sosPerk] + 1] = {week = week + offset, color = color} end end end end end return {perkList, notices} -- sorting: perkList[perks.id] gives you weeks of appropriate perk end

function GetGlobalOffsetInYear(pStart, pEnd) local offset = 0 for i = pStart, pEnd, -1 do		if sos[i][5] ~= nil and type(sos[i][5]) == "table" then offset = offset + (sos[i][5].offset or 0) end end mw.log("Week Offset: " .. offset) return tonumber(offset) end

function GetPerksInception(year) local charTable local dlcDate local weekOffset local result = GetEmptyPerkTable for i, perk in ipairs(perks) do		if perk.character ~= nil then --if Perk has associated character that means it's a unique perk if		perk.charType == 'S' then charTable = survivors elseif	perk.charType == 'K' then charTable = killers else	return -1 end if charTable[perk.character].dlc ~= nil then dlcDate = utils.GetDatetime(dlcs[charTable[perk.character].dlc].rDate) if tonumber(year) == tonumber(dlcDate.year) then weekOffset = tonumber(os.date("%W", os.time({year = dlcDate.year, month = 1, day = 1}))) --check if the year starts with W0 or W1 - see #48 (documentation of GetWeekNumber) releaseWeek = tonumber(os.date("%W", os.time(dlcDate))) - weekOffset + 1 -- +1 because week indexing start at 0 result[perk.id] = {week = releaseWeek, color = inceptionColor, inception = true} mw.log("Perk " .. perk.name .. " was released at week " .. releaseWeek .. "(+" .. weekOffset .. ")")				end end end end return result end

function mergePerkListWithInceptionList(perkList, perkInception) local merged = GetEmptyPerkTable for i = 1, #perkList do --perkList as I would to keep dependency on the logic that's used when creating perkList variable due to potential later changes if perkInception[i] ~= 0 then if perkList[i] == 0 then perkList[i] = {perkInception[i]} else perkList[i][#perkList[i] + 1] = perkInception[i] end end end --mw.log(mw.dumpObject(perkList)) return perkList end

function ResolvePerkOccurence(perkOccurenceList) local result = 0 for _, perkOcc in ipairs(perkOccurenceList) do		if perkOcc.inception == true then else result = result + 1 end end return result end

function p.AssembleSoSCalendarTable(year) year = ResolveYearParam(year) local pStart, pEnd = unpack(GetIndexesOfPeriod(year)) local perkList, weekNames = unpack(SoSArchiveCalendar(year, pStart, pEnd)) local perkInception = GetPerksInception(year) perkList = mergePerkListWithInceptionList(perkList, perkInception) local result = "" local currentWeekDate = 1 local startWeekDate = 1 local weekFound local hideable if year == 2016 then currentWeekDate = tonumber(os.date("%W", os.time(_inception))) startWeekDate = tonumber(os.date("%W", os.time(_inception))) end local globalOffset = GetGlobalOffsetInYear(pStart, pEnd) local lastWeekNumber = currentWeekDate + (pStart - pEnd + globalOffset) local numberOfWeeksInYear = pStart - pEnd + 1 + globalOffset if perkInception == -1 then return strings.wrongCharType end result = result .. " " .. nl --Show/Hide Switch result = result .. "{| class=\"wikitable unsortable\"" .. nl result = result .. "! " .. strings.icon .." !! " .. strings.name .. " !! colspan = " .. numberOfWeeksInYear .. "| " .. strings.weeksOf .. " " .. year .. "!! " .. strings.shrineCount .. nl result = result .. ntl .. nl result = result .. "! colspan = 2" .. "| " .. nl	--mw.log(mw.dumpObject(weekNames)) for i = 1, numberOfWeeksInYear do -- Weeks Header if i ~= 1 then result = result .. " || class = \"week-cell\" | " else result = result .. "| class = \"week-cell\" | " end result = result .. (weekNames[currentWeekDate] or currentWeekDate) currentWeekDate = currentWeekDate + 1 end result = result .. nl .. "!" .. nl	utils.sortItemsByName(perks) mw.log("pStart: " .. pStart) mw.log("pEnd: " .. pEnd) mw.log("Number of Weeks (without weeks with offsets): " .. numberOfWeeksInYear) mw.log(mw.dumpObject(sos[pStart])) mw.log(mw.dumpObject(sos[pEnd])) mw.log(lastWeekNumber) mw.log(lastWeekNumber - (pStart - pEnd)) mw.log(43) for i, perk in ipairs(perks) do		if perk.character ~= nil and perkList[perk.id] ~= 0 then for j, perkOcc in ipairs(perkList[perk.id]) do				hideable = false if perkOcc.inception == true and (j == 1 and #perkList[perk.id] == 1) then hideable = true break end end result = result .. ntl if hideable then result = result .. " class=\"inception-row" .. year .. "\" style=\"display: none;\" " end result = result .. nl .. "! 32px !! " .. perk.name .. nl .. "|"			for j = startWeekDate, lastWeekNumber do --going through week in year weekFound = false for k, perkOcc in ipairs(perkList[perk.id]) do --perkOcc = Perk Occurrence --if perk.id == 30 then mw.log("Perk " .. perk.name .. ": " .. perk.id .. ", weekOcc:" .. perkOcc.week .. ", j: " .. j) end if perkOcc.week == j then local color = frame:expandTemplate{title = "clr", args = {perkOcc.color or "white"}}

result = result .. "style=\"background-color: #" .. color .. ";\" title = \"" ..strings.week .. " " .. perkOcc.week .. "\" | " -- weekFound = true break end end if j ~= lastWeekNumber then --if it's not occurence in last week then add delimeters result = result .. " ||"				end end result = result .. nl .. "! " .. ResolvePerkOccurence(perkList[perks[i].id]) .. nl		end end result = result .. "|}"	--mw.log(result) return result end

function p.AssembleSoSYearPage(year) year = ResolveYearParam(year) local pStart, pEnd = unpack(GetIndexesOfPeriod(year)) local result = "" local currentWeekDate = 1 local startWeekDate = 1 local sosIndex = 0 local header if year == 2016 then currentWeekDate = tonumber(os.date("%W", os.time(_inception))) --43 startWeekDate = tonumber(os.date("%W", os.time(_inception))) --52 end local currentOffset = 0 local lastWeekNumber = currentWeekDate + (pStart - pEnd)

if perkInception == -1 then return strings.wrongCharType end utils.sortItemsByName(perks) mw.log("pStart: " .. pStart) mw.log("pEnd: " .. pEnd) mw.log("lastWeekNumber: " .. lastWeekNumber) mw.log("lastWeekNumber CALC: " .. lastWeekNumber - (pStart - pEnd)) mw.log("startWeekDate: " .. startWeekDate) for i = startWeekDate, lastWeekNumber do --going through week in year sosIndex = pStart + startWeekDate - i		-- do return end if sosIndex > #sos or sosIndex < 1 then break end --If the index is actually higher than the number of elements in SoS table or the index is below 0 if sos[sosIndex][5] ~= nil then --If there's a table after 4 perks then retrieve data if sos[sosIndex][5].header ~= nil then header = sos[sosIndex][5].header elseif sos[sosIndex][5].offset ~= nil then currentOffset = currentOffset - 1 header = strings.week .. " " .. (i + currentOffset) if sos[sosIndex][5].hideVersion ~= true then header = header .. " v." .. (1 - sos[sosIndex][5].offset) end else header = strings.week .. " " .. i + currentOffset end text = sos[sosIndex][5].text or "" else header = strings.week .. " " .. i + currentOffset text = "" end --result = sos[sosIndex][1] .. ", " .. sos[sosIndex][2] .. ", " .. sos[sosIndex][3] .. ", " .. sos[sosIndex][4] .. nl .. nl .. result --result = ("i: " .. i .. "| Index: " .. sosIndex .. "| lastWeekNumber - currentOffset: " .. lastWeekNumber - currentOffset) .. nl .. result result = SoS(sosIndex) .. nl .. nl .. result result = text .. nl .. result result = "== " .. header .. " ==" ..nl .. result

end mw.log(result) return result end

return p