Module:DLCs
Jump to navigation
Jump to search
local p = {}
local utils = require("Module:Utils")
local data = require("Module:Datatable" .. utils.lang())
local mathOps = require("Module:MathOps")
local str = require("Module:Strings")
local ext = require("Module:Extensions")
local various = require("Module:Various")
local c = utils -- As there is onlny one function relevant to colors I make only alias to utils Module to convenient naming "c - color"
local frame = mw.getCurrentFrame()
local _chapterCategories = {1, 2} --DLC categories that have their number index in name
local _checkForLastDlcCapsules = 3
local _defaultCharacterCountInDlcType = {
[1] = 2, --Full chapters has 2 characters by default
[2] = 1 --Half chapters has 1 character by default
}
local strings = {
the = cstr.the,
noDLC = "DLC wasn't found in DLC table in [[Module:datatable|Datatable Module]]",
charNotFound = "Character not found",
noSpecialTheme = "No special Theme Music",
noSteamLink = "No linked Store Page",
sp = "Store Page",
rd = "Release Date",
cat = "Category",
cost = "Cost",
theme = "Theme Music",
killerTheme = "Killer Theme",
survTheme = "Survivor Theme",
chapter = "CHAPTER",
paragraph = "paragraph",
halfChapter = "half-chapter",
retracted = "Retracted",
retired = "Retired",
retractedDlc = "Retracted #1# #2#", --name of dlc category will be placed
retiredDlc = "Retired #1# #2#",
dlcLink = "Downloadable Content",
logo = "Logo",
dlc = "DLC",
mainArticle = "Main Article", --DLC page text under chapter title
nSwitch = iclr(4, 'This DLC comes pre-installed on the Nintendo Switch version of the game.'),
xps = iclr(4, 'This DLC comes pre-installed on the Console versions of the game.'),
was = "was",
willBe = "will be",
released = "released",
uknownDate = "unknown date",
forString = "for",
orString = "or",
on = "on", -- was released "on" {date}
inString = "in",
adds = "adds", -- The DLC "adds" {character(s)}
andString = "and",
}
----------------------------------------------
if utils.lang() ~= cstr.empty and (counter or 0) < 1 then
counter = (counter or 0) + 1
strings = require("Module:DLCs" .. utils.lang()).strings
end
local fileSuffixes = {
dlcImage = "_main_header", --DLC Image
themeMusic = "_Theme_Music" --DLC Theme music
}
function p.getLatestDlcId()
result = 0;
for _, dlc in ipairs(data.dlcs) do
if result < dlc.id then result = dlc.id end
end
return result
end
p.dlcMinIdForCapsuleCheck = p.getLatestDlcId() - _checkForLastDlcCapsules + 1
--used for truncating the page name (CHAPTER ##: )DLCName to get only DLCName
function p.getShortName(name)
if name:match(":") ~= nil then
--mw.log(name:match("^[%a%d ]*: ?(.+)"))
return name:match("^[%a%d%. ]*: ?(.+)")
else
return name
end
end
--with "Chapter" prefix
function p.getDlcFullNameByCharacter(character)
local charDlcs = p.getDlcs(character)
local result = cstr.empty
for i, dlc in ipairs(charDlcs) do
result = result .. ((i > 1 and dnl) or cstr.empty) ..
link(((table.contains(_chapterCategories, dlc.category) and strings.chapter .. space .. p.getDlcOrderNumber(dlc) .. colon .. space) or cstr.empty) .. dlc.name)
end
return result
end
function p.getDlcs(character)
return
(
type(character) == types.table
and type(character.dlc) == types.table
and (character.dlc.category and {character.dlc}) --dlc can be already populated as a dlc table
)
or p.getDlcsByCharacter(character)
end
--called directly from wiki
function p.getMainDlcFullNameByCharacter(character)
character = utils.getCharacterByName(utils.resolveParameter(character))
if character then
character.dlc = p.getMainDlc(p.getDlcsByCharacter(character))
return p.getDlcFullNameByCharacter(character)
end
return strings.charNotFound
end
function p.getDlcByCharacter(character) return p.getDlcsByCharacter(character) end
function p.getDlcsByCharacter(character)
if not character or not character.name then
character = utils.getCharacterByName(utils.resolveParameter(character))
end
local result = {}
if character then
if type(character.dlc) == types.table then
if not table.empty(character.dlc) then
if type(character.dlc[1]) == types.table then
result = character.dlc
elseif type(character.dlc[1]) == types.number then
for _, dlcId in ipairs(character.dlc) do
table.add(result, p.getDlcById(dlcId))
end
end
end
elseif type(character.dlc) == types.number then
table.add(result, p.getDlcById(character.dlc))
end
end
return result
end
function p.isDiscounted(dlcList)
halvingDlcCategories = {8}
if dlcList.category and table.contains(halvingDlcCategories, dlcList[1].category) then
return true
else
for _, dlc in ipairs(dlcList) do
if table.contains(halvingDlcCategories, dlc.category) then
return true
end
end
end
return false
end
function p.getChapterPackDlc(dlcList)
for _, dlc in ipairs(dlcList) do
if dlc.category == 8 then return dlc end
end
end
function p.getDlcOrderNumber(dlc)
dlc = utils.resolveParameter(dlc)
if dlc.category == 1 then --chapter
return getChapterNumber(dlc)
elseif dlc.category == 2 then --paragraph
return getParagraphNumber(dlc)
end
return 0
end
function p.getDlcOrderNumberByCharacter(character)
local charDlcs = p.getDlcsByCharacter(character)
for _, dlc in ipairs(charDlcs) do
if table.contains(_chapterCategories, dlc.category) then
return p.getDlcOrderNumber(dlc)
end
end
return 0
end
function getChapterNumber(dlc)
local counter = 0
for _, dlcItem in ipairs(data.dlcs) do
if dlcItem.category == 1 then
counter = counter + 1
if dlc.id == dlcItem.id then
return counter
end
end
end
end
function getParagraphNumber(dlc)
local counter = 0
local previousChapterFound = false
local dlcIndex = 0
for i, dlcItem in ipairs(data.dlcs) do
if dlcItem.id == dlc.id then
dlcIndex = i
break
end
end
while not previousChapterFound and dlcIndex > 0 do
if data.dlcs[dlcIndex].category == 1 then
return getChapterNumber(data.dlcs[dlcIndex]) + 0.5
end
dlcIndex = dlcIndex - 1
end
end
function resolveDlcParameter(dlc)
if dlc == nil then
return p.getDlcByName(mw.title.getCurrentTitle().text)
elseif type(dlc) == "table" then
if dlc.args[1] == nil then
return p.getDlcByName(mw.title.getCurrentTitle().text) --none parameter => calling from page
end
if type(dlc.args[1]) == nil then
return p.getDlcByName(p.getShortName(mw.title.getCurrentTitle().text))
elseif type(dlc.args[1]) == "string" then
if tonumber(dlc.args[1]) == nil then
return p.getDlcByName(dlc.args[1])
else
return p.getDlcById(tonumber(dlc.args[1]))
end
end
elseif type(dlc) == "string" then
return p.getDlcByName(dlc)
elseif type(dlc) == "number" then
return p.getDlcById(dlc)
end
end
function p.getMainDlc(dlcList)
if (dlcList and dlcList.category) then --dlcList is actually a sole DLC object
if table.contains(_chapterCategories, dlcList.category) then
return dlcList
else
return nil
end
end
for _, dlc in ipairs(dlcList or {}) do
if table.contains(_chapterCategories, dlc.category) then
return dlc
end
end
end
function p.getCharacterMainDlc(character)
return p.getMainDlc(p.getDlcs(character))
end
function p.getDlcByName(name)
for _, dlc in ipairs(data.dlcs) do
if name == dlc.name then return dlc end
end
name = p.getShortName(name) --if the name was passed with CHAPTER prefix, remove it
for _, dlc in ipairs(data.dlcs) do
if name == dlc.name then return dlc end
end
end
function p.getDlcById(id)
for _, dlc in ipairs(data.dlcs) do
if id == dlc.id then
local dlcObj = table.copy(dlc)
return dlcObj
end
end
end
function p.getCountOfDlcs()
return utils.getCount("dlc")
end
function p.getCountOfChapters()
return utils.getCount("chapter")
end
function p.getCountOfParagraphs()
return utils.getCount("paragraph")
end
function p.getCountOfClothingDlcs()
return utils.getCount("clothing")
end
function p.getCountOfSoundtracks()
return utils.getCount("ost")
end
function p.getCountOfCharacterDlcs()
return utils.getCount("character")
end
function getCostTable(dlc)
local result = cstr.empty
if type(dlc.cost) == types.string then
return tl .. dlc.cost
end
if dlc.cost == false then
return false
end
if dlc.cost == nil then
dlc = populateDefaultCosts(dlc)
if not dlc.cost then return result end
end
for i, cost in ipairs(dlc.cost) do
local ccy = getCurrencyById(cost.ccy)
if cost.value == 0 then
cost.value = "-"
end
result = result .. tl
if ccy.gc then
result = result .. utils.commaFormat(cost.value) .. space .. utils.IconLink(ccy.plural or ccy.name .. "s") -- #1: calling IconLink template #2: the 's' makes plural version of currency,
else
result = result .. ccy.symbol .. cost.value
end
if dlc.cost[i + 1] ~= nil then
result = result .. nl .. ntl .. nl
end
end
return result
end
function p.resolveDlcTableMainPage()
local result = cstr.empty
local name, fileName, linkPage
local lastCategory = 0
local closingResolve, offset
utils.sortDlcByCategory(data.dlcs)
local latestId = p.getLatestDlcId()
result = result ..
'<div class="fpbox" id="fpDlcs" style="text-align: center;">' ..
'<div class="heading">' .. link(strings.dlcLink) .. '</div>' ..
'<div class="fplinks">'
for i, dlc in ipairs(data.dlcs) do
if not dlc.skip then
if dlc.category ~= lastCategory then
lastCategory = dlc.category
result = result ..
'<div class = "dlcCategorySection">' ..
'<div class = "categoryLabel">' .. resolveDlcCategory(dlc) .. '</div>' ..
'<div class = "categoryDlcs">'
end
name = dlc.name or strings.noDLC
fileName = p.resolveDlcCapsuleFileNameByDlc(dlc, latestId - _checkForLastDlcCapsules + 1) -- "-1" = check file for last TWO DLCs; [Latest DLC ID] - number of last DLC + 1 => _checkForLastDlcCapsules = 3 => last 3 DLC are checked
linkPage = name .. (dlc.multiName and space .. brackets(strings.dlc) or cstr.empty)
--Capsule
result = result ..
'<div class = "displayFlex dlcCapsule relative">' ..
'<div class = "displayFlex dlcCapsuleImageContainer relative">' ..
'<div class = "dlcBorder absolute"></div>' ..
'<div class = "dlcLightEffect absolute"></div>' ..
'<div class = "dlcCpasuleImg relative">' .. file(fileName, 'link=' .. linkPage) .. '</div>' ..
'</div>' ..
'<div class = "dlcLink displayFlex">' .. link(linkPage, name) .. '</div>' ..
'</div>'
closingResolved = false
offset = 1
repeat
local futureDlc = data.dlcs[i+offset]
--if the DLC is last, or any other following DLC(s) are skip = true, or simply current DLC and upcoming NON-skip DLC are different category
if dlc.id == data.dlcs[#data.dlcs].id or (futureDlc.skip and futureDlc.id == data.dlcs[#data.dlcs].id) or (dlc.category ~= futureDlc.category and not futureDlc.skip) then
closingResolved = true
result = result ..
'</div>' .. --closing ".categoryDlcs"
'</div>' -- closing ".dlcCategorySection"
elseif dlc.category == futureDlc.category and not futureDlc.skip then
closingResolved = true
else
offset = offset + 1
end
until closingResolved or offset + i > #data.dlcs
end
end
result = result ..
'</div>' ..
'</div>'
return result
end
function p.resolveDlcCapsuleFileNameByDlc(dlc, latestId)
local fileConst = "Capsule"
local fileName = utils.resolveFileName(dlc.name) .. fileConst
if dlc.id >= latestId and not utils.isValidFileName(fileName, cstr.png) then
return 'UnknownDLC.png'
end
return fileName .. dot .. cstr.png
end
--currently works only for Chapters and Half-Chapters
function populateDefaultCosts(dlc)
local result = nil
local dlcChars = utils.getCharsByDlc(dlc)
if not (dlc.retracted or dlc.retired) and (dlc.category == 1 or dlc.category == 2) then
result = {}
local dlcCharCount = (#dlcChars > 0 and #dlcChars) or _defaultCharacterCountInDlcType[dlc.category]
table.insert(result, {ccy = 1, value = data.dlcCosts[dlc.category][dlcCharCount]}) --Dollar
table.insert(result, {ccy = 5, value = data.charACCost * dlcCharCount}) -- Auric Cells
if not dlc.licensed then
table.insert(result, {ccy = 3, value = data.charISCost * dlcCharCount}) -- IS Cost per character
end
end
dlc.cost = result
return dlc
end
function getCountDlcCosts(dlc)
if type(dlc.cost) == "string" then return 1 end
return #dlc.cost
end
function getCurrencyById(id)
for _, ccyItem in ipairs(ccy) do
if ccyItem.id == id then return ccyItem end
end
end
--[[
function p.resolveThemeMusic(dlc)
return resolveThemeMusic(p.getDlcById(dlc))
end]]
--Stranger Things Theme Music.ogg
function resolveThemeMusic(dlc)
local dlcChars = utils.getCharsByDlc(dlc)
local survTheme
local killerTheme
for _, character in ipairs(dlcChars) do
if utils.isKiller(character) and not killerTheme then
killerTheme = various.resolveCharacterThemeMusic(character)
elseif not survTheme then
survTheme = various.resolveCharacterThemeMusic(character)
end
if killerTheme and survTheme then break end
end
return survTheme, killerTheme
--[[
lg(dlc.name)
local name = utils.resolveFileName(utils.CapitalizeName(utils.RemoveSpecialCharacters(dlc.name)), true, true) .. fileSuffixes.themeMusic
lg(name)
for _, theme in ipairs(dlcThemes) do
if theme.id == dlc.id then
name = theme.fileName
end
end
local valid = utils.isValidFileName(name, cstr.ogg)
return (valid and link(cstr.file .. name .. dot .. cstr.ogg)) or strings.noSpecialTheme
]]
end
function resolveShopLink(dlc)
if dlc.link ~= nil and type(dlc.link) == types.number and dlc.link > 0 then
return "[https://store.steampowered.com/app/" .. dlc.link .. " " .. strings.sp .. "]"
end
return strings.noSteamLink
end
function resolveDlcCategory(dlc)
if dlc.retracted then
return utils.getDynamicString(dlcCategories[dlc.category], strings.retractedDlc)
elseif dlc.retired then
return utils.getDynamicString(dlcCategories[dlc.category], strings.retiredDlc)
else
return dlcCategories[dlc.category]
end
end
function p.getImageFileNameByDlc(id) mw.log(getImageFileNameByDlc(p.getDlcById(id))) end --dev remove
function getImageFileNameByDlc(dlc)
for _, dlcImage in ipairs(dlcImages) do
if dlc.id == dlcImage.id then
return utils.resolveImageName(dlcImage.cover)
end
end
return utils.resolveImageName(utils.resolveFileName(dlc.name) .. fileSuffixes.dlcImage)
end
function p.resolveDlcTable(dlc)
dlc = resolveDlcParameter(dlc)
if dlc == nil then return bclr("gold", strings.noDLC) .. nl end
local result = cstr.empty
local costTable = getCostTable(dlc)
local survTheme, killerTheme = resolveThemeMusic(dlc)
result = result ..
'{| class="wikitable" style="float:right;"' .. nl ..
ntl .. 'style="font-size: 24px;"' .. nl ..
hl .. 'width="350px" align="center" colspan="2"' .. tl .. b(utils.replaceLastSpaceByNBSP(dlc.name)) .. nl .. ntl .. nl ..
tl .. 'class="center" width="350px" height="50px" colspan="2"' .. tl .. file(getImageFileNameByDlc(dlc), '300px') .. nl .. ntl .. nl ..
tl .. 'width="100px"' .. tl .. b(strings.rd) .. dtl .. 'width="200px"' .. tl .. utils.resolveDateTime(dlc.rDate) .. nl .. ntl .. nl
if dlc.retired then
result = result .. tl .. b(strings.retired) .. nl .. tl .. utils.resolveDateTime(dlc.retired) .. nl .. ntl .. nl
end
if dlc.retracted then
result = result .. tl .. b(strings.retracted) .. nl .. tl .. utils.resolveDateTime(dlc.retracted) .. nl .. ntl .. nl
end
result = result ..
tl .. b(strings.cat) .. dtl .. 'width="200px"' .. tl .. resolveDlcCategory(dlc) .. nl .. ntl .. nl ..
((dlc.cost and
tl .. 'rowspan = ' .. getCountDlcCosts(dlc) .. tl .. b(strings.cost) .. dtl .. 'width="200px" ' .. costTable .. nl .. ntl .. nl) or cstr.empty)
if killerTheme or survTheme then
result = result ..
((killerTheme and tl .. b((killerTheme and not survTheme and strings.theme or strings.killerTheme)) .. dtl .. 'width="200px"' .. tl .. killerTheme .. nl .. ntl .. nl) or cstr.empty) ..
((survTheme and tl .. b(strings.survTheme) .. dtl .. 'width="200px"' .. tl .. survTheme .. nl .. ntl .. nl) or cstr.empty)
end
result = result ..
hl .. 'align="center" colspan="2"' .. tl .. resolveShopLink(dlc) .. nl ..
"|}"
mw.log(result)
return result
end
function p.getListOfDlcChapters()
local result = cstr.empty;
local counter = 1;
for _, dlcItem in ipairs(data.dlcs) do
if dlcItem.category == 1 then
result = result .. nbullet .. link(strings.chapter .. space .. counter .. colon .. space .. dlcItem.name) .. nl
counter = counter + 1
end
end
mw.log(result)
return result
end
--If the iconlink for currrency doesn't work, add new field 'plural' in ccy table in Datatable module: plural = "Bloodpoints" (for Bloodpoint currency)
function p.resolveDlcPage(args)
local dlcTypes = resolveDlcTypes(utils.resolveParameter(args))
local header = utils.resolveParameter(args):upper()
header = (header == strings.halfChapter:upper() and strings.chapter) or header
local result = cstr.empty
utils.sortDlcByCategory(data.dlcs)
local counter = 1
for _, dlc in ipairs(data.dlcs) do
for _, dlcType in ipairs(dlcTypes) do
if dlc.category == dlcType then
result = result .. getDlcArticle(dlc, counter, header)
counter = counter + 1
break
end
end
end
mw.log(result)
return result
end
function getDlcArticle(dlc, number, header)
return
'<h4> ' .. header .. space .. number .. colon .. space .. dlc.name .. ' </h4>' .. nl ..
file(getDlcLogo(dlc) .. dot .. cstr.png, 'thumb', '400x150px') .. nlp ..
i(strings.mainArticle .. colon .. space .. link((dlc.name .. (dlc.multiName and space .. brackets(strings.dlc) or cstr.empty)))) .. nlp ..
the(dlc) .. space .. b(dlc.name) .. space .. strings.dlc .. space .. getDlcTime(dlc) .. space .. getDlcCosts(dlc) .. space .. getDlcReleaseDate(dlc) .. dot .. nlp ..
((#utils.getCharsByDlc(dlc) > 0 and the(strings.dlc) .. space .. strings.dlc .. space .. strings.adds ..space .. getCharacterString(dlc) .. dot .. dnl) or cstr.empty) .. --if there are no characters skip the line completely
((dlc.flags and getAdditionalNote(dlc)) or cstr.empty)
end
function getDlcLogo(dlc)
return strings.logo .. space .. utils.FirstLetterLower(utils.resolveFileName(dlc.name, false, true))
end
function getDlcTime(dlc)
result = strings.released
if dlc.rDate ~= nil and utils.IsFullDateTime(dlc.rDate) and utils.toTimestamp(dlc.rDate) < utils.today() then
result = strings.was .. space .. result
else
if utils.getDatePart(dlc.rDate, "month") and utils.getMonth(dlc.rDate) < utils.getMonth(utils.today()) then
result = strings.was .. space .. result
else
result = strings.willBe .. space .. result
end
end
return result
end
function getDlcReleaseDate(dlc)
result = cstr.empty
if dlc.rDate ~= nil then
result = ((utils.IsFullDateTime(dlc.rDate) and strings.on) or strings.inString) .. space .. utils.resolveDateTime(dlc.rDate, true)
end
return result
end
function getDlcCosts(dlc)
if type(dlc.cost) == types.string then return strings.forString .. space .. dlc.cost end
local result = cstr.empty
if dlc.cost == nil then
populateDefaultCosts(dlc)
end
if dlc.cost then --if it not nill or not false, then process costs
if #dlc.cost > 0 then
result = result .. strings.forString .. space
end
utils.sortRealCcyFirst(dlc.cost)
for i, cost in ipairs(dlc.cost) do
local currency = utils.getCcyById(cost.ccy)
if not currency.gc then --real currency
result = result .. currency.symbol .. cost.value
else --game currency
result = result .. utils.commaFormat(cost.value) .. space .. utils.IconLink(currency.plural or currency.name .. 's')
end
if i < #dlc.cost then --add ' or ' if there will be more currencies
result = result .. space .. strings.orString .. space
end
end
end
return result
end
function getCharacterString(dlc)
local characters = utils.getCharsByDlc(dlc)
local result = cstr.empty
utils.sortCharsKillersFirst(characters)
for i, character in ipairs(characters) do
--result = result .. utils.IconLink((not character.power and character.shortName or character.name) or character.name)
result = result .. utils.IconLink(
(utils.isKiller(character) and character.name .. ((character.multiName and space .. brackets(ils.killer)) or cstr.empty)) or character.shortName or character.name,
((utils.isKiller(character) and the(character)) or cstr.empty) .. character.name
)
if i + 1 == #characters then
result = result .. space .. strings.andString .. space
elseif i < #characters then --add comma if there will be more currencies
result = result .. comma .. space
end
end
return result
end
function getAdditionalNote(dlc)
local result = cstr.empty
for _, flag in ipairs(dlc.flags) do
if flag == "xps" then result = result .. strings.xps .. dnl
elseif flag == "switch" then result = result .. strings.nSwitch .. dnl
end
end
return result
end
function resolveDlcTypes(dlcTypeString)
if dlcTypeString == strings.chapter:lower() then return {1, 7}
elseif dlcTypeString == strings.halfChapter:lower() then return {2}
--elseif dlcTypeString == "clothing pack" then return {3}
--elseif dlcTypeString == "soundtrack" then return {4}
--elseif dlcTypeString == "character pack" then return {5}
elseif dlcTypeString == "other" then return {6}
end
return {0} --not found
end
return p