Dead by Daylight Wiki
Advertisement

local p = {}
local frame = mw.getCurrentFrame()
local data = require("Module:Datatable")
local dataPerks = require("Module:Datatable/Perks")
local utils = require("Module:Utils")
local str = require("Module:Strings")

local strings = {
	descNotFound = "Unable to retrieve the Perk description or unable to display it.",
	perkValuesNotFound = "Unable to retrieve the Perk values.",
	unitNotFound = "Unable to retrieve the measurment unit for the Perk values.",
	notUniquePerk = "Unable to provide Character name as this is a General Perk not assigned to any specific Character.",
	dlcNotFound = i(bclr(6, "Unable to retrieve linked DLC, hence unable to display its associated Perks." .. cstr.contact)),
	charNotFound = i(bclr(6, "Unable to retrieve linked Character, hence unable to display their associated Perks." .. cstr.contact)),
	several = "Several",
	all = 'All', --used in Perk Table for common perks of Survivrros and Killers
	icon = "Icon",
	name = "Name",
	desc = "Description",
	character = "Character",
	killers = "Killers",
	survivors = "Survivors",
	belongs = "This Perk belongs to",
	noChar = "No linked Character available, unable to display their Perks.",
	
	unlockBloodweb = bclr(11, "Unlocking this Perk makes it available in the Bloodweb of all #character#"),
	unlockable = bclr(12, "May be unlocked in the Shrine of Secrets or in #owner# Bloodweb at"),
	level = "Level"
}

function getMaxPerkId()
	result = 0
	for _, perk in ipairs(perks) do
		if result < perk.id then result = perk.id end
	end
	return result
end

function p.getNextFreePerkId()
	return getMaxPerkId() + 1
end

function getUnusedPerksCount()
	local result = 0
	
	for _, perk in ipairs(perks) do
		if perk.unused then result = result + 1 end
	end

	return result
end

function p.getRetiredPerksCount()
	local result = 0
	
	for _, perk in ipairs(perks) do
		if perk.retired then result = result + 1 end
	end

	return result
end

function getCommonPerksCount()
	local result = 0
	
	for _, perk in ipairs(perks) do
		if perk.character == nil and perk.unused ~= true then result = result + 1 end
	end
	
	return result
end

function p.getCountPerksByType(pType)
	pType = utils.resolveParameter(pType)
	local result = 0
	
	for _, perk in ipairs(perks) do
		if perk.charType == pType and perk.unused ~= true then result = result + 1 end
	end
	--mw.log(result)
	return result
end

function p.getCountPerksByCategory(cat, charType)
	local category = utils.resolveParameter(cat)
	local charType = charType or utils.resolveParameter(cat, 2, true)
	
	return #getPerksByCategory(category, charType)
end

local _unusedPerksCount = getUnusedPerksCount()
local _commonPerksCount = getCommonPerksCount()
local _uniquePerksCount = table.getn(perks) - _unusedPerksCount - _commonPerksCount
local _nonUniquePerksCount = _unusedPerksCount + _commonPerksCount

p._nonUniquePerksCount = _nonUniquePerksCount
p._allPerksCount = table.getn(perks)

function p.getPerksCount() --active in game
	return _uniquePerksCount + _commonPerksCount
end

function ResolveIntParam(param) --TODO remove, shouldn't be used anymore
	if type(param) == "table" and param.args[1] ~= nil then
		return tonumber(param.args[1])
	else
		return param
	end
end

function getUnitById(id)
	--mw.log(id)
	for _,unit in ipairs(units) do
		if unit.id == id then return unit.value end
	end
	return unitNotFound
end

function p.getTeachableUnitById(unitID)
	--CapitalizeName
	for _, unit in ipairs(unitMapper) do
		for _, id in ipairs(unit.id) do
			if id == unitID then
				return utils.CapitalizeName(unit.generic)
			end
		end
	end
	for _, unit in ipairs(units) do
		if unit.id == unitID then
			return utils.CapitalizeName(unit.value)
		end
	end
end

