local p = {}
local mathOps = require("Module:MathOps")
local str = require("Module:Strings")
local frame = mw.getCurrentFrame()
local _brigtnessTreshold = 0.36
local _isValidFileNameCounter = 0
local _colorOrder = {2, 6, 8, 4, 3, 10, 11, 12, 14, 15, 21, 22, 25, 26, 9, 27, 28, 29, 13, 31, 32}
local strings = {
--genders, not currently used
male = "Maschile",
female = "Femminile",
transmale = "Maschio Trans",
transfemale = "Femmina Trans",
nonhuman = "Non applicabile (non umano)",
undefined = "Non definito",
--charNotFound = "Character not found!",
patch = "Patch",
tabberHeaderNotFound = "Intestazione non trovata",
category = "Categoria", --used for [[Category:...]]
boxTypeNotFound = b("La casella dinamica è priva di tipo!"),
notSpecified = "notSpecified", --used in IconLink for assemble machine function
clrTestString = "Una stringa estremamente lunga che serve come stringa di prova per il testo colorato automatico e ordinato che mette in risalto i valori importanti sul nostro bellissimo wiki di Dead by Daylight. Per questo test consideriamo solo parole di 3 o più caratteri.",
notEnoughColors = "nessun altro colore in ordine",
}
p.strings = strings
local months = {
January = "Gennaio",
February = "Febbraio",
March = "Marzo",
April = "Aprile",
May = "Maggio",
June = "Giugno",
July = "Luglio",
August = "Agosto",
September = "Settembre",
October = "Ottobre",
November = "Novembre",
December = "Dicembre"
}
local days = {
Monday = "Lunedì",
Tuesday = "Martedì",
Wednesday = "Mercoledì",
Thursday = "Giovedì",
Friday = "Venerdì",
Saturday = "Sabato",
Sunday = "Domenica"
}
----------------------------------------------
p.timeFormat1 = "%d.%m.%Y" -- 5.9.2022
p.timeContants = {
second = 1,
minute = 60, --in seconds
hour = 3600,
day = 86400,
week = 604800
}
local timeContants = p.timeContants
--Bridge to Language Module, as more appropriate place to have this function
function p.lang()
return require("Module:Languages").lang()
end
--If params will be passed {...} then index should equals 0, so this list will be passeb back: ((index == 0 and params.args) or params)
function p.resolveParameter(param, index, returnCanBeNil)
local retArg
if type(param) == "table" then retArg = ((index == 0 and param.args) or (param.args and param.args[(index or 1)] and p.replaceSpecialCharacters(param.args[(index or 1)]))) or nil end --if parameter is passed from wiki, not other function
if retArg == nil and type(param) == "table" and param.args ~= nil and next(param.args) == nil and not returnCanBeNil then retArg = getParamOrPageName() end --param.args ~= nil and next(param.args): this means that params.args is not nil but empty table
if retArg == nil and not returnCanBeNil then retArg = getParamOrPageName(param) end --if parameter was passed directly or not at all
if retArg ~= nil and type(param) == "string" then retArg = p.replaceSpecialCharacters(retArg) end --final processing
return retArg
end
function getParamOrPageName(param)
if param and
(type(param) ~= types.table or
(type(param) == types.table and not param.args))
then
return param
else
local pageName = pageName or mw.title.getCurrentTitle().text
local pageParts = string.split(pageName, '/') -- remove page suffixes such as language codes
if #pageParts > 1 then
_unofficialLang = pageParts[#pageParts]
return pageParts[#pageParts - 1] --take second to last part of page name
end
return pageParts[1] --if there is no other part than the name itself
end
end
--translates string containing #1# #2# ... to individual strings (resp. words) contained in dyntable
function p.getDynamicString(dynTable, templateString)
--dynTable = {"Testing Chapter", nil, "DLC Str World", nil, 999};
--templateString = "Retracted #1# #2# #3# #4# #5# #6# #7# #8#"
local skipList = {skip = {}, spans = {}}
local spanRegex = "(<span .-</span>)" --mostly clr case
local spanSkipRegex = "&&span" --it needs to use a DIFFERENT symbol than hashtag (#) as the style of spans can contain one due to colorhex colour value
local skipRegex = "#(.-)#" --skip anything inside hashtags
if type(dynTable) == types.string then --is parameter is string then just split it
dynTable = string.split(dynTable)
elseif type(dynTable) == types.table then
local tmp = {}
local skipCounter = 1
local spanCounter = 1
for i, el in pairs(dynTable) do
if el:find(spanRegex) then --if you find any spans to skip then ...
for m in el:gmatch(spanRegex) do --let's find spans (and pull them out) to skip them with splitting by "space"
table.insert(skipList.spans, m)
el = el:gsub(m, spanSkipRegex .. spanCounter)
spanCounter = spanCounter + 1
end
end
--finding any strings DOES NOT need to be first as span regex WILL NOT create elements matching this regex
if el:find(skipRegex) then --if you find any string to skip then ...
for m in el:gmatch(skipRegex) do
table.insert(skipList.skip, m)
el = el:gsub(skip(m:gsub('%W','%%%1')), "#skip" .. skipCounter) --We have to escape special characters as the string is being used as a pattern and for an instance "[[ ]]" are magic chars for LUA patterns
skipCounter = skipCounter + 1
end
end
table.insert(tmp, string.split(tostring(el))) --if parameter is table, split all strings inside
end
dynTable = table.flatten(tmp) --format from table.insert is {{"splitted", "string"}, {"splitted", "another", "string"}}, so it's needed to be flattened
--mw.log(mw.dumpObject(dynTable))
end
if templateString then
for m in templateString:gmatch("#(%d+)#") do --replace all #1# occurences
local currentRegexString = "#" .. tonumber(m) .. "#"
if tonumber(m) <= #dynTable then
templateString = templateString:gsub(currentRegexString, dynTable[tonumber(m)])
else
templateString = templateString:gsub(currentRegexString, cstr.empty) --remove all remaining marks
end
end
else
templateString = table.join(dynTable, space)
end
templateString = string.trim(templateString)
--lg(templateString)
for m in templateString:gmatch("#skip(%d)") do --now we put spans back into string
local currentRegexString = "#skip" .. tonumber(m)
templateString = templateString:gsub(currentRegexString, skipList.skip[tonumber(m)])
end
for m in templateString:gmatch(spanSkipRegex .. "(%d)") do --now we put spans back into string
local currentRegexString = spanSkipRegex .. tonumber(m)
templateString = templateString:gsub(currentRegexString, skipList.spans[tonumber(m)])
end
--mw.log(templateString)
return templateString
end
--------------------------------------------------------------------------------
function p.getCount(subject)
local list
subject = p.resolveParameter(subject)
--if you have another list just add it into a list then call appropriate function
if subject == "map" then return p.getMapsCount()
elseif subject == "realm" then list = realms
elseif subject == "killer" then list = killers
elseif subject == "survivor" then list = survivors
elseif subject == "dlc" then list = dlcs
elseif subject == "chapter" then return p.getCountDlcType(1)
elseif subject == "paragraph" then return p.getCountDlcType(2)
elseif subject == "clothing" then return p.getCountDlcType(3)
elseif subject == "ost" then return p.getCountDlcType(4)
elseif subject == "character" then return p.getCountDlcType(5)
elseif subject == "ccy" then return getCountCCY(true)
elseif subject == "ccy-gc" then return getCountCCY(true) --redundant option to keep convention
elseif subject == "ccy-rc" then return getCountCCY(false)
elseif subject == "killerPerk" then return getPerksCount('K')
elseif subject == "survPerk" then return getPerksCount('S')
else return 0
end
local x = 0
for _, item in ipairs(list) do if item.skip or item.retired then x = x + 1 end end
return #list - x
end
function p.getMapsCount()
local data = require("Module:Datatable")
local result = 0
for _, map in ipairs(data.maps) do
if not (map.skip or map.retired) then
if map.variations then
for _, var in ipairs(map.variations) do
if not (map.skip or map.retired) then
result = result + 1
end
end
else
result = result + 1
end
end
end
return result
end
function p.getCountDlcType(type)
local count = 0
for _, dlc in ipairs(dlcs) do
if dlc.category == type and not dlc.skip then count = count + 1 end
end
return count
end
function getPerksCount(charType)
local perks = mw.loadData("Module:Datatable/Perks" .. p.lang()).perks
local count = 0
for _, perk in ipairs(perks) do
if perk.charType == charType and not perk.unused then count = count + 1 end
end
return count
end
function getCountCCY(gc)
local list = ccy
local i = 0
while list[i + 1] and list[i + 1].gc == gc do i = i + 1 end
return i
end
function isRealCcy(ccyId)
local data = require("Module:Datatable" .. p.lang())
for _, currency in ipairs(data.ccy) do
if currency.id == ccyId then return not currency.gc end
end
return false
end
function p.getCcyById(id)
for _, currCcy in ipairs(ccy) do
if currCcy.id == id then return currCcy end
end
return nil
end
function p.getMaxId(tab)
result = 0
for _, item in ipairs(tab) do
result = (item.id > result and item.id) or result
end
return result
end
-- Function allowing for consistent treatment of boolean-like wikitext input.
-- It works similarly to the template {{yesno}}.
function p.bool(val, default)
-- If your wiki uses non-ascii characters for any of "yes", "no", etc., you
-- should replace "val:lower()" with "mw.ustring.lower(val)" in the
-- following line.
val = type(val) == 'string' and val:lower() or val
if val == nil then
return nil
elseif val == true
or val == 'yes'
or val == 'y'
or val == 'true'
or val == 't'
or val == 'on'
or tonumber(val) == 1
then
return true
elseif val == false
or val == 'no'
or val == 'n'
or val == 'false'
or val == 'f'
or val == 'off'
or tonumber(val) == 0
then
return false
else
return default
end
end
--Example usage:
--amount = 1333444.1
--print(format_num(amount,2))
--print(format_num(amount,-2,"US$"))
--amount = -22333444.5634
--print(format_num(amount,2,"$"))
--print(format_num(amount,2,"$","()"))
--print(format_num(amount,3,"$","NEG "))
--Output:
--1,333,444.10
--US$1,333,400
---$22,333,444.56
--($22,333,444.56)
--NEG $22,333,444.563
function p.formatNum(amount, decimal, prefix, neg_prefix)
return p.format_num(amount, decimal, prefix, neg_prefix)
end
function p.format_num(amount, decimal, prefix, neg_prefix)
local str_amount, formatted, famount, remain
decimal = decimal or 2 -- default 2 decimal places
neg_prefix = neg_prefix or "-" -- default negative sign
famount = math.abs(mathOps.round(amount,decimal))
famount = math.floor(famount)
remain = mathOps.round(math.abs(amount) - famount, decimal)
-- comma to separate the thousands
formatted = p.commaFormat(famount)
-- attach the decimal portion
if (decimal > 0) then
remain = string.sub(tostring(remain),3)
formatted = formatted .. "." .. remain ..
string.rep("0", decimal - string.len(remain))
end
-- attach prefix string e.g '$'
formatted = (prefix or "") .. formatted
-- if value is negative then format accordingly
if (amount<0) then
if (neg_prefix=="()") then
formatted = "("..formatted ..")"
else
formatted = neg_prefix .. formatted
end
end
return formatted
end
function p.commaFormat(amount)
local formatted = amount
while true do
formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
if (k==0) then
break
end
end
return formatted
end
--Converting Arabic numbers to Roman
function p.toRomanNumerals(s)
local numbers = { 1, 5, 10, 50, 100, 500, 1000 }
local chars = { "I", "V", "X", "L", "C", "D", "M" }
s = tonumber(s)
if not s or s ~= s then return false end
if s == math.huge then return false end
s = math.floor(s)
if s <= 0 then return s end
local ret = cstr.empty
for i = #numbers, 1, -1 do
local num = numbers[i]
while s - num >= 0 and s > 0 do
ret = ret .. chars[i]
s = s - num
end
for j = 1, i - 1 do
local n2 = numbers[j]
if s - (num - n2) >= 0 and s < num and s > 0 and num - n2 ~= n2 then
ret = ret .. chars[j] .. chars[i]
s = s - (num - n2)
break
end
end
end
return ret
end
--Converting Roman numbers to Arabic
function ToNumeral(roman)
local Num = { ["M"] = 1000, ["D"] = 500, ["C"] = 100, ["L"] = 50, ["X"] = 10, ["V"] = 5, ["I"] = 1 }
local numeral = 0
local i = 1
local strlen = string.len(roman)
while i < strlen do
local z1, z2 = Num[ string.sub(roman,i,i) ], Num[ string.sub(roman,i+1,i+1) ]
if z1 < z2 then
numeral = numeral + ( z2 - z1 )
i = i + 2
else
numeral = numeral + z1
i = i + 1
end
end
if i <= strlen then numeral = numeral + Num[ string.sub(roman,i,i) ] end
return numeral
end
function resolveNameWithRomanNumbers(str)
local index = string.find(str, " [^ ]*$") + 1
local romanNumber = (string.sub(str, index))
local result
if string.match(romanNumber, "[MDCLXVI]+$") then --finding ONLY Roman letters in last "word"
result = str
else
result = str
end
return result
end
function p.capitalizeName(str) return p.CapitalizeName(str) end
function p.CapitalizeName(str) --deprecated name
--mw.log(string.gsub(" "..str, "[%s%-]%a", string.upper):sub(2))
--return string.gsub(" "..str, "[%s%-]%a", string.upper):sub(2) --original version
return string.gsub(" "..str:lower(), "[%s%-]%a", string.upper):sub(2)
end
function p.firstLetterLower(str) return p.FirstLetterLower(str) end
function p.FirstLetterLower(str) --deprecated name
return str:sub(1, 1):lower() .. str:sub(2)
end
function p.firstLetterUpper(str) return p.FirstLetterUpper(str) end
function p.FirstLetterUpper(str) --deprecated name
return str:sub(1,1):upper() .. str:sub(2)
end
--[[function p.fixDiacritics(str)
local letterSequences = {
["195"] = {fix = "â", sequence = {"162"}}
}
local fixedSeqsOffset = 0
local currentLen = #str + fixedSeqsOffset
local i = 1
while i <= currentLen do
local ascii = tostring(string.byte(str:sub(i,i)) or 0)
for startSequence, seqData in pairs(letterSequences) do
if ascii == startSequence then
local foundSequence = true
for j, seqCode in ipairs(seqData.sequence) do
local seqAscii = tostring(string.byte(str:sub(i+j,i+j)))
if seqAscii ~= seqCode then
foundSequence = false
break
end
end
if foundSequence then
fixedSeqsOffset = i - (i + #seqData.sequence)
currentLen = #str + fixedSeqsOffset
-- #seqData.sequence + 1 = number of ascii codes that needs to be replaced, the sequence list + the initial ascii code, i.e. the key value from letterSequences
str = str:sub(1, i - 1) .. seqData.fix .. str:sub(i + #seqData.sequence + 1)
end
end
end
i = i + 1
end
return str
end]]
function p.RemoveSpecialCharacters(str, full, replaceDiacritics)
str = string.gsub(str, "'", "")
str = string.gsub(str, "®", "")
str = string.gsub(str, "™", "")
str = string.gsub(str, ":", "")
str = string.gsub(str, "!", "") --perks
str = string.gsub(str, "?", "") --achievements
str = string.gsub(str, "/", "") --achievements
str = string.gsub(str, "%&", "And") --probably can be changed to lower: "and"
if replaceDiacritics then
str = p.replaceDiacritics(str, full)
end
return str
end
function p.replaceDiacritics(str, full)
--Diacritics
specialLettersLight = {
["A"] = {"À", "Á", "Â", "Ã", "Ä"},
["a"] = {"à", "á", "â", "ã", "ä"},
["e"] = {"è", "é", "ê", "ë", "ě"},
["O"] = {"Ò", "Ó", "Ô", "Õ", "Ö"},
["o"] = {"ò", "ó", "ô", "õ", "ö"}
}
specialLetters = {
["A"] = {"À", "Á", "Â", "Ã", "Ä"},
["a"] = {"à", "á", "â", "ã", "ä"},
["E"] = {"È", "É", "Ê", "Ë", "Ě"},
["e"] = {"è", "é", "ê", "ë", "ě"},
["I"] = {"Ì", "Í", "Î", "Ñ", "Ï"},
["i"] = {"ì", "í", "î", "ñ", "ï"},
["O"] = {"Ò", "Ó", "Ô", "Õ", "Ö"},
["o"] = {"ò", "ó", "ô", "õ", "ö"},
["U"] = {"Ù", "Ú", "Û", "Ů", "Ü"},
["u"] = {"ù", "ú", "û", "ů", "ü"},
["Y"] = { "Ý", "Ÿ"},
["y"] = { "ý", "ÿ"}
}
--Originally it was grouped by set [ÀÁ], however there is some sort of bug there that makes it not working
--str = string.gsub(str, "[ÁÂ]", "A")
--if debugRun then mw.log(mw.dumpObject(specialLetters)) end
for letter, row in pairs(((full and specialLetters) or specialLettersLight)) do
for _, special in ipairs(row) do
str = string.replace(str, special, letter)
end
end
return str
end
function p.replaceSpecialCharacters(name)
local charList = {
["'"] = ''',
["&"] = '&'
}
if name == nil then error("Name parameter is empty, but it shouldnt?") end
for repl, sChar in pairs(charList) do
name = string.gsub(tostring(name), sChar, repl)
end
return name
end
function p.pageExists(name)
return (not name == cstr.empty) or mw.title.new(name).exists
end
function p.isValidFileName(name, extension)
extension = extension or "png"
name = p.RemoveSpecialCharacters(name)
_isValidFileNameCounter = _isValidFileNameCounter + 1
mw.log("Counter:" .. tostring(_isValidFileNameCounter) .. ' - ' .. cstr.media .. name .. dot .. extension)
return not (name == cstr.empty or not mw.title.new(cstr.media .. name .. dot .. extension).exists)
end
function p.resolveFileName(str, keepSpaces, removeDiacritics)
keepSpaces = keepSpaces or false
local result = ""
result = string.lower(str)
--mw.log(result)
result = p.RemoveSpecialCharacters(result, keepSpaces, removeDiacritics)
--mw.log(result)
result = p.capitalizeName(result)
if not keepSpaces then
result = string.gsub(result, "[ ]", "")
end
--In future if there will be needed replace charactere such as "é" just add another substitution
--mw.log(result)
return result
end
function p.resolveImageName(name)
if mw.title.new(cstr.media .. name .. dot .. cstr.png).exists then return name .. dot .. cstr.png end
if mw.title.new(cstr.media .. name .. dot .. cstr.jpg).exists then return name .. dot .. cstr.jpg end
if mw.title.new(cstr.media .. name .. dot .. cstr.gif).exists then return name .. dot .. cstr.gif end
return name .. dot .. cstr.png
end
function p.GetDisplayName(item)
return (item.tName or item.name)
end
function p.IconLink(icon, pageLink, displayText, background)
local result = cstr.empty
background = background or p.resolveParameter(icon, "bg", true) or p.resolveParameter(icon, 4, true)
background = p.bool(background == nil or background)
displayText = displayText or p.resolveParameter(icon, 3, true)
pageLink = pageLink or p.resolveParameter(icon, 2, true)
local linkless = p.resolveParameter(icon, "linkless", true) or pageLink == "linkless" --linkless as a second parameter should be deprecated
icon = p.resolveParameter(icon)
local iconObject, pluralType = p.getIconObject(icon)
if pluralType then --if automatic plural is detected, then links should redirect to singular form
pageLink = pageLink or iconObject.name
displayText = icon --since automatic plurals don't imitate 3-parameters scenario we have to set the display text to be same as the first parameter (icon)
end
local iconFile = iconObject.iconFile
local filename = cstr.file .. iconFile .. tl .. "link=" .. (pageLink or icon)
local text = (displayText and pageLink and pageLink .. tl .. displayText) or pageLink or icon --cstr.empty
--local boxDesc = cstr.empty
if pageLink == "img" then
text = cstr.empty
filename = cstr.file .. iconFile .. tl .. "link=" .. icon
end
if not linkless and text ~= cstr.empty then
text = link(text)
--boxDesc = getBoxDescription(pageLink or icon) //hover box, currently disabled
elseif linkless or pageLink == "linkless" then
text = (displayText or icon)
end
local imageFile
if background and iconObject.assembly then
local loLogic = require("Module:Loadout" .. p.lang())
--this needs to (should) be done sophisticated way
local imgType =
(iconObject.iconFile:lower():find("addon") and "addon") or
(iconObject.iconFile:lower():find("items") and "item") or
(iconObject.iconFile:lower():find("favors") and "offering") or
(iconObject.iconFile:lower():find("powers") and "power") or
(iconObject.iconFile:lower():find("perks") and "perk") or
strings.notSpecified
--if iconObject.iconFile:lower():find("favors") then imgType = "offering" end
local loObj = (loLogic["get" .. p.capitalizeName(imgType) .. "ByName"] and loLogic["get" .. p.capitalizeName(imgType) .. "ByName"](icon)) or nil
imageFile = p.assembleImage(imgType, icon, 32, {rarity = (loObj and loObj.rarity) or 1, iconLink = true})
else
imageFile = link(filename, "64px") --iconObject.ilRes or "32px")
end
local tooltipText = link(filename, "96px", "link=")
--result = '<span class = "wrap-span pcView" style = "display:none;"><span class="box-span">' .. boxDesc .. '</span>' .. text .. file .. '</span>'
local padding = p.getPaddingsFromIcon(iconObject)
result = text ..
'<span class = "iconLink" style = "vertical-align: middle; padding: ' .. padding.top .. space .. padding.right .. space .. padding.bottom .. space .. padding.left .. '">' ..
'<span style = "display:none; width:' .. (iconObject.ilRes or "32px") .. semi .. 'max-height: ' .. (iconObject.ilRes or "32px") .. '" class = "pcView pcIconLink">' .. p.tooltip(imageFile, tooltipText, true, true, true) .. '</span>' ..
'<span class = "mobileView mobileIconLink">' .. link(filename, iconObject.ilRes or "32px") .. '</span>' ..
'</span>'
--mw.log(result)
return result
--return frame:expandTemplate{title = "IconLink", args = {icon, pageLink, displayText} }
end
function p.loadoutIconLink(icon, pageLink, displayText, params)
displayText = displayText or p.resolveParameter(icon, "displayText", true) or p.resolveParameter(icon, 3, true)
pageLink = pageLink or p.resolveParameter(icon, "link", true) or p.resolveParameter(icon, 2, true)
icon = p.resolveParameter(icon, "icon", true) or p.resolveParameter(icon, 1, true) or icon
local info = {
displayText = displayText or pageLink or icon,
link = pageLink or icon,
icon = icon
}
local iconObject, pluralType = p.getIconObject(info.icon)
local filename = cstr.file .. iconObject.iconFile .. tl .. "link=" .. (info.link or info.icon)
local tooltipText = link(filename, "96px", "link=") --default tooltip
if iconObject.iconFile:lower():find("perk") then
local loLogic = require("Module:Loadout" .. p.lang())
tooltipText = loLogic.getPerkPageTable(params.perk) or "WIP/Error:" .. params.perk.name
end
local imageFile = link(filename, "64px")
local padding = p.getPaddingsFromIcon(iconObject)
local result = link(info.displayText, info.link) ..
'<span class = "iconLink" style = "vertical-align: middle; padding: ' .. padding.top .. space .. padding.right .. space .. padding.bottom .. space .. padding.left .. '">' ..
'<span style = "display:none; width:' .. (iconObject.ilRes or "32px") .. semi .. 'max-height: ' .. (iconObject.ilRes or "32px") .. '" class = "pcView pcIconLink">' .. p.tooltip(imageFile, tooltipText, true, true, true) .. '</span>' ..
--'<span class = "mobileView mobileIconLink">' .. link(filename, iconObject.ilRes or "32px") .. '</span>' ..
'</span>'
return result
end
function p.getPaddingsFromIcon(icon)
return {
top = icon.paddingTop or 0,
right = icon.paddingRight or 0,
bottom = icon.paddingBottom or 0,
left = icon.paddingLeft or 0
}
end
function getBoxDescription(icon) --curently disabled
local ic = p.getIconObject(icon)
local result = cstr.empty
if ic.category == "Perks" then
result = '<h3>' .. icon .. '</h3><hr>'-- .. prkz.getPerkDescriptionByName(perk.name)
end
return result
end
function p.resolveTextColorByBackground(hexBgColor)
if(type(hexBgColor) == "table") then
hexBgColor = hexBgColor.args[1]
end
local red = tonumber(string.sub(hexBgColor, 1, 2), 16)
local green = tonumber(string.sub(hexBgColor, 3, 4), 16)
local blue = tonumber(string.sub(hexBgColor, 3, 4), 16)
local darkness = (0.299 * red + 0.587 * green + 0.114 * blue) / 255
--mw.log("Red: " .. red .." \t\tor\t Blue: " .. blue .. " | Green: " .. green)
--mw.log(darkness)
if(darkness < _brigtnessTreshold) then
return "white"
else
return "black"
end
end
function p.getPageTitle()
return mw.title.getCurrentTitle().text
end
---------------------------------------------------------------------------------
function p.getSumOfSizes(row)
local result = 0
local i = 1
while row[i] do
result = result + row[i][1] -- hardcoded first variable in table
i = i + 1
end
return result
end
--[[
function compSize(row1, row2)
local sum1 = row1.size
local sum2 = row2.size
if(type(sum1) == "table") then
sum1 = p.getSumOfSizes(sum1) --converting back from table to number
end
if(type(sum2) == "table") then
sum2 = p.getSumOfSizes(sum2)
end
if sum1 > sum2 then return true
elseif sum1 < sum2 then return false
else
if row1.realm < row2.realm then return true
elseif row1.realm > row2.realm then return false
else
return resolveNameWithRomanNumbers(row1.name) < resolveNameWithRomanNumbers(row2.name)
end
end
end
]]
function compName(row1, row2)
if row1.diacritics then
if row2.diacritics then
return p.RemoveSpecialCharacters(row1.name, true, true) < p.RemoveSpecialCharacters(row2.name, true, true)
else
return p.RemoveSpecialCharacters(row1.name, true, true) < row2.name
end
elseif row2.diacritics then
return row1.name < p.RemoveSpecialCharacters(row2.name, true, true)
end
return row1.name < row2.name
end
function compFilename(row1, row2)
return row1.filename < row2.filename
end
function compInt(row1, row2) -- 1, 2, 3
return row1 < row2
end
function compIntReversed(row1, row2) -- 3, 2, 1
return row1 > row2
end
function compIntDesc(row1, row2) -- 3, 2, 1
return row1 > row2
end
function compLevel(row1, row2)
return row1.level < row2.level
end
function compDlcCategory(row1, row2)
if row1.category == row2.category then
return row1.id < row2.id
end
return row1.category < row2.category
end
function compId(row1, row2)
return row1.id < row2.id
end
function compRealCcyFirst(row1, row2)
ccy1 = isRealCcy(row1.ccy)
ccy2 = isRealCcy(row2.ccy)
if (ccy1 and ccy2) or (not ccy1 and not ccy2) then --TRUE and TRUE or FALSE and FALSE
return row1.ccy < row2.ccy
elseif ccy1 and not ccy2 then --TRUE and FALSE
return true
else
return false
end
end
function compCharsKillersFirst(row1, row2)
char1 = row1.power ~= nil
char2 = row2.power ~= nil
if (char1 and char2) or (not char1 and not char2) then --TRUE and TRUE or FALSE and FALSE
return row1.id < row2.id
elseif char1 and not char2 then --TRUE and FALSE
return true
else
return false
end
end
function rarityAndName(row1, row2)
if row1.rarity and row2.rarity and row1.rarity < row2.rarity then
return true
elseif row1.rarity and row2.rarity and row1.rarity > row2.rarity then
return false
elseif row1.pieces and row2.pieces then
local row1Name = cstr.empty
local row2Name = cstr.empty
for _, piece in pairs(row1.pieces) do row1Name = piece.name break end
for _, piece in pairs(row2.pieces) do row2Name = piece.name break end
return row1Name < row2Name
else
return compName(row1, row2)
end
end
function p.sortByKeys(tableList) --only int, TODO check key type via pairs() fc
local result = {}
local orderedKeys = table.keys(tableList)
p.sortTable(orderedKeys, true)
for _, key in pairs(orderedKeys) do
result[key] = tableList[key]
end
return result
end
function p.sortByRDate(tableList, reversed)
table.sort(tableList,
function (row1, row2)
if p.toTimestamp(row1.rDate) < p.toTimestamp(row2.rDate) then
if reversed then
return false
else
return true
end
end
end
)
return tableList
end
function p.sortMapsBySize() --deprecated?
local m = require("Module:Datatable" .. p.lang())
--mw.log(mw.dumpObject(maps))
table.sort(maps,compSize)
--mw.log(mw.dumpObject(maps))
end
function p.sortItemsByName(tableList)
table.sort(tableList, compName)
end
function p.sortItemsBycompFilename(tableList) --used for charms (as perk object is not retrieved, only cosData record)
table.sort(tableList, compFilename)
end
function p.sortTable(tableList, reversed)
if reversed then
table.sort(tableList, compIntReversed)
else
table.sort(tableList, compInt)
end
end
function p.sortTableDesc(tableList)
table.sort(tableList, compIntDesc)
end
function p.sortDlcByCategory(tableList)
table.sort(tableList, compDlcCategory)
end
function p.sortPerksByLevel(tableList)
table.sort(tableList, compLevel)
end
function p.sortTableById(tableList)
table.sort(tableList, compId)
end
function p.sortRealCcyFirst(tableList)
table.sort(tableList, compRealCcyFirst)
end
function p.sortCharsKillersFirst(tableList)
table.sort(tableList, compCharsKillersFirst)
end
function p.sortCosmeticsByRarityAndName(tableList)
table.sort(tableList, rarityAndName)
end
---------------------------------------------------------------------------------
-- Time functions --
function p.today()
return os.time(os.date("!*t"))
end
function p.getYear(stamp) return p.getTimeAspect(stamp, "year") end
function p.getMonth(stamp) return p.getTimeAspect(stamp, "month") end
function p.getDay(stamp) return p.getTimeAspect(stamp, "day") end
function p.getHour(stamp) return p.getTimeAspect(stamp, "hour") end
function p.getMinute(stamp) return p.getTimeAspect(stamp, "minute") end
function p.getSecond(stamp) return p.getTimeAspect(stamp, "second") end
function p.getWeekday(stamp) return p.getTimeAspect(stamp, "weekday") end
function p.getTimeAspect(stamp, aspect) --aspect being selected time portion
if type(stamp) ~= 'number' then
stamp = p.toTimestamp(stamp)
end
local tag = '%c'
if aspect == "year" then tag = '%Y'
elseif aspect == "month" then tag = '%m'
elseif aspect == "day" then tag = '%d'
elseif aspect == "hour" then tag = '%H'
elseif aspect == "minute" then tag = '%M'
elseif aspect == "second" then tag = '%S'
elseif aspect == "weekday" then tag = '%w'
end
local result = os.date(tag, stamp)
return (aspect == "weekday" and tonumber(result)) or result
end
function p.addTime(portion, amount, stamp)
stamp = stamp or 0
if type(stamp) ~= 'number' then
stamp = p.toTimestamp(stamp)
end
local multiplier = timeContants[portion]
stamp = stamp + (amount * multiplier)
return stamp
end
function p.getTimeDiff(timestamp1, timestamp2) --First parameter is first in timeline, Second Parameter is later one
return os.difftime(timestamp2, timestamp1)
end
function p.getTimeDiffFormatting(seconds, unit)
return seconds / timeContants[unit]
end
function p.toTimestamp(datestamp)
if type(datestamp) == 'string' then
datestamp = p.GetDatetime(datestamp)
end
return os.time(datestamp) --if datestamp = nil, os.time() returns current datetime stamp
end
function p.toDate(timestamp, timeFormat)
return os.date(timeFormat or '%d %B %Y', timestamp) --21 October 2021
end
function p.resolveDateTime(datetime, skipDay) --with weekday
local matchYear = "^(..)%.(..)%.(%d%d%d%d)$"
local matchMonth = "^(..)%.(%d%d)%.(....)$"
local matchDay = "^(%d%d)%.(..)%.(....)$"
local year, month, day, dayName = false
local rDate = os.time(p.GetDatetime(datetime))
if string.find(datetime, matchYear) then
year = os.date("%Y", rDate)
end
if string.find(datetime, matchMonth) then
month = os.date("%B", rDate)
end
if string.find(datetime, matchDay) then
day = os.date("%d", rDate)
dayName = os.date("%A", rDate)
end
--(day and month) is to avoid showing a day as the function sets the month to January (first month) by default, if the month is not provided
--day part: ((day and month and day .. space) or cstr.empty)
--month part: ((month and months[month] .. space) or cstr.empty)
--year part: year
--dayName part: ((day and month and not skipDay and space .. brackets(days[dayName])) or cstr.empty)
return ((day and month and day .. space) or cstr.empty) .. ((month and months[month] .. space) or cstr.empty) .. year .. ((day and month and not skipDay and space .. brackets(days[dayName])) or cstr.empty)
end
function p.isFullDateTime(datestamp) return p.IsFullDateTime(datestamp) end
function p.IsFullDateTime(datestamp)
local day, month, year = datestamp:match("^(..)%.(..)%.(....)$")
if month == "##" or day == "##" or year == "####" then return false end
return true
end
--convertToTimeVersion = normally, return table counts as 1.1.1970 (with day = 1, month = 1 and year = 1970).
--But if we want to have time stamp (like remaining time) these default values add 1 day, 1 month and 1970 years to the countdown table (Time stamp version table)
function p.GetDatetime(datestamp, convertToTimeVersion)
local result = {}
if type(datestamp) == types.string then
local day, month, year, hour, min, sec = datestamp:match("^(..)%.(..)%.(%d%d%d%d) ?(%d?%d?):?(%d?%d?):?(%d?%d?)$")
if month == "##" then
month = 1
result.fakeMonth = true
end
if day == "##" then
day = 1
result.fakeDay = true
end
result.year = tonumber(year)
result.month = tonumber(month)
result.day = tonumber(day)
result.hour = tonumber((hour ~= cstr.empty and hour) or 0)
result.min = tonumber((min ~= cstr.empty and min) or 0)
result.sec = tonumber((sec ~= cstr.empty and sec) or 0)
result.timestamp = p.toTimestamp(result)
local tempDateObj = os.date("!*t", os.time(result))
result.yday = tempDateObj.yday --year day included
result.wday = p.correctWeekday(tempDateObj).wday --weekDay included
result.week = p.getWeekNumber(result.timestamp)
result.quarter = math.ceil(result.month / 3)
result.dst = tempDateObj.isdst
elseif type(datestamp) == types.number then
result = os.date("!*t", datestamp)
if(convertToTimeVersion) then
result.day = result.day - 1 --we have to subtract it, not set it, as the datetime stamp can reach to days and months
result.month = result.month - 1
result.year = result.year - 1970
end
result = p.correctWeekday(result)
result.timestamp = datestamp
end
return result
end
--.wday is indexed 1 - 7, Sunday being 1 => https://www.lua.org/manual/5.3/manual.html#:~:text=wday%20(weekday%2C%201%E2%80%937%2C%20Sunday%20is%C2%A01)
--%w starts 0 - 6, Sunday being 0 => https://www.lua.org/pil/22.1.html#:~:text=%25w,6%20%3D%20Sunday%2DSaturday%5D
--following row synchronises the convention with '%w' and shifting it to 1 - 7 starting Monday, as for the rest of Wiki
function p.correctWeekday(datetable)
datetable.wday = (datetable.wday == 1 and 7) or datetable.wday - 1
return datetable
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 p.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.getDatePart(datestamp, part)
local day, month, year = datestamp:match("^(..)%.(..)%.(....)$")
if year == "####" then month = false end
if month == "##" then month = false end
if day == "##" then day = false end
if part == "day" then return day
elseif part == "month" then return month
elseif part == "year" then return year
end
end
--------------------------------------------------------------------------------
function p.regularReplace(reggedString, regexTable)
local result = cstr.empty
for key, value in pairs(regexTable) do
reggedString = reggedString:gsub(key, value)
end
return reggedString
end
function p.getIcon(icon)
return p.getIconObject(icon).iconFile
end
function p.getIconObject(icon)
icon = p.resolveParameter(icon)
local icons = mw.loadData("Module:Datatable/Icons" .. p.lang()).icons --mw.loadData
local pluralRegex = "^(.+)s$"
local plural2Regex = "^(.+)es$"
local plural3Regex = "^(.+)ies$"
local plural = icon:match(pluralRegex)
local plural2 = icon:match(plural2Regex) --Toolboxes => Toolbox
local plural3 = icon:match(plural3Regex) --Bodies => Bod
local result = getIconElement(icons, icon, true)
if result then
return result
elseif plural and icons[plural] ~= nil then --Pallets => Pallet
return getIconElement(icons, plural), 1
elseif plural2 and icons[plural2] ~= nil then --Toolboxes => Toolbox
return getIconElement(icons, plural2), 2
elseif plural3 and icons[plural3 .. "y"] ~= nil then --Bodies => Body
return getIconElement(icons, plural3 .. "y"), 3
else
return getIconElement(icons, icon)
end
end
function getIconElement(icons, icon, returnNil)
if type(icons[icon]) == types.table then
local result = table.copy(icons[icon])
result.name = icon
return result
elseif returnNil then
return nil
elseif icons[icon] == nil then
return icons["Unknown QuestionMark"] or icons[1]
else
return getIconElement(icons, icons[icon])
end
end
--************************* survivors & killers ******************************--
function p.getCharacterById(id, charTable)
for _, character in ipairs(charTable) do
if character.id == id then return character end
end
end
function p.getCharacter(object)
local data = require("Module:Datatable" .. p.lang())
if object.killer or object.charType == 'K' then
return p.getCharacterById(object.killer or object.character, data.killers)
else
return p.getCharacterById(object.survivor or object.character, data.survivors)
end
end
--function returns appropriate character name that should be compatible with IL.
--IL itself is not executed to have option to customise the IL call
function p.resolveCharacterIconLinkName(owner)
return (owner and owner.multiName and owner.realName) or (not p.isKiller(owner) and owner.shortName) or owner.name or false
end
function p.resolveCharacterPortraitFileName(character, maxId)
local fileConst = "_charPreview_portrait"
local isKiller = p.isKiller(character)
local unknownChar = (isKiller and "UnknownKiller") or "UnknownSurvivor"
local fileName
fileName = p.getFileNameFromTableById(character.id, (isKiller and killerImages) or survivorImages, "preview") --get custom name from table
if fileName == cstr.empty or not p.isValidFileName(fileName) then --K/S{ID}_charPreview_portrait
fileName = p.getCharacterIdentifier(character) .. fileConst
end
if (maxId and (character.id >= maxId - 1 and not p.isValidFileName(fileName)) or (not maxId and not p.isValidFileName(fileName))) then --File not Found; maxId - 1 to consider last two characters, not only the last one
fileName = unknownChar .. fileConst
end
--mw.log(fileName)
return fileName
end
function p.getCharacterIdentifier(character)
return ((p.isKiller(character) and 'K') or 'S') .. string.format("%02d", character.id)
end
function p.getFileNameFromTableById(id, fileTable, field)
--mw.log(id)
for j, sImage in ipairs(fileTable) do
if sImage.id == id and sImage[field] ~= nil then
return sImage[field]
end
end
return cstr.empty
end
--Not currently used
function p.resolveGender(abbr)
if abbr == 'M' then return strings.male
elseif abbr == 'F' then return strings.female
elseif abbr == 'N' then return strings.nonhuman
elseif abbr == 'M/F' then return strings.male .. comma .. strings.female
elseif abbr == 'F/M' then return strings.female .. comma .. strings.male
elseif abbr == 'TM' then return strings.transmale
elseif abbr == 'TF' then return strings.transfemale
else return strings.undefined
end
end
--classes in form: "sortable, class2, class3 ,..."
function p.wrapBasicTable(content, classes, styles, skipNtl)
return '{| class = ' .. quotes('wikitable' .. ((classes and space .. p.getTableClasses(classes)) or cstr.empty)) .. ((styles and space .. 'style = ' .. quotes(styles)) or cstr.empty) .. nl ..
(not skipNtl and ntl .. nl or cstr.empty) .. content ..'|}'
end
function p.wrapTable(content, classes, styles, skipNtl)
return (classes and '{| class = ' .. quotes((p.getTableClasses(classes))) or cstr.empty) .. ((styles and space .. 'style = ' .. quotes(styles)) or cstr.empty) .. nl ..
(not skipNtl and ntl .. nl or cstr.empty) .. content ..'|}'
end
function p.getTableClasses(classes)
classes = p.resolveParameter(classes, 1)
local unknownClass = "unknownClass"
if not classes then return unknownClass end
if type(classes) == "string" then
classes = classes:gsub(", ", space):gsub(",", space)
else --todo type(classes) == "table"
return unknownClass
end
return classes
end
function p.getCharacterByName(name)
name = name or p.resolveParameter(name)
local str = require("Module:Datatable" .. p.lang())
for _, surv in ipairs(survivors) do
if surv.shortName == name or surv.name == name then
local survivor = table.copy(surv)
survivor.isKiller = false
return survivor
end
end
for _, killer in ipairs(killers) do
if killer.shortName == name or killer.realName == name or the(killer) .. killer.name == name or killer.name == name then
local klr = table.copy(killer)
klr.isKiller = true
return klr
end
end
return nil
end
--deprecated
function p.getCharsByDlc(dlc, charType)
local data = require("Module:Datatable" .. p.lang())
local result = {}
local listTable = (charType == 'S' and data.survivors) or (charType == nil and data.survivors) or data.killers --if the charType is not set then set the table by default to survivors
for _, character in ipairs(listTable) do
if character.dlc == dlc.id then
result[#result + 1] = character
end
end
if charType ~= nil then --if the charType is set, we need loop through only one table. Otherwise charType wasn't set in order to retrieve both types of characters from DLC
return result
end
for _, character in ipairs(data.killers) do --since the default table is survivor, we can hardcode killer table as a second table
if character.dlc == dlc.id then
result[#result + 1] = character
end
end
return result
end
function p.getCharacterFirstName(character)
character = p.resolveParameter(character)
if not character.name then
character = p.getCharacterByName(character)
end
local regex = '([^ ]*) ?' --%a doesn't consider diacritics such as É (for instance: Élodie)
local isKiller = p.isKiller(character)
local text = (isKiller and character.name) or character.shortName or character.name --if the char is killer then use their name, otherwise it's survivor, thus use shortName or name
if isKiller then return text end --if it's killer, then their nickname is already what it's supposed to return
if regex and text:gmatch(regex) then
for m in text:gmatch(regex) do
return m --return first occurence before potential space, otherwise return whole string
end
end
return "Name not found"
end
function p.replaceLastSpaceByNBSP(text) -- Non Breakable SPace
local lastCharSpaceRegex = "^(.+) $"
local regex = "^(.+) (.+)$" --should pick last space(?)
text = text:gsub(lastCharSpaceRegex, "%1" .. nbspC):gsub(regex, "%1" .. nbspC .. "%2")
return text
end
function p.isCharacterKiller(character) return p.isKiller(character) end
function p.isKiller(character)
local character = p.resolveParameter(character)
return character.radius ~= nil
end
function p.getPossessiveName(character, params, fullName)
local langs = require("Module:Languages")
params = params or {link = p.bool(p.resolveParameter(character, "link", true)) or false}
params.fullName = fullName or p.bool(p.resolveParameter(character, "fullName", true)) or false
if not character.name then
character = p.getCharacterByName(p.resolveParameter(character))
end
params.firstName = p.getCharacterFirstName(character)
return langs.evaluatePossessive(character, params)
end
--[[
function p.getDativeName(character, params)
params = params or {link = p.bool(p.resolveParameter(character, "link", true)) or false}
if not character.name then
character = p.getCharacterByName(p.resolveParameter(character))
end
if _language ~= "en" then
return langs["dative_" .. _language](character, params) .. firstName
end
return firstName
end
]]
function p.clr(color, text)
text = text or p.resolveParameter(color, 2, true)
color = p.resolveParameter(color)
return clr(color, text)
--return frame:expandTemplate{title = "clr", args = {color, text}}
end
function p.biclr(color, text) return i(p.clr(color, text)) end
function p.ibclr(color, text) return i(p.clr(color, text)) end
--Colour Ordered
function p.clro(text, reset)
reset = reset or p.bool(p.resolveParameter(text, 2, true)) or false
text = p.resolveParameter(text, 1)
if not p.bool(frame:callParserFunction("#varexists:colorOrderCounter")) or reset then
frame:callParserFunction("#vardefine:colorOrderCounter", 1 )
end
local order = tonumber(frame:callParserFunction("#var:colorOrderCounter")) % (#_colorOrder + 1) --making list infinite by going back
order = (order == 0 and 1) or order --adjusting an index from order = #colorOrder => 0 + 1 = 1
frame:callParserFunction("#vardefine:colorOrderCounter", order + 1 )
return ((text ~= nil and text ~= cstr.empty) and b(clr(_colorOrder[order], text))) or clr(_colorOrder[order], text)
end
--Colour Ordered Reset
function p.clror(text)
return p.clro(text, true)
end
function p.clroTest()
local words = string.split(strings.clrTestString, space)
local result = cstr.empty
local clrOffset = 0
for i, word in ipairs(words) do
if #word <= 2 then clrOffset = clrOffset - 1 end
local clrIndex = i + clrOffset
local color = _colorOrder[clrIndex]
result = result .. (#word > 2 and p.tooltip(((i == 1 and p.clror(word)) or p.clro(word)), "order: " .. clrIndex .. br .. "colour: " .. color, true, true) or word) .. (clrIndex < #_colorOrder and space or cstr.empty)
if clrIndex == #_colorOrder then break end
end
return result .. ((#words > #_colorOrder and "...") or (#words < #_colorOrder and dot .. space .. brackets(strings.notEnoughColors)) or cstr.empty)
end
function p.bclr(color, text)
return b(p.clr(color, text))
end
function clr(color, text)
local result = 'inherit' --CSS value, don't change it
--mw.log(tonumber(color, 10))
color = tonumber(color, 10) or color:lower() --if the string is int then convert it to integer
if color == 1 or color == "brown" then result = "ab713c"
elseif color == "1bg" then result = "5d4533"
elseif color == 2 or color == "yellow" then result = "e8c252"
elseif color == "2bg" then result = "d7ad2f"
elseif color == 3 or color == "green" then result = "199b1e"
elseif color == "3bg" then result = "0f791f"
elseif color == 4 or color == "purple" then result = "ac3ee3"
elseif color == "4bg" then result = "672d7f"
elseif color == 5 or color == "pink" then result = "ff0955"
elseif color == "5bg" then result = "cf0b45"
elseif color == 6 or color == "orange" then result = "ff8800"
elseif color == "6bg" then result = "ff5300"
elseif color == 7 or color == "grey" then result = "808080"
elseif color == 8 or color == "red" then result = "d41c1c"
elseif color == 9 or color == "beige" then result = "e7cda2"
elseif color == 10 or color == "blue" then result = "0e98ff"
elseif color == 11 or color == "violet" then result = "b91a9b"
elseif color == "11bg" then result = "800080"
elseif color == 12 or color == "light blue" then result = "9bb0bf"
elseif color == "12bg" then result = "9bb0bf"
elseif color == 13 or color == "faded jade" then result = "418284"
elseif color == 14 or color == "gold" then result = "ffa800"
elseif color == "14bg" then result = "ffa800"
elseif color == 15 or color == "fuchsia" then result = "ec0dea"
elseif color == "15bg" then result = "b30ad2"
elseif color == 16 or color == "white" then result = "ffffff"
elseif color == 17 or color == "vomit green" then result = "8ad672"
elseif color == 18 or color == "blood red" then result = "900a0a"
elseif color == 19 or color == "bright red" then result = "ff0000"
elseif color == 20 or color == "silver" then result = "b5afb0"
elseif color == 21 or color == "turquoise" then result = "37d1c0"
elseif color == 22 or color == "cinnamon" then result = "b74004"
elseif color == 23 or color == "black" then result = "000000"
elseif color == 24 or color == "wiki gold" then result = "b7a269"
elseif color == 25 or color == "bronze" then result = "c2593a"
elseif color == 26 or color == "chartreuse" then result = "b6fa36"
elseif color == 27 or color == "screamin' green" then result = "63ef98"
elseif color == 28 or color == "lavender" then result = "b57edc"
elseif color == 29 or color == "aquamarine" then result = "7fffd4"
elseif color == 30 or color == "ultramarine" then result = "120a8f"
elseif color == 31 or color == "olive" then result = "808000"
elseif color == 32 or color == "pale rose" then result = "f5bfd9"
end
if text ~= nil and text ~= cstr.empty then
result = '<span class="luaClr clr clr' .. color .. '" style="color: ' .. ((result ~= 'inherit' and '#') or cstr.empty) .. result .. ';">' .. text .. '</span>'
end
return result
end
--calls for Strings function Syntax Highlight
function p.sh(value, lang, inline)
inline = p.bool(inline or p.resolveParameter(value, 3, true))
lang = lang or p.resolveParameter(value, 2, true)
value = p.resolveParameter(value)
return sh(value, lang, inline)
end
--text = text containing a tooltip
--tooltip = tooltip content
--iconless = true/false whether the (i) icon should appear after the text containing tooltip (default false)
--borderless = true/false whether underline should be shown (default false)
function p.tooltip(text, tooltip, iconless, borderless, pcViewOnly)
borderless = borderless or p.resolveParameter(text, 4, true) or p.resolveParameter(text, "borderless", true)
iconless = p.bool(iconless or p.resolveParameter(text, 3, true) or (p.resolveParameter(text, "tooltip") and p.resolveParameter(text, 2, true)) or p.resolveParameter(text, "iconless"))
tooltip = tooltip or p.resolveParameter(text, 2, true) or p.resolveParameter(text, "tooltip")
text = p.resolveParameter(text)
local containsLink = p.containsLink(tooltip)
return
'<span class = "tooltip' .. ((containsLink and space .. 'linkIncluded') or cstr.empty) .. ((borderless and space .. 'borderless') or cstr.empty) .. '">' ..
'<span class="tooltipBaseText' .. ((iconless and space .. 'iconless') or cstr.empty) .. '">' .. text .. '</span>'..
(((tooltip ~= nil and tooltip ~= cstr.empty) and
'<span class = "tooltiptext">' ..
((not pcViewOnly and '<span class = "mobileView"> (</span>') or cstr.empty) .. --if it's mobile view then apply add brackets...
'<span class="tooltipTextWrapper">' .. tooltip .. '</span>' ..
((not pcViewOnly and '<span class = "mobileView">) </span>') or cstr.empty) ..
'</span>') or cstr.empty) ..
'</span>'
end
function p.containsLink(text)
local regex = '%[%[.-%]%]'
local rawRegex = '?%&%#91;%&%#91;.-%&%#93;%&%#93;'
if string.match(text, regex) or string.match(text, rawRegex) then --we want to return boolean, not the index
return true
else
return false
end
end
function p.ptb(content, title, patch)
return
ptb(
p.resolveParameter(content),
title or p.resolveParameter(content, 2, true),
patch or p.resolveParameter(content, 3, true)
)
end
function ptb(content, title, patch)
title = title or p.resolveParameter(content, 2, true)
patch = patch or p.resolveParameter(content, 3, true)
if not patch or p.isPatchLive(patch) then
return content
else
return
dynamicBox(content, title, "ptb")
end
end
function p.deletedBox(content, title, patch)
return
deletedBox(
p.resolveParameter(content),
title or p.resolveParameter(content, 2, true),
patch or p.resolveParameter(content, 3, true)
)
end
function deletedBox(content, title, patch)
local result = dynamicBox(content, title, "deletedBox")
patch = (patch and string.trim(patch)) or nil
if patch and not p.isPatchLive(patch) then
--result = includeonly(result) .. noinclude(category("DeleteBox Remove Lists", "Remove List " .. patch))
result = result .. category("DeleteBox Remove Lists", "DeleteBox List for " .. patch)
end
return result --no further logic so redirecting straight to dynamic box
end
function p.wipBox(content, title, patch)
return
wipBox(
p.resolveParameter(content),
title or p.resolveParameter(content, 2, true),
patch or p.resolveParameter(content, 3, true)
)
end
function wipBox(content, title, patch)
local result = dynamicBox(content, title, "wipBox")
return result --no further logic so redirecting straight to dynamic box
end
function dynamicBox(content, title, boxType)
title = title or p.resolveParameter(content, 2, true)
boxType = boxType or p.resolveParameter(content, 3, true)
if not boxType then
return strings.boxTypeNotFound
end
return
'<div class = "dynamicBox ' .. boxType .. '">' ..
((title and '<div class = "dynamicTitle ' .. boxType .. '">' .. title .. '</div>' ) or cstr.empty) ..
'<div class = "dynamicContent ' .. boxType .. '">' .. content .. '</div>' ..
'</div>'
end
function p.isPatchLive(patchParam)
local patch = p.getPatch(patchParam)
--dLog(((patchParam and "[" .. patchParam .. ((not patch and " - Not Found") or cstr.empty) .. "]") or cstr.empty) .. " isPatchLive: " .. (patch and (p.IsFullDateTime(patch.rDate) and tostring(p.dateHasPassed(p.GetDatetime(patch.rDate)))) or tostring(false)))
return patch and (p.IsFullDateTime(patch.rDate) and p.dateHasPassed(p.GetDatetime(patch.rDate))) or false
end
function p.isCharacterLive(character)
if character.dlc then
local dlcM = require("Module:DLCs")
local dlc = dlcM.getCharacterMainDlc(character)
return (dlc == nil) or p.IsFullDateTime(dlc.rDate) and p.dateHasPassed(dlc.rDate)
end
return false
end
function p.getPatch(patch)
local data = require("Module:Datatable" .. p.lang())
for _, patchObj in ipairs(data.patches) do
if patchObj.patch == patch then return patchObj end
end
end
function p.getLatestPatch()
return require("Module:Datatable" .. p.lang()).latestPatch.patch
end
--timestamp = {year = 2022, month = 7, day = 19} --example format
function p.dateHasPassed(timestamp)
local currentDate = os.date("*t") --gives current timestamp: {year = 1998, month = 9, day = 16, yday = 259, wday = 4, hour = 23, min = 48, sec = 10, isdst = false}
local releaseOffset = 60 * 60 * 15 --release hour 15:00 (server time is -2 hours from my current time)
timestamp = p.standardiseDateObject(timestamp)
--mw.log(os.difftime(os.time(timestamp) + releaseOffset, os.time(currentDate)))
return os.difftime(os.time(timestamp) + releaseOffset, os.time(currentDate)) < 0 -- subtracting releaseOffset to adjust timing according to actual release hour
end
function p.isEventLive(element)
--not element.event is to skip events from different tables, such as tomes
return not (element.event or element.patch) and (element.rDate and p.dateHasPassed(element.rDate)) and (element.eDate == nil or not p.dateHasPassed(element.eDate))
or (element.patch and element.rDate and p.isFullDateTime(element.rDate) and p.dateHasPassed(element.rDate) and (element.eDate == nil or not p.dateHasPassed(element.eDate)))
end
function p.standardiseDateObject(dateObj)
if type(dateObj) == types.table then --if we pass table, we need to check time values and populate them to 0 by default
if not dateObj.hour then dateObj.hour = 0 end
if not dateObj.min then dateObj.min = 0 end
if not dateObj.sec then dateObj.sec = 0 end
dateObj = os.time(dateObj) --we convert it to seconds constant (resp. variable)
end
dateObj = p.GetDatetime(dateObj)
return dateObj
end
function p.resolvePatchList()
local data = require("Module:Datatable" .. p.lang())
local result = cstr.empty
local yearCounter = 0
local tempResult = cstr.empty
for _, patch in ipairs(data.patches) do
local patchDate = p.standardiseDateObject(patch.rDate)
local suffixLink = getSuffixLink(patch)
local suffixPatchName = getSuffixPatchName(patch) --in case of PTB we need to attach " (PTB)" suffix
if yearCounter ~= patchDate.year then
if yearCounter ~= 0 then
result = result .. frame:callParserFunction{ name = '#tag', args = {"tabber", tempResult} } .. nl
end
tempResult = cstr.empty
yearCounter = patchDate.year
result = result ..
"|-|" .. patchDate.year .. "=" .. nl
end
local patchNotes, result = pcall(function () return frame:expandTemplate{title = strings.patch .. space .. patch.patch .. suffixPatchName} end)
if patchNotes and --[[not patch.ptb and]] patch ~= patches[#patches] then
local patchVersions = string.split(patch.patch, '.')
if patchVersions[3] ~= "0" then patchVersions[3] = "0" end
for i = #patchVersions, 4 , -1 do --in case that patch contains anything else after last patch number let's remove it (example: "6.1.2 / 6.1.3")
table.remove(patchVersions)
end
local patchLink = strings.patch .. space .. patch.patch .. suffixPatchName
local patchLinkModified = strings.patch .. space .. table.concat(patchVersions, ".")
tempResult = tempResult ..
frame:expandTemplate{title = "!"}.. "-" .. frame:expandTemplate{title = "!"} .. patch.patch .. suffixPatchName .. "=" .. nl .. --creating individual patch tabs
frame:expandTemplate{title = "(!"} .. space .. 'class = "wikitable"' .. nl ..
"!" .. nl .. "==== " .. link(patchLinkModified .. ((suffixLink and suffixLink .. tl .. patchLink) or cstr.empty)) .. " - " .. p.toDate(os.time(patchDate)) .. " ====" .. nl .. --28 April 2022
frame:expandTemplate{title = "!-"} .. nl ..
frame:expandTemplate{title = "!"} .. result .. nl ..
frame:expandTemplate{title = "!)"} .. nl
end
end
result = result .. frame:callParserFunction{ name = "#tag", args = {"tabber", tempResult} } .. nl --post loop aiditon for last year
result = frame:extensionTag{name = 'tabber',content = result}
return result
end
function getSuffixPatchName(patch)
if patch.ptb then
return " (PTB)"
end
return cstr.empty
end
function getSuffixLink(patch)
local versions = string.split(patch.patch, '.')
if versions[3] ~= "0" then
return "#Hotfix " .. patch.patch
elseif patch.ptb then
return "#Public Test Build " .. patch.patch --patch needs to not contain "(PTB)" part
end
return nil
end
function p.getPatchLink(patch)
local suffixLink = getSuffixLink(patch)
local suffixPatchName = getSuffixPatchName(patch) --in case of PTB we need to attach " (PTB)" suffix
local patchLink = strings.patch .. space .. patch.patch .. suffixPatchName
local patchVersions = string.split(patch.patch, '.')
if patchVersions[3] ~= "0" then patchVersions[3] = "0" end
local patchLinkModified = strings.patch .. space .. table.concat(patchVersions, ".")
return link(patchLinkModified .. ((suffixLink and suffixLink .. tl .. patchLink) or cstr.empty))
end
--wt = wordTable
function p.getWord(wt, index)
local keys = table.keys(wt)
local maxValue = table.max(keys)
local minValue = table.min(keys)
if index > maxValue then return wt[maxValue] end --if the index is higher than highest amount, return word of highest amount available
if wt[index] then return wt[index] end -- if the index is exactly of plural count then return exact word on index
if index < minValue then return wt[minValue] end --if index not found, it means it's lower than lowest amount in a word table
for i, keyValue in ipairs(keys) do
if index < keyValue then
return wt[keys[i - 1]] --if the index is between two amounts(indexes) use the lower one
end
end
end
--{{header = 2022, content = "..."}, {}, ...}
function p.getTabberFromTable(tabberTable)
local result = cstr.empty
for _, section in ipairs(tabberTable) do
result = result .. nl .. '|-|' .. (section.header or strings.tabberHeaderNotFound) .. '=' .. nl .. section.content .. nl
end
result = frame:callParserFunction{ name = '#tag', args = {"tabber", result} }
return result
end
function p.isTagPresent(el, searchedTag)
if el.tags and #el.tags > 0 then
for _, tag in ipairs(el.tags) do
if string.lower(searchedTag) == string.lower(tag) then
return true
end
end
end
return false
end
function p.yesNo(param, note, imgSize)
imgSize = imgSize or p.resolveParameter(param, 3, true) or p.resolveParameter(param, "size", true)
note = note or p.resolveParameter(param, 2, true) or p.resolveParameter(param, "note", true)
param = p.resolveParameter(param, 1, true) or param
local imageFilename = "Unknown QuestionMark.png"
local tickClass = "unknown"
local size = (imgSize or 32) .. "px"
if p.bool(param) == true then
imageFilename = "DBD_UI_Icon_CheckMark.png"
tickClass = "tickYes"
elseif p.bool(param) == false then
imageFilename = "DBD_UI_Icon_X.png"
tickClass = "tickNo"
elseif param:lower() == "nan" then
imageFilename = "NaN.png"
tickClass = "tickNaN"
end
--lg(span(p.tooltip(span(file(imageFilename, size), tickClass),cstr.empty,true,true), "yesNo"))
return
span(
p.tooltip(
span(file(imageFilename, size), tickClass),
note or cstr.empty,
true,
true
)
, "yesNo")
end
--imgType:
-- perk = perk with switching background
-- sosPerk = teachable frame background
--icon icon that is inside
--imgSize currently not used, should be available for manually adjusting the size of image
function p.assembleImage(imgType, icon, imgSize, params)
local variousLogic = require("Module:Various")
params = params or p.resolveParameter(imgType, 4, true) --not sure if table can be passed directly from wiki
local rarity = p.resolveParameter(imgType, "rarity", true)
local rarityNumber = tonumber(rarity) or (params and params.rarity and tonumber(params.rarity))
rarity = string.replace(((rarityNumber and (variousLogic.rarity[rarityNumber].techName or variousLogic.rarity[rarityNumber].name)) or (rarity or (params and params.rarity)) or variousLogic.rarity[1].techName or variousLogic.rarity[1].name):lower(), space, dash) --Common by default
rarity = string.replace(rarity, "&", "and")
imgSize = imgSize or p.resolveParameter(imgType, 3, true) or p.resolveParameter(imgType, "size", true)
icon = icon or p.resolveParameter(imgType, 2, true) or p.resolveParameter(imgType, "icon", true)
imgType = p.resolveParameter(imgType)
local imgSizeStyle = (imgSize and ' style = "width: ' .. imgSize .. 'px; height: ' .. imgSize .. 'px;"') or cstr.empty
local isIconLink = (params and params.iconLink == true) or false
local align = (params and params.align and "float: " .. params.align .. semi) or cstr.empty
if imgType == "sosPerk" then
return
'<div class = "game-element-container inline-flex">' ..
'<div class = "game-element-bg-settings game-element-bg-settings-size-sos teachable-perk-element"' .. imgSizeStyle .. '></div>' ..
'<div class = "game-element-bg-settings-size-sos game-element-imgObject absolute"' .. imgSizeStyle .. '>' .. file(p.getIcon(icon), "link=" .. icon) .. '</div>' ..
'<div class = "game-element-bg-settings game-element-bg-settings-size-sos teachable-indicator-perk-element absolute"' .. imgSizeStyle .. '></div>' ..
'</div>'
elseif imgType == "perk" then
return
'<div class = "game-element-container flex">' ..
'<div class = "game-element-bg-settings game-element-bg-settings-size-perk animate-perk-settings animate-perk-bg"' .. imgSizeStyle .. '></div> ' ..
'<div class = "game-element-bg-settings game-element-bg-settings-size-perk animate-perk-settings animate-perk-level absolute"' .. imgSizeStyle .. '></div>' ..
'<div class = "game-element-bg-settings-size-sos game-element-imgObject absolute"' .. imgSizeStyle .. '>' .. file(p.getIcon(icon), "link=" .. icon, "center") .. '</div>' ..
'</div>'
elseif table.contains({"addon", "item", "power", "offering"}, imgType) then
--rarity = (rarity and variousLogic.rarity[rarity].name:lower()) or string.replace((params and (params.rarity and (tonumber(params.rarity) and variousLogic.rarity[params.rarity].name) or params.rarity) or variousLogic.rarity[1].name):lower(), space, dash)
addonContainer = (imgType == "addon" and "addon-container") or cstr.empty
imgSize = imgSize or 128
if isIconLink then
return
'<span class = "margin-auto" style = "max-width: ' .. imgSize .. 'px;' .. align ..'">' ..
'<span class = "game-element-container ' .. addonContainer .. ' flex relative">' ..
'<span class = "game-element-bg-settings game-element-bg-settings-size-perk ' .. rarity .. '-' .. (table.contains({"item", "offering"}, imgType) and imgType or "item") .. '-element" ' .. imgSizeStyle .. '></span>' ..
((imgType == "addon" and '<span class = "game-element-bg-settings game-element-bg-settings-size-perk addon-marker addon-marker-il absolute"></span>') or cstr.empty) ..
'<span class = "game-element-bg-settings-size-sos absolute" ' .. imgSizeStyle .. '>' .. file(p.getIcon(icon), "link=" .. icon, "300px") .. '</span>' ..
'</span>' ..
'</span>'
else
return
'<div class = "margin-auto" style = "max-width: ' .. imgSize .. 'px; scale: calc(' .. imgSize .. '/128); ' .. align ..'">' ..
'<div class = "game-element-container ' .. addonContainer .. ' flex relative">' ..
'<div class = "game-element-bg-settings game-element-bg-settings-size-perk ' .. rarity .. '-' .. (table.contains({"item", "offering"}, imgType) and imgType or "item") .. '-element"></div>' ..
((imgType == "addon" and '<div class = "game-element-bg-settings game-element-bg-settings-size-perk addon-marker absolute"></div>') or cstr.empty) ..
'<div class = "game-element-bg-settings-size-sos absolute">' .. file(p.getIcon(icon), "link=" .. icon, "300px") .. '</div>' ..
'</div>' ..
'</div>'
end
end
end
function p.resolvePageHeader(params)
return nil
end
return p
Advertisement
English
Modulo:Utils
Advertisement