--Function that actually returns string that will replace the placeholder
function pl(id, tripplet, mode)
	tripplet = tonumber(tripplet)
	for _,perk in ipairs(perks) do
		if perk.id == id then
			if (mode and mode == "teachable") then
				return utils.clr(b(strings.several .. space .. p.getTeachableUnitById(perk.units[tripplet])), 13)
			else
				local index = 1 + ((tripplet - 1) * 3) --tripplet is an offset, +1 is indexing from 1 in LUA, * 3 is because every values are grouped by 3 values/tiers
				return "'''" .. utils.clr(perk.values[index], 2) .. "'''/'''" .. utils.clr(perk.values[index + 1], 3) .. "'''/'''" .. utils.clr(perk.values[index + 2], 4) .. " " ..
				--I remove the dynamic base level as long as the perks are not diversed anymore and all perks are uncommon/rare/very rare value
				--return "'''" .. utils.clr(perk.values[index], perk.baseLevel) .. "'''/'''" .. utils.clr(perk.values[index + 1], perk.baseLevel + 1) .. "'''/'''" .. utils.clr(perk.values[index + 2], perk.baseLevel + 2) .. " " ..
					getUnitById(perk.units[tripplet]) .. "'''" --Units, Tripplet is an index in 'units' list
			end
		end
	end
	return "'''" .. strings.perkValuesNotFound .. "'''"
end

function teachableEdit(perkDesc)
	if perkDesc.desc[1][1] ~= nil then
		perkDesc.desc[1][1] = removeIconLinks(perkDesc.desc[1][1], false)
		perkDesc.desc[1][1] = removeLinks(perkDesc.desc[1][1], false)
	end
	return perkDesc
end

--Function replaces "#pl(x)" string with tripplet value of according number in brackets
function subValues(perkDesc, mode)
	local regexString = "#pl%((%d)%)" -- looking and extracting number from "#pl(x)"
	if perkDesc.desc[1][1] ~= nil then
		for m in perkDesc.desc[1][1]:gmatch(regexString) do --TODO the first index shouldn't be hardcoded due to history log (if the description will be copied, then it won't be the first)
			local currentRegexString = "#pl%(" .. tonumber(m) .. "%)" --you need to replace ONLY CURRENT INDEX, otherwise you'll get replaced all #pl(x) at once when going through the first time (i.e. the first tripplet will replace all #pl() in text
			perkDesc.desc[1][1] = perkDesc.desc[1][1]:gsub(currentRegexString, pl(perkDesc.id, tonumber(m), mode))
			--mw.log(perkDesc.desc[1][1])
		end
	end
	--mw.log("PL: " .. pl)
	return perkDesc
end

function subNames(perkDesc)
	local regexString = "#pn"
	if perkDesc.desc[1][1] ~= nil then
		for m in perkDesc.desc[1][1]:gmatch(regexString) do --TODO the first index shouldn't be hardcoded due to history log (if the description will be copied, then it won't be the first)
			perkDesc.desc[1][1] = perkDesc.desc[1][1]:gsub(regexString, "''".. p.getPerkById(perkDesc.id).name .. "''")
		end
	end
	return perkDesc
end

function prependRetire(perkDesc)
	if perkDesc.desc[1][1] ~= nil then
		perkDesc.desc[1][1] = perkStrings.retiredPerk .. pg .. perkDesc.desc[1][1]
	end
	return perkDesc
end

function prependRawDesc(perkDesc)
	if perkDesc.desc[1][1] ~= nil then
		perkDesc.desc[1][1] = 
			'<div class = "switchArea"></div>' ..
			'<div class = "rawPerkDesc" style="display:none;">' .. dnl .. reparseRawDesc(mw.text.nowiki(perkDesc.desc[1][1])) .. dnl .. '</div>' ..
			'<div class = "formattedPerkDesc">' .. perkDesc.desc[1][1] .. '</div>'
	end
	return perkDesc
end

function removeIconLinks(text, raw)
	local regex
	if raw then
		regex = '( ?%&%#91;%&%#91;' .. cstr.file .. '.-%&%#93;%&%#93;)' --"( %[%[" .. cstr.file .. ".-%]%])" --&#91; &#93; --iconLinks/images
	else
		regex = '( ?%[%[' .. cstr.file .. '.-%]%])' --"( [[" .. cstr.file .. ".-]])" --iconLinks/images
	end
	for m in text:gmatch(regex) do
		text = text:gsub(regex, cstr.empty)--cstr.empty)
	end
	return text
end

function removeLinks(text, raw)
	local regStart, regEnd
	if raw then
		regStart = '%&%#91;%&%#91;'
		regEnd = '%&%#93;%&%#93;'
	else
		regStart = '%[%['
		regEnd = '%]%]'
	end
	local regex = regStart .. '(.-)' .. regEnd --simple links
	for m in text:gmatch(regex) do
		local currentRegex = regStart .. m .. regEnd
		text = text:gsub(currentRegex, m)--cstr.empty)
	end
	return text
end

function reparseRawDesc(text)
	--mw.log(text)
	--mw.log("************************")
	
	--IconLink removal
	text = removeIconLinks(text, true)

	--[[Link]]
	text = removeLinks(text, true)
	
	--line carriage conversion to to <br>
	regex = '%&%#10;'
	if text:gmatch(regex) then
		text = text:gsub(regex, br)
	end

	--quotes:
	regex = '\n([^\n]-clr9[^\n]-&#39;)$'
	if text:gmatch(regex) then
		text = text:gsub(regex, cstr.empty)--cstr.empty)
	end	
	
	regex = '\n'
	if text:gmatch(regex) then
		text = text:gsub(regex, brnl)
	end
	
	--encoded <br>
	regex = '&#60;br&#62;'
	if text:gmatch(regex) then
		text = text:gsub(regex, br)
	end
	
	--last <br> removal
	regex = "<br>$"
	if text:gmatch(regex) then
		text = text:gsub(regex, cstr.empty)
	end
	
	--mw.log(text)
	return text
end

function p.getPerkById(id)
	for _, perk in ipairs(perks) do
		if perk.id == id then
			return perk
		end
	end
end

function p.getPerkByName(name)
	name = utils.resolveParameter(name)
	for _, perk in ipairs(perks) do
		if perk.name == name then
			return perk
		end
	end
end

function p.getPerksByNames(...)
	nameList = utils.resolveParameter(..., 0)
	perkList = {}
	for _, name in ipairs(nameList) do
		perkList[#perkList + 1] = p.getPerkByName(name)
	end
	return perkList
end

function p.getPerkDescription(perk, mode)
	perk = utils.resolveParameter(perk)
	for i, perkDesc in ipairs(perkDescription) do
		if perkDesc.id == perk.id then
			perkDesc = subValues(perkDesc, mode) --pl(#) => Perk # trio Values
			perkDesc = subNames(perkDesc) -- #pn => Perk Name
			if perk.retired then perkDesc = prependRetire(perkDesc) end
			if mode == "teachable" then perkDesc = teachableEdit(perkDesc) end
			perkDesc = prependRawDesc(perkDesc)
			
			--mw.log(perkDesc.desc[1][1])
			return perkDesc.desc[1][1]
		end
	end
	return strings.descNotFound .. ((perk and perk.name and colon .. space .. perk.name) or cstr.empty)
end

function p.getPerkTeachableDescription(id)
	id = tonumber(utils.resolveParameter(id))
	
	for _, perkDesc in ipairs(perkDescription) do
		if perkDesc.id == id then
			--mw.log(perkDesc.teachDesc[1])
			return perkDesc.teachDesc[1]
		end
	end
end

function p.getPerkDescriptionById(id)
	id = tonumber(utils.resolveParameter(id))
	return p.getPerkDescription(p.getPerkById(id))
end

function p.getPerkDescriptionByName(name)
	name = utils.resolveParameter(name)
	return p.getPerkDescription(p.getPerkByName(name))
end

function p.getTeachablePerkDescriptionByName(name)
	name = utils.resolveParameter(name)
	local perk = p.getPerkByName(name)
	local owner = p.getPerkOwnerByPerk(perk)
	mw.log(p.replaceCharacterTypeByOwner(owner, strings.unlockBloodweb) .. dnl .. p.getPerkDescription(perk, "teachable") .. dnl .. p.replaceOwnerRegexByOwner(owner, strings.unlockable) .. space .. bclr(6, strings.level .. space .. ((perk and perk.level) or cstr.empty)))
	return p.replaceCharacterTypeByOwner(owner, strings.unlockBloodweb) .. dnl .. p.getPerkDescription(perk, "teachable") .. dnl .. p.replaceOwnerRegexByOwner(owner, strings.unlockable) .. space .. bclr(6, strings.level .. space .. ((perk and perk.level) or cstr.empty))
end

function p.replaceCharacterTypeByOwner(owner, text)
	text = text or utils.resolveParameter(owner, 2)
	isKiller = utils.isCharacterKiller(owner)
	local regex = '#character#'
	for m in text:gmatch(regex) do
		text = text:gsub(regex, (isKiller and strings.killers) or strings.survivors)--cstr.empty)
	end
	return text
end

function p.replaceOwnerRegexByOwner(owner, text)
	text = text or utils.resolveParameter(owner, 2)
	local firstName = utils.getCharacterFirstName(owner)
	local regex = "#owner#"
	for m in text:gmatch(regex) do
		if owner.possessive == true then
			firstName = firstName .. "'" --if it's true then just add the apostrof to the end
		elseif owner.possessive then --it's not true nor false nor empty, hence it should be string (not checked)
			firstName = owner.possessive
		else
			firstName = firstName .. "'s" --if nothing then simply add 's after first name
		end
		text = text:gsub(regex, firstName)--cstr.empty)
	end
	return text
end

function p.getPerkOwnerByPerk(perk)
	if perk.character == nil then return notUniquePerk end
	if perk.charType == 'S' then
		return utils.getCharacterById(perk.character, survivors)
	elseif perk.charType == 'K' then
		return utils.getCharacterById(perk.character, killers)
	else
		return "Unknown Character"
	end
end

function p.getPerksOwnerNameByPerk(perk) --Name
	return p.getPerkOwnerByPerk(perk).name
end

function p.getPortraitOfPerkOwnerByPerk(perk)
	return perk.charType .. string.format("%02d", p.getPerkOwnerByPerk(perk).id) .. '_charSelect_portrait.png'
end

function getPerkOwnerFirstName(perk)
	local owner = p.getPerkOwnerByPerk(perk)
	if perk.charType == 'S' then
		if owner.shortName ~= nil then
			return utils.split(owner.shortName)[1]
		else
			return utils.split(owner.name)[1]
		end
	else --Killer name
		return owner.name
	end
end

--cat = category
--solution = defines which way the category should be processed
--charType = 'K' - Only Killers, 'S' - Only survivors, skipped - Both
function p.resolvePerkCategory(cat, solution, charType)
	category = utils.resolveParameter(cat)
	solution = solution or utils.resolveParameter(cat, 2)
	charType = charType or utils.resolveParameter(cat, 3, true)

	utils.sortItemsByName(perks)
	if		solution == "table"			then return getPerksTableByCategory(category, charType)
	elseif	solution == "charTable"		then return getPerksTableWithPortrait(category, charType)
	elseif	solution == "unusedTable"	then return getPerksTableUnused(category, charType)
	--elseif	solution == "secondApproach"	then return functionReturningCategorisedPerks(cat, charType)
	else return solution
	end
end

--*********** Perk Solution Functions ***********
function getPerksTableByCategory(cat, charType)
	local result = cstr.empty
	local perkList = getPerksByCategory(cat, charType)

	for _, perk in ipairs(perkList) do
		result = result .. getTableRowPerk(perk)
		if perk.id ~= perkList[#perkList].id then
			result = result .. ntl .. nl
		end
	end
	
	result = utils.wrapBasicTable(result)
	
	--mw.log(result)
	return result
end

function getPerksTableWithPortrait(cat, charType)
	local result = cstr.empty
	local perkList = getPerksByCategory(cat, charType)
	
	for _, perk in ipairs(perkList) do
		result = result .. getTableRowWithPortraitPerk(perk)
		if perk.id ~= perkList[#perkList].id then
			result = result .. ntl .. nl
		end
	end
	
	result = getPerkTableHeader({strings.icon, strings.name, strings.desc, strings.character}, nil, {false, true, false, true}) .. result
	result = utils.wrapBasicTable(result, "sortable")
	
	--mw.log(result)
	return result
end

function getPerksTableUnused(cat, charType)
	local result = cstr.empty
	local perkList = getUnusedPerks(charType)
	
	for _, perk in ipairs(perkList) do
		result = result .. getUnusedTableRow(perk)
		if perk.id ~= perkList[#perkList].id then
			result = result .. ntl .. nl
		end
	end
	
	result = getPerkTableHeader({strings.icon, strings.name, strings.desc, strings.belongs}, nil, {false, true, false, true}) .. result
	result = utils.wrapBasicTable(result)
	
	--mw.log(result)
	return result
end
--*********** END Perk Solution Functions ***********
--*********** Perk Row Functions ***********
function getTableRowPerk(perk)
	return
		hl .. '[[' .. cstr.file .. utils.getIcon(--[[perk.techName or --temporarily commented]]perk.name) .. '| ' .. perk.name .. ' | 100px]]' .. nl ..
		hl .. '[[' .. perk.name .. ']]' .. nl ..
		'| ' .. (p.getPerkDescription(perk) or '') .. nl
end

function getCharTableRowPerk(perk, ext) --using gif version of perk (character page)
	return
		hl .. '[[' .. cstr.file .. ((ext == 'png' and utils.getIcon(--[[perk.techName or --temporarily commented]] perk.name)) or p.getPerkIconFilenameByPerk(perk, ext or 'gif')) .. '| ' .. perk.name .. ' | 128px]]' .. nl ..
		hl .. '[[' .. perk.name .. ']]' .. nl ..
		'| ' .. (p.getPerkDescription(perk) or '') .. nl
end

function getTableRowWithPortraitPerk(perk)
	return
		hl .. '[[' .. cstr.file .. utils.getIcon(--[[perk.techName or --temporarily commented]] perk.name) .. '| ' .. perk.name .. ' | 100px]]' .. nl ..
		hl .. '[[' .. perk.name .. ']]' .. nl ..
		'| ' .. (p.getPerkDescription(perk) or '') .. nl ..
		hl .. ((perk.character and ( link(p.getPerkOwnerByPerk(perk).name .. tl .. getPerkOwnerFirstName(perk)) .. br .. link(cstr.file .. p.getPortraitOfPerkOwnerByPerk(perk) .. '|73px'))) or '<span style="display:none;">.</span>' .. strings.all) .. nl
end

function getUnusedTableRow(perk)
	return 
		getTableRowPerk(perk) ..
		hl .. ((perk.character and ( '[[' .. getPerkOwnerFirstName(perk) .. ']]' .. br .. '[[' .. cstr.file .. p.getPortraitOfPerkOwnerByPerk(perk) .. '|73px]]')) or
			(perk.charType and ((perk.charType == 'S' and utils.IconLink('Survivors')) or (perk.charType == 'K' and utils.IconLink('Killers')))) or cstr.empty) .. nl
end
--*********** END Perk Row Functions ***********
--*********** Get PerkList Functions ***********
function getPerksByCategory(cat, charType)
	local result = {}
	cat = utils.split(cat, ',')
	
	if cat[#cat] == "all" then 
		return getAllCharTypePerks(charType)
	end
	for _, perk in ipairs(perks) do
		if perk.tags ~= nil then
			local found = false
			for _,searchedCat in ipairs(cat) do
				for _, category in ipairs(perk.tags) do
					if category == searchedCat and (charType == nil or perk.charType == charType) then
						result[#result + 1] = perk
						found = true
						break
					end
				end
				if found then break end
			end
		end
	end

	return result
end

function getAllCharTypePerks(charType)
	local result = {}
	for _, perk in ipairs(perks) do
		if (charType == nil or perk.charType == charType) and not perk.unused then
			result[#result + 1] = perk
		end
	end
	return result
end

function getUnusedPerks(charType)
	result = {}
	for _, perk in ipairs(perks) do
		if (charType == nil or perk.charType == charType) and perk.unused then
			result[#result + 1] = perk
		end
	end
	return result
end
--*********** END Get PerkList Functions ***********

function p.getPerkDescriptions(params, last) --temporary function, not used anywhere permanently
	first = tonumber(utils.resolveParameter(params))
	last = tonumber(last or utils.resolveParameter(params, 2))
	local result = ''
	
	utils.sortTableById(perks)
	for _, perk in ipairs(perks) do
		if perk.id >= first and perk.id <= last then
			result = result .. hl .. perk.id .. nl .. 
			hl .. (perk.techName or perk.name) .. nl ..
			'| ' .. p.getPerkDescription(perk) .. nl
			if perk.id ~= last then
				result = result .. ntl .. nl
			end
		end
	end
	
	result = utils.wrapBasicTable(result)
	--mw.log(result)
	return result
end
--************************************
function getPerkTableHeader(colList, colors, sortings)
	result = ''
	
	for i, header in ipairs(colList) do
		result = result .. hl .. ((sortings and not sortings[i] and ' class = "unsortable" ' .. ' | ') or '') .. ((colors and utils.clr(header, colors[i])) or header) .. nl
	end
	result = result .. ntl .. nl
	return result
end

function p.getPerkIconFilenameByPerk(perk, ext)
	return p.getPerkIconFilename(nil, ext, perk)
end

function p.getPerkIconFilename(args, ext, perk)
	local name = (utils.resolveParameter(args, 1, true)) --should be (args, 1, true)?
	ext = (ext or utils.resolveParameter(args, 2, true))
	perk = (perk or p.getPerkByName(name))

	return (not perk and 'Teachable unknown.png') or utils.resolveFileName((perk.techName or name or perk.name), false, true) .. ((ext and  '.' .. ext) or name)
end

function p.getTeachablePerkIconFilename(args)
	return "Teachable_" ..	utils.FirstLetterLower(p.getPerkIconFilename(args))
end

function p.getTeachablePerkIcon(perk)
	return "Teachable_" ..	utils.FirstLetterLower(p.getPerkIconFilenameByPerk(perk, 'png'))
end

function p.getTeachableDescriptionById(id)
	return p.getPerkDescription(p.getPerkById(tonumber(utils.resolveParameter(id))), "teachable")
end

function p.getPerksByCharacter(character)
	result = {}
	charType = (character.power == nil and 'S') or 'K' --to determine whether the character is killer or survivor, since the power is unique attribute for killers and should be mandatory
	
	for _, perk in ipairs(perks) do
		if character.id == perk.character and charType == perk.charType then
			result[#result + 1] = perk
		end
	end
	return result
end

function p.resolveCharPerks(character, ext) --character can be character object as well (or string, or .args[1] = char name)
	ext = ext or utils.resolveParameter(character, 2, true)
	character = utils.resolveParameter(character)
	character = (character.name and character) or utils.getCharacterByName(character)
	if not character then return strings.charNotFound end
	local perkList = p.getPerksByCharacter(character)
	local result = cstr.empty
	
	utils.sortPerksByLevel(perkList)
	for _, perk in ipairs(perkList) do
		result = result .. getCharTableRowPerk(perk, ext)
		if perk.id ~= perkList[#perkList].id then
			result = result .. ntl .. nl
		end
	end
	
	result = utils.wrapBasicTable(result)
	
	--mw.log(result)
	return result
end

--charType - K/S
--[order] - #th character from DLC
function p.getCharPerksByDLC(charType, order, dlcName)
	local dlcs = require("Module:DLCs")
	local dlcName = (dlcName and dlcs.getShortName(dlcName)) or dlcs.getShortName(utils.getPageTitle())
	order = order or tonumber(utils.resolveParameter(charType, 2, true)) or 1
	charType = utils.resolveParameter(charType)
	local dlc = dlcs.getDlcByName(dlcName)
	if not dlc then return strings.dlcNotFound end
	local characters = utils.getCharsByDlc(dlc, charType)

	if #characters > 0 then
		return p.resolveCharPerks((characters[order] or characters[1]), "png")
	end
	return dlcName .. dot .. space .. strings.noChar
end

function p.getPerkCostISByName(name)
	local sos = require("Module:SoS")
	perk = p.getPerkByName(name)
	if perk ~= nil then
		return utils.commaFormat(sos.GetPerkCostByPerk(perk))
	else
		return 0
	end
end

function p.getPerksTableByNames(...)
	if type(...) ~= "table" then
		args = {...} 
	else
		args = ...
	end

	local perkList = p.getPerksByNames(args)
	result = cstr.empty
	
	for _, perk in ipairs(perkList) do
		result = result .. getTableRowPerk(perk)
		
		if perkList[#perkList].id ~= perk.id then
			result = result .. nl .. ntl .. nl
		end
	end
	result = utils.wrapBasicTable(result)

	return result
end

return p
Advertisement