Module:Loadout

From Dead by Daylight Wiki
Jump to navigation Jump to search
Template-info.svg Documentation
Flag Name Use Text Output
decom This Unlockable was decommissioned in the past and no longer exists in the current Game. THIS #loadout WAS DECOMMISSIONED (This Unlockable was decommissioned in the past and no longer exists in the current Game.)
deprecated This Unlockable can no longer be obtained, but stockpiles may still be used in Trials. THIS #loadout CAN NO LONGER BE OBTAINED FROM THE BLOODWEB (This Unlockable can no longer be obtained, but stockpiles may still be used in Trials.)
mobile This Unlockable is only available in the mobile build of Dead by Daylight. THIS #loadout IS ONLY AVAILABLE ON DBD MOBILE (This Unlockable is only available in the mobile build of Dead by Daylight.)
notAvailable This Unlockable can no longer be obtained and stockpiles can no longer be used.
If it is an Event Unlockable, it may return with its associated Event.
THIS #loadout IS NO LONGER AVAILABLE (This Unlockable can no longer be obtained and stockpiles can no longer be used.
If it is an Event Unlockable, it may return with its associated Event.
)
retired This Unlockable can no longer be obtained and stockpiles can no longer be used.
It will not return to the Game.
THIS #loadout WAS RETIRED (This Unlockable can no longer be obtained and stockpiles can no longer be used.
It will not return to the Game.
)
secret This Offering is secret and will not reveal its identity to the other Players during the burning sequence before loading into the Trial. THIS #loadout IS SECRET (This Offering is secret and will not reveal its identity to the other Players during the burning sequence before loading into the Trial.)
unused This Unlockable is not used in the current Game, but exists in the Game Code. THIS #loadout IS UNUSED (This Unlockable is not used in the current Game, but exists in the Game Code.)

local p = {}
local utils = require("Module:Utils")
local data = mw.loadData("Module:Datatable" .. utils.lang())
local str = require("Module:Strings")
local various = require("Module:Various")
local frame = mw.getCurrentFrame()
--local data = require("Module:Datatable")

local loadoutData
local loadoutTypeLower
local loadoutDescData
local loadoutTypeDisplay = cstr.empty
local loadoutTypeDisplayLower = cstr.empty
local loadoutTypeDisplayCapitalised = cstr.empty
local loadoutTypeDisplayGroup = cstr.empty
local loadoutType = cstr.empty
local loadoutLink = cstr.empty
local loadoutTable = {}
local loadoutDescTable = {}

p.loDescriptions = {
	decom = "This " .. i("Unlockable") .. " was decommissioned in the past and no longer exists in the current Game.",
	deprecated = "This " .. i("Unlockable") .. " can no longer be obtained, but stockpiles may still be used in Trials.",
	mobile = "This " .. i("Unlockable") .. " is only available in the mobile build of " .. i("Dead by Daylight") .. dot,
	notAvailable = "This " .. i("Unlockable") .. " can no longer be obtained and stockpiles can no longer be used." .. br .. "If it is an " .. i("Event Unlockable") .. ", it may return with its associated Event.",
	retired = "This " .. i("Unlockable") .. " can no longer be obtained and stockpiles can no longer be used." .. br .. " It will not return to the Game.",
	secret = "This " .. i("Offering") .. " is secret and will not reveal its identity to the other Players during the burning sequence before loading into the Trial.",
	unused = "This " .. i("Unlockable") .. " is not used in the current Game, but exists in the Game Code.",
	unknownDesc = "Unable to retrieve a description for this flag"
}
local loDescriptions = p.loDescriptions

p.strings = {
	ptbHeader = "This description is based on the changes announced for or featured in the upcoming Patch #patch#",
	wipHeader = "This description is currently a " .. i("Work in Progress") .. " or will be adjusted soon.",
	
	loadoutNotFound = "Unable to retrieve the #loadout or unable to display it. It can be added to the #loadoutLink. Otherwise " .. cstr.contact,
	loadoutDescNotFound = "Unable to retrieve the #loadout description or unable to display it. It can be added to the #loadoutLink. Otherwise " .. cstr.contact,
	unitNotFound = "Unable to retrieve the unit for this Perk's tiered values." .. cstr.contact,
	perkNotFound = "Unable to retrieve the Perk's information." .. cstr.contact,

	
	decom =             tooltip(bclr(16, "THIS #loadout WAS DECOMMISSIONED"), p.loDescriptions.decom, true, true),
	deprecated =        tooltip(bclr(5, "THIS #loadout CAN NO LONGER BE OBTAINED FROM THE BLOODWEB"), p.loDescriptions.deprecated, true, true),
	mobile =            tooltip(bclr(4, "THIS #loadout IS ONLY AVAILABLE ON DBD MOBILE"), p.loDescriptions.mobile, true, true),
	notAvailable =      tooltip(bclr(8, "THIS #loadout IS NO LONGER AVAILABLE"), p.loDescriptions.notAvailable, true, true),
	retired =           tooltip(bclr(13, "THIS #loadout WAS RETIRED"), p.loDescriptions.retired, true, true),
	secret =            tooltip(bclr(6, "THIS #loadout IS SECRET"), p.loDescriptions.secret, true, true),
	unused =            tooltip(bclr(16, "THIS #loadout IS UNUSED"), p.loDescriptions.unused, true, true),
	unknownDesc =       tooltip(bclr(16, "THE EFFECTS OF THIS #loadout ARE UNKNOWN"), p.loDescriptions.unknownDesc, true, true),
	
	--General Strings
	andString = "and", --Loadoout Page
	
	--Loadoout Page
	page_is = "is",
	page_aUnique = "a " .. bclr("orange", "Unique"),
	page_aGeneral = "a " .. b("General"),
	page_dbdMobile = '#' .. i(cstr.gameName) .. ' Mobile#',
	
	page_offering = "Offering",
	page_item = "Item",
	page_addon = "Add-on",
	page_perk = "Perk",
	page_skill = "Skill",
	
	page_belongingTo = "belonging to",
	page_availableToAll = "available to all",
	page_allPlayers = "all Players",
	page_andShared = "and shared",
	page_forItems = "for",
	
	page_killers = "Killers",
	page_survivors = "Survivors",
	
	page_mobile =       skip("It is only available on Mobile"),
	page_secret =       skip("It is secret"),
	page_deprecated =   skip("It is no longer available in the Bloodweb"),
	page_notAvailable = skip("It is no longer available"),
	page_retired =      skip("It has been retired"),
	--offeringHeaderTextOrder = "#1# #2#...",
	
	page_prestigeText1 = "to",
	page_prestigeText2 = "respectively to unlock",
	page_prestigeText3 = "of",
	page_prestigeText4 = "for all other Characters",
	page_tier = "Tier",
	page_perks = "Perks",
	page_uniquePerks = "Unique Perks", --article on Perks Page
	page_prestige = "Prestige",
	
	page_powerDefault = skip(link("#name")) .. " is the " .. link("Power") .. " of #owner.",
	
	--loadout table:
	class = "Class",
	icon = "Icon",
	name = "Name",
	desc = "Description",
	cost = "Cost",
	character = "Character",
	slot = "Slot",
	all = "All", --used for general perks
	
	bloodpoints = "Bloodpoints",
	unknownCategory = "Unknown Category",
	
--	statusEffect = "Status Effect",
--	statusEffects = "Status Effects"
}
local strings = p.strings
-- singular forms / mapping table
-- [loadoutTypeDisplay_NameForTable] = SINGULAR_FORM
p.loadoutStrings = {
	items = "ITEM",
	offerings = "OFFERING",
	addons = "ADD-ON",
	perks = "PERK",
	powers = "POWER",
	skills = "SKILL",
}
-- plural forms
-- used for categories
p.loadoutDisplayGroup = {
	items = "Items",
	offerings = "Offerings",
	addons = "Add-ons",
	perks = "Perks",
	powers = "Powers",
	skills = "Skills",
}
---------------------------------------------------------------------------------------------
--                                 DO NOT TRANSLATE / EDIT                                 --
---------------------------------------------------------------------------------------------
if utils.lang() ~= cstr.empty and (counter or 0) < 1 then
	counter = (counter or 0) + 1
	strings = require("Module:Loadout" .. utils.lang()).strings
end

local function loadoutUpperString() return string.upper(loadoutTypeDisplay) end
local function loadoutCapitalised() return utils.capitalizeName(loadoutTypeDisplay) end

--Table used for mapping which string should use which style
p.loadoutStringMapper = {
	loadoutNotFound = loadoutCapitalised,
	loadoutDescNotFound = loadoutCapitalised,
	
	unknownDesc = loadoutUpperString,
	retired = loadoutUpperString,
	mobile = loadoutUpperString,
	deprecated = loadoutUpperString,
	notAvailable = loadoutUpperString,
	unused = loadoutUpperString,
	decom = loadoutUpperString,
	secret = loadoutUpperString,
}

--rarities can be found in Module:Various

--Function setting global setting which loadout type is current processed
local function setLoadoutTable(loType)
	loadoutTypeDisplay = p.loadoutStrings[loType] -- ADD-ON
	loadoutTypeDisplayLower = string.lower(loadoutTypeDisplay) -- add-on
	loadoutTypeDisplayCapitalised = utils.firstLetterUpper(loadoutTypeDisplayLower) -- Add-on
	loadoutTypeDisplayGroup = p.loadoutDisplayGroup[loType] -- Add-ons
	loadoutType = loType -- addons
	loadoutTypeLower = string.replace(string.sub(loType, 1, -2), '-', cstr.empty) -- addon
	loadoutData = mw.loadData("Module:Datatable/Loadout" .. utils.lang())
	loadoutLink = link("Module:Datatable/Loadout/Descriptions", "Datatable/Loadout")
	loadoutTable = loadoutData[string.replace(loType:lower(), '-', cstr.empty)]
end

--This cannot be called before setLoadoutTable() function
local function loadDescriptionModule()
	loadoutDescData = mw.loadData("Module:Datatable/Loadout/Descriptions" .. utils.lang())
	loadoutDescTable = loadoutDescData[loadoutTypeLower .. "Descriptions"]
end

function p.getLoString(index)
	index = utils.resolveParameter(index, 1, true) or (index and strings[index] and index) or "unknownDesc"
	return strings[index]
end

function p.getLoDescription(index)
	index = utils.resolveParameter(index, 1, true) or (index and strings[index] and index) or "unknownDesc"
	return loDescriptions[index]
end

-------------------------------------------------------------------------
--                               Add-ons                               --
-------------------------------------------------------------------------
function p.getAddonsCount(cat, charType)
	setLoadoutTable("addons")
	return various.getCountElementsByCategory(cat, loadoutTable, charType)
end
function p.getAddonByName(name, withDesc)
	setLoadoutTable("addons")
	return getLoadoutByName(name, withDesc)
end
function p.getAddonDescription(name) --used directly from wiki
	return p.getAddonByName(name, true).desc
end
function p.getAddonTable(name)
	local item = p.getAddonByName(name, true)
	local tabParams = initialiseLoadoutTableParameters(name, tabParams)
	return getLoadoutTable(item, tabParams)
end
function p.getAddonsTable(names, params)
	setLoadoutTable("addons")
	local tabParams = initialiseLoadoutTableParameters(names, params)
	local loadoutList = getLoadoutByNames(names, true)
	return loadoutList and getLoadoutCategoryTable(loadoutList, tabParams) or false
end
function p.getAddonsByCategory(cat, charType)
	setLoadoutTable("addons")
	local tabParams = initialiseLoadoutTableParameters(cat)
	local loadoutList = getLoadoutByCategory(cat, charType)
	return loadoutList and getLoadoutCategoryTable(loadoutList, tabParams) or false
end
function p.resolveAddonPage(pageName, params)
	setLoadoutTable("addons")
	local tabParams = {displayHeader = true, displayCost = true, displayName = false}
	tabParams = initialiseLoadoutTableParameters(pageName, tabParams)
	local loadoutObj = getLoadoutByName(pageName, true)
	return resolveLoadoutPage(loadoutObj, tabParams)
end
function p.getAddonsByOwner(name)
	setLoadoutTable("addons")
	local tabParams = {displayHeader = true, assembleImage = true, displayName = true}
	local loadoutList = getLoadoutByOwner(name)
	return getLoadoutCategoryTable(loadoutList, tabParams)
end
function p.getAddonsByRarities(rarities, params)
	setLoadoutTable("addons")
	local tabParams = initialiseLoadoutTableParameters(rarities, params)
	local loadoutList = getLoadoutByRarities(rarities)
	return loadoutList and getLoadoutCategoryTable(loadoutList, tabParams) or false
end
function p.getAddonRarity(name)
	setLoadoutTable("addons")
	local loadoutObj = getLoadoutByName(name, true)
	return loadoutObj and loadoutObj.rarity or 0
end
function p.getAddonsHistory(names, params)
	setLoadoutTable("addons")
	local tabParams = initialiseLoadoutTableParameters(names, params) --should not have much of effect
	local loadoutList = getLoadoutByNames(names)
	return getLoadoutHistory(loadoutList, tabParams)
end
-------------------------------------------------------------------------
--                             Add-ons END                             --
-------------------------------------------------------------------------
-------------------------------------------------------------------------
--                                Items                                --
-------------------------------------------------------------------------
function p.getItemsCount(cat, charType)
	setLoadoutTable("items")
	return various.getCountElementsByCategory(cat, loadoutTable, charType)
end
function p.getItemByName(name, withDesc)
	setLoadoutTable("items")
	local result = getLoadoutByName(name, withDesc)
	return result
end

function p.getItemDescription(name) --used directly from wiki
	return p.getItemByName(name, true).desc
end
function p.getItemTable(name)
	local item = p.getItemByName(name, true)
	local tabParams = initialiseLoadoutTableParameters(name, tabParams)
	return getLoadoutTable(item, tabParams)
end
function p.getItemsTable(names, params)
	setLoadoutTable("items")
	local tabParams = initialiseLoadoutTableParameters(names, params)
	local loadoutList = getLoadoutByNames(names, true)
	return loadoutList and getLoadoutCategoryTable(loadoutList, tabParams) or false
end
function p.getItemsByCategory(cat, charType)
	setLoadoutTable("items")
	local tabParams = initialiseLoadoutTableParameters(cat, params)
	local loadoutList = getLoadoutByCategory(cat, charType)
	return loadoutList and getLoadoutCategoryTable(loadoutList, tabParams) or false
end
function p.resolveItemPage(pageName, params)
	setLoadoutTable("items")
	local tabParams = {displayHeader = true, displayCost = true, displayName = false}
	tabParams = initialiseLoadoutTableParameters(pageName, tabParams)
	local loadoutObj = getLoadoutByName(pageName, true)
	return resolveLoadoutPage(loadoutObj, tabParams)
end
function p.getItemsByOwner(name)
	setLoadoutTable("items")
	local tabParams = {displayHeader = true, assembleImage = true, displayName = true}
	local loadoutList = getLoadoutByOwner(name)
	return getLoadoutCategoryTable(loadoutList, tabParams)
end
function p.getItemsByRarities(rarities, params)
	setLoadoutTable("items")
	local tabParams = initialiseLoadoutTableParameters(rarities, params)
	local loadoutList = getLoadoutByRarities(rarities)
	return loadoutList and getLoadoutCategoryTable(loadoutList, tabParams) or false
end
function p.getItemsHistory(names, params)
	setLoadoutTable("items")
	local tabParams = initialiseLoadoutTableParameters(names, params) --should not have much of effect
	local loadoutList = getLoadoutByNames(names)
	return getLoadoutHistory(loadoutList, tabParams)
end
-------------------------------------------------------------------------
--                              Items END                              --
-------------------------------------------------------------------------
-------------------------------------------------------------------------
--                              Offerings                              --
-------------------------------------------------------------------------
function p.getOfferingsCount(cat, charType)
	setLoadoutTable("offerings")
	return various.getCountElementsByCategory(cat, loadoutTable, charType)
end
function p.getOfferingByName(name, withDesc)
	setLoadoutTable("offerings")
	return getLoadoutByName(name, withDesc)
end
function p.getOfferingDescription(name) --used directly from wiki
	return p.getOfferingByName(name, true).desc
end
function p.getOfferingTable(name)
	local item = p.getOfferingByName(name, true)
	local tabParams = initialiseLoadoutTableParameters(name, tabParams)
	return getLoadoutTable(item, tabParams)
end
function p.getOfferingsTable(names, params)
	setLoadoutTable("offerings")
	local tabParams = initialiseLoadoutTableParameters(names, params)
	local loadoutList = getLoadoutByNames(names, true)
	return loadoutList and getLoadoutCategoryTable(loadoutList, tabParams) or false
end
function p.getOfferingsByCategory(cat, charType)
	setLoadoutTable("offerings")
	local tabParams = initialiseLoadoutTableParameters(cat)
	local loadoutList = getLoadoutByCategory(cat, charType)
	return loadoutList and getLoadoutCategoryTable(loadoutList, tabParams) or false
end
function p.getOfferingsByRarities(rarities, params)
	setLoadoutTable("offerings")
	local tabParams = initialiseLoadoutTableParameters(rarities, params)
	local loadoutList = getLoadoutByRarities(rarities)
	return loadoutList and getLoadoutCategoryTable(loadoutList, tabParams) or false
end
function p.resolveOfferingPage(pageName)
	setLoadoutTable("offerings")
	local tabParams = {displayHeader = true, displayCost = true, displayName = false}
	tabParams = initialiseLoadoutTableParameters(pageName, tabParams)
	local loadoutObj = getLoadoutByName(pageName, true)
	return resolveLoadoutPage(loadoutObj, tabParams)
end
function p.resolveOfferingsByRealm(pageName)
	setLoadoutTable("offerings")
	local tabParams = initialiseLoadoutTableParameters(rarities, params)
	local loadoutList = getLoadoutByRealm(pageName, true)
	return loadoutList and getLoadoutCategoryTable(loadoutList, tabParams) or false
end
function p.getOfferingsHistory(names, params)
	setLoadoutTable("offerings")
	local tabParams = initialiseLoadoutTableParameters(names, params) --should not have much of effect
	local loadoutList = getLoadoutByNames(names)
	return getLoadoutHistory(loadoutList, tabParams)
end
-------------------------------------------------------------------------
--                            Offerings END                            --
-------------------------------------------------------------------------
-------------------------------------------------------------------------
--                                Perks                                --
-------------------------------------------------------------------------
function p.getPerksCount(cat, charType)
	setLoadoutTable("perks")
	return various.getCountElementsByCategory(cat, loadoutTable, charType)
end
function p.getPerkByName(name, withDesc)
	setLoadoutTable("perks")
	return getLoadoutByName(name, withDesc)
end
function p.getPerkDescription(name) --used directly from wiki
	return p.getPerkByName(name, true).desc
end
function p.getPerkTable(name)
	local item = p.getPerkByName(name, true)
	return getLoadoutTable(item)
end
function p.getPerksTable(names, params)
	setLoadoutTable("perks")
	local tabParams = initialiseLoadoutTableParameters(names, params)
	local loadoutList = getLoadoutByNames(names)
	return loadoutList and getLoadoutCategoryTable(loadoutList, tabParams) or false
end
function p.getPerksByCategory(cat, charType)
	setLoadoutTable("perks")
	local tabParams = {assembleImage = false}
	tabParams = initialiseLoadoutTableParameters(cat, tabParams)
	local loadoutList = getLoadoutByCategory(cat, charType)
	return loadoutList and getLoadoutCategoryTable(loadoutList, tabParams) or false
end
function p.resolvePerkPage(pageName, params)
	setLoadoutTable("perks")
	local tabParams = initialiseLoadoutTableParameters(names, params)
	local loadoutObj = getLoadoutByName(pageName, true)
	return resolveLoadoutPage(loadoutObj, tabParams)
end
function p.getPerkPageTable(perkName)
	setLoadoutTable("perks")
	local loadoutObj = (type(perkName) == types.table and not perkName.args and perkName) or getLoadoutByName(perkName, true)
	return getPerkPageTable(loadoutObj)
end 
function p.getPerksByOwner(name, params)
	setLoadoutTable("perks")
	local tabParams = initialiseLoadoutTableParameters(name, params)
	local loadoutList = getLoadoutByOwner(name)
	return getLoadoutCategoryTable(loadoutList, tabParams)
end
function p.getCharPerksByDLC(charType, order, dlcName)
	setLoadoutTable("perks")
	dlcName = dlcName or utils.resolveParameter(charType, 3)
	order = order or tonumber(utils.resolveParameter(charType, 2, true)) or 1
	charType = utils.resolveParameter(charType, 1)
	local tabParams = initialiseLoadoutTableParameters(charType, params)
	local loadoutList = getCharLoadoutByDLC(charType, order, dlcName)
	return loadoutList and getLoadoutCategoryTable(loadoutList, tabParams) or false
end
function p.getPerksHistory(names, params)
	setLoadoutTable("perks")
	local tabParams = initialiseLoadoutTableParameters(names, params) --should not have much of effect
	local loadoutList = getLoadoutByNames(names)
	return getLoadoutHistory(loadoutList, tabParams)
end
-------------------------------------------------------------------------
--                              Perks END                              --
-------------------------------------------------------------------------
-------------------------------------------------------------------------
--                               Powers                                --
-------------------------------------------------------------------------
function p.getPowerByName(name, withDesc)
	setLoadoutTable("powers")
	return getLoadoutByName(name, withDesc)
end
function p.getPowerDescription(name) --used directly from wiki
	return p.getPowerByName(name, true).desc
end
function p.getPowersByCategory(cat, charType)
	setLoadoutTable("powers")
	local tabParams = {assembleImage = false}
	tabParams = initialiseLoadoutTableParameters(cat, tabParams)
	local loadoutList = getLoadoutByCategory(cat, charType)
	return loadoutList and getLoadoutCategoryTable(loadoutList, tabParams) or false
end
function p.getPowerByOwner(name, params)
	setLoadoutTable("powers")
	local tabParams = initialiseLoadoutTableParameters(name, params)
	local loadoutList = getLoadoutByOwner(name)
	return (loadoutList and p.getPowerDescription(loadoutList[1].name, params)) or false
end
function p.resolvePowerPage(pageName, params)
	setLoadoutTable("powers")
	local tabParams = initialiseLoadoutTableParameters(names, params)
	local loadoutObj = getLoadoutByName(pageName, true)
	return resolveLoadoutPage(loadoutObj, tabParams)
end
function p.getPowersHistory(names, params)
	setLoadoutTable("powers")
	local tabParams = initialiseLoadoutTableParameters(names, params) --should not have much of effect
	local loadoutList = getLoadoutByNames(names)
	return getLoadoutHistory(loadoutList, tabParams)
end

-------------------------------------------------------------------------
--                             Powers END                              --
-------------------------------------------------------------------------
-------------------------------------------------------------------------
--                               Skills                                --
-------------------------------------------------------------------------
function p.getSkillsCount(cat, charType)
	setLoadoutTable("skills")
	return various.getCountElementsByCategory(cat, loadoutTable, charType)
end
function p.getSkillByName(name, withDesc)
	setLoadoutTable("skills")
	return getLoadoutByName(name, withDesc)
end
function p.getSkillDescription(name)
	return p.getSkillByName(name, true).desc
end
function p.getSkillTable(name, params)
	local tabParams = {displayHeader = true, displayCost = false, displaySlot = true}
	tabParams = initialiseLoadoutTableParameters(name, tabParams)
	local item = p.getSkillByName(name, true)
	return getLoadoutTable(item, tabParams)
end

function p.getSkillsByCategory(cat, charType)
	setLoadoutTable("skills")
	local tabParams = {displaySlot = true, displayClass = true, disableNameLink = true}
	tabParams = initialiseLoadoutTableParameters(cat, tabParams)
	local loadoutList = getLoadoutByCategory(cat, charType)
	--utils.sortBySlot(loadoutList)
	utils.sortByClassAndSlot(loadoutList)
	return loadoutList and getLoadoutCategoryTable(loadoutList, tabParams) or false
end
function p.resolveSkillPage(pageName, params)
	setLoadoutTable("skills")
	local tabParams = {displayHeader = true, displayName = false, displayCost = false, displaySlot = true}
	tabParams = initialiseLoadoutTableParameters(pageName, tabParams)
	local loadoutObj = getLoadoutByName(pageName, true)
	return resolveLoadoutPage(loadoutObj, tabParams)
end
function p.getSkillByOwner(name, params)
	setLoadoutTable("skills")
	local tabParams = initialiseLoadoutTableParameters(name, params)
	local loadoutList = getLoadoutByOwner(name)
	return (loadoutList and p.getPowerDescription(loadoutList[1].name, params)) or false
end
-------------------------------------------------------------------------
--                             Skills END                              --
-------------------------------------------------------------------------
--------------------------------------------------------------------------
--                             LUA OBJECTS                              --
--------------------------------------------------------------------------

function p.getPerkObjectsByOwner(name)
	setLoadoutTable("perks")
	return getLoadoutByOwner({args={name = name, disableRestrictions = true}})
end
function p.getPowerObjectByOwner(killerParam)
	setLoadoutTable("powers")
	local killersLogic = require("Module:Killers" .. utils.lang())
	return (tonumber(killerParam) and killersLogic.getKillerById(killerParam) or tonumber(killerParam)) or killersLogic.getKillerByName(killerParam)
end

--------------------------------------------------------------------------
--                           LUA OBJECTS END                            --
--------------------------------------------------------------------------

function initialiseLoadoutTableParameters(directParams, flagParams)
	local flags =
	{
		assembleImage = true,
		displayHeader = false,
		displayName = true,
		displayCost = false,
		displaySlot = false,
		displayClass = false,
		displayPortrait = false,
		disableNameLink = false,
	}
	for fName, flag in pairs(flagParams or {}) do
		flags[fName] = flag
	end
	for fName, flag in pairs(flags) do
		local directFlagParam = utils.bool(utils.resolveParameter(directParams, fName, true))
		if directFlagParam ~= nil then
			flags[fName] = directFlagParam --in order to rewrite the field that is looping thorugh it needs to be change via index
		end
	end
	return flags
end

--Returns whole object
--THIS FUNCTION MUST BE CALLED BY OTHER FUNCTION SETTING loadoutTable VARIABLE!
function getLoadoutByName(name, withDesc)
	withDesc = withDesc or utils.resolveParameter(name, 2, true)
	name = utils.resolveParameter(name)
	local loadoutObj = (loadoutTable[name] and table.copy(loadoutTable[name])) or false
	if not loadoutObj then return false end
	loadoutObj.name = loadoutObj.name or name
	
	if withDesc and loadoutObj then
		loadDescriptionModule()

		if loadoutDescTable[name] then --there is ["Name"] index for the loadout
			loadoutObj.desc = loadoutDescTable[name].desc
		end
		if not loadoutObj.desc then
			loadoutObj.desc = strings.loadoutDescNotFound
			loadoutObj.descNotFound = true
		end
		
		postprocessDescription(loadoutObj)
	end
	
	return loadoutObj
end

--used for specific list of loadout elements
--THIS FUNCTION MUST BE CALLED BY OTHER FUNCTION SETTING loadoutTable VARIABLE!
function getLoadoutByNames(names)
	names = utils.resolveParameter(names, 1)
	local searchedLoadout = various.getElementsByNames(names, loadoutTable)
	
	utils.sortCosmeticsByRarityAndName(searchedLoadout)
	return ((searchedLoadout and not table.empty(searchedLoadout)) and searchedLoadout) or false
end

--THIS FUNCTION MUST BE CALLED BY OTHER FUNCTION SETTING loadoutTable VARIABLE!
function getLoadoutByRarities(rarities)
	rarities = utils.resolveParameter(rarities, 1)
	local searchedLoadout = various.getElementsByRarities(rarities, loadoutTable)
	
	utils.sortItemsByName(searchedLoadout)
	return ((searchedLoadout and not table.empty(searchedLoadout)) and searchedLoadout) or false
end

function getLoadoutByRealm(realm)
	realm = utils.resolveParameter(realm, 1)
	local mapsLogic = require("Module:Maps")
	realmObj = mapsLogic.getRealmByName(realm)
	realmTag = string.replace(string.replace((realmObj.techName or realmObj.name), space, cstr.empty), "'", cstr.empty)
	searchedLoadout = {}
	
	for loName, loadout in pairs(loadoutTable) do
		if utils.isTagPresent(loadout, realmTag) then
			local lo = table.copy(loadout)
			lo.name = lo.name or loName
			table.add(searchedLoadout, lo)
		end
	end
	
	utils.sortCosmeticsByRarityAndName(searchedLoadout)
	return ((searchedLoadout and not table.empty(searchedLoadout)) and searchedLoadout) or false
end

function getLoadoutDescription(loadout)
	return p["get" .. utils.FirstLetterUpper(loadoutTypeLower) .. "Description"](loadout.name) or strings.loadoutDescNotFound
end

function postprocessDescription(loadout)
	--can be thrownchanged to loop, with flags = {"deprecated", "mobile", "notAvailable", "retired"}
	if loadout.descNotFound or loadout.notFound then 
		loadout.desc = string.replace(loadout.desc, "#loadoutLink", loadoutLink)
		loadout.desc = string.replace(loadout.desc, "#loadout", p.loadoutStringMapper.loadoutDescNotFound())
	end
	if loadout.unknownDesc then loadout.desc = string.replace(strings.unknownDesc, "#loadout", p.loadoutStringMapper.unknownDesc()) .. dnl .. loadout.desc end
	if loadout.deprecated then loadout.desc = string.replace(strings.deprecated, "#loadout", p.loadoutStringMapper.deprecated()) .. dnl .. loadout.desc end --currently replace feature is not used in deprecated string
	if loadout.mobile then loadout.desc = string.replace(strings.mobile, "#loadout", p.loadoutStringMapper.mobile()) .. dnl .. loadout.desc end
	if loadout.notAvailable then loadout.desc = string.replace(strings.notAvailable, "#loadout", p.loadoutStringMapper.notAvailable()) .. dnl .. loadout.desc end
	if loadout.retired then loadout.desc = string.replace(strings.retired, "#loadout", p.loadoutStringMapper.retired()) .. dnl .. loadout.desc end
	if loadout.unused or loadout.unusedVisible then loadout.desc = string.replace(strings.unused, "#loadout", p.loadoutStringMapper.unused()) .. dnl .. loadout.desc end
	if loadout.decom then loadout.desc = string.replace(strings.decom, "#loadout", p.loadoutStringMapper.decom()) .. dnl .. loadout.desc end
	if loadout.secret then loadout.desc = string.replace(strings.secret, "#loadout", p.loadoutStringMapper.secret()) .. dnl .. loadout.desc end

	loadout = processTagsDescriptionAutomation(loadout)
	
	subNames(loadout)
	subValues(loadout)
	processElementValues(loadout)
	if loadout.wip then --wip override ptb, having bigger urgency
		wrapWipBox(loadout)
	else
		wrapPtb(loadout) --ptb should not appear if wip is true
	end
end

--This must be loaded only from this modile, when the loTypes are set
function postprocessText(text)
	return string.replace(text, "#loadout", loadoutTypeDisplayLower)
end

function processTagsDescriptionAutomation(loadout)
	if loadout.deprecated or loadout.notAvailable or (loadout.killer and type(loadout.killer) == types.number and loadout.killer > 0) or (loadoutType == "items" and table.contains({11, 12}, loadout.rarity) --[[limited]]) then
		return loadout
	end
	
	if loadout.tags then
		for _, tag in ipairs(loadout.tags) do
			if table.contains(various.eventTypes, tag) then
				loadout.eventItem = true --not necesarry but for clarity why not
				loadout.eventType = tag
				break
			end
		end
	end
	
	if not loadout.eventItem then
		return loadout
	end

	local eventList = various.getLiveEventsList()
	for _, event in ipairs(eventList) do
		if event.type == "event" and event.tags and utils.isTagPresent(event, loadout.eventType) then
			return loadout
		end
	end
	
	if		loadoutType == "items" then 
		loadout.desc = string.replace(strings.deprecated, "#loadout", p.loadoutStringMapper.deprecated()) .. dnl .. loadout.desc
	elseif	loadoutType == "addons" or loadoutType == "offerings" then
		loadout.desc = string.replace(strings.notAvailable, "#loadout", p.loadoutStringMapper.notAvailable()) .. dnl .. loadout.desc
	end
		
	return loadout
end

--used on dedicated #loadout page
function getLoadoutTable(loadoutObj, tabParams)
	tabParams.displayCost = (loadoutType == "perks" and not table.contains({11, 12}, loadoutObj.rarity)) or (loadoutType ~= "perks" and tabParams.displayCost) --11 = Limited, 12 = Limited & Craftable
	return getLoadoutCategoryTable({loadoutObj}, tabParams)
	--return result
end

function getPerkPageTable(perk)
	local perksModule = require("Module:Perks" .. utils.lang())
	if perk == nil then return strings.perkNotFound end
	perk.desc = perk.desc or p.getPerkDescription(perk.name)
	local bpClasses1 = 'BG-All PerkCostsBG'
	local bpClasses2 = 'borderless'
	local bpClasses3 = 'SquareBG-#-enh'
	local perkIconFilename = perksModule.getPerkIconFilenameByPerk(perk, cstr.gif, true)
	--if file exist then no link, otherwise make link for link to upload
	local perkIcon = utils.assembleImage("perk", perk.name, 128, {rarity = perk.rarity or 2})
	local bpIcon = file('IconHelp bloodpoints' .. dot .. cstr.png, '56px', 'link=' .. strings.bloodpoints)
	local firstLevel = 2 --based on indexes in perkBPCosts list
	local lastLevel = 4
	

	-------------------------------- PC --------------------------------
	local pcView = 
		'<div class="perkWrapper displayFlex overflowScroll">' .. nl .. --style = "display: none"
			'<div class = "perkTable divTable displayFlex flexColumn">' .. nl ..
				'<div class = "perkInfo displayFlex">' .. nl ..
					'<div class = "perkIcon displayFlex flexColumn flexGrow">' .. nl ..
						'<div class= "perkIconHeader divTableHeader">' .. strings.icon .. '</div>' .. nl ..
						'<div class = "perkIconImage displayFlex flexCenter divTableCell">' .. perkIcon .. '</div>' .. nl ..
					'</div>' .. nl ..
					'<div class = "perkCost displayFlex flexColumn">' .. nl ..
						'<div class= "perkCostHeader divTableHeader">' .. strings.cost .. '</div>' .. nl ..
						'<div class = "perkIconImage displayFlex">' .. nl
							for i = firstLevel, lastLevel, 1 do
								pcView = pcView ..
									div(utils.formatNum(perksModule.perkBPCosts[i], 0) .. br .. bpIcon, string.gsub("perkCost#", "#", i-1), "displayFlex", "flexColumn", "flexCenter", "BG-All", "PerkCostsBG", "borderless", string.gsub(bpClasses3, "#", i)) .. nl
									--tl .. class(bpClasses1, bpClasses2, string.gsub(bpClasses3, "#", i)) .. tl .. utils.formatNum(perksModule.perkBPCosts[i], 0) .. br .. bpIcon .. nl
							end
							pcView = pcView ..
						'</div>' .. nl ..
					'</div>' .. nl ..
				'</div>' .. nl ..
				'<div class = "perkDescHeader divTableHeader">Description</div>' .. nl ..
				'<div class = "perkDesc divTableCell">' .. perk.desc .. '</div>' .. nl ..
			'</div>' .. nl ..
		'</div>' .. nl

	
	-------------------------------- Mobile --------------------------------
	--temporarily disabled
	local mobileView = cstr.empty
	--[[
		hl .. 'width = "128px"' .. tl .. strings.icon .. nl ..
		hl .. colspan(1) .. tl .. strings.cost .. nl .. ntl .. nl ..
		tl .. rowspan(3) .. tl .. center(perkIcon) .. nl
	for i = firstLevel, lastLevel, 1 do
		mobileView = mobileView ..
			tl .. center(bclr(i, utils.formatNum(perksModule.perkBPCosts[i], 0)) .. utils.IconLink(ils.bloodpoints, 'img')) .. nl .. ntl .. nl
	end
	mobileView = mobileView ..
		hl .. colspan(2) .. tl .. strings.desc .. nl .. ntl .. nl ..
		tl .. colspan(2) .. tl .. perk.desc .. nl
		
	mobileView = utils.wrapBasicTable(mobileView, 'mobileView')
	]]
	
	local result = pcView .. dnl .. mobileView
	
	--mw.log(result)
	return result
end

function resolvePerkPrestige(perk)
	if not perk then return strings.perkNotFound .. space .. brackets(strings.name .. colon .. space .. quotes(perk.name or cstr.empty)) end
	
	if not perk.character then
		return cstr.empty
	end
	
	local owner = getLoadoutOwner(perk)
	local isKiller = utils.isKiller(owner)
	
	return
		utils.getDynamicString(
			{
				skip(link(strings.page_perks .. '#' .. strings.page_uniquePerks, strings.page_prestige)) .. space .. skip(((isKiller and the(owner)) or cstr.empty) .. ((not isKiller and owner.shortName) or owner.name)) .. space .. strings.page_prestigeText1 .. space .. b(strings.page_prestige .. " 1, 2, 3") .. space .. 
				strings.page_prestigeText2 .. space .. b(clr(2, strings.page_tier .. space .. 'I') .. comma .. clr(3, strings.page_tier .. space .. 'II') .. comma .. clr(4, strings.page_tier .. space .. 'III')) .. space .. 
				strings.page_prestigeText3 .. space .. b(perk.name) .. space .. strings.page_prestigeText4 .. dot
			},
			strings.prestigeOrder
		)
	-- [[Perks#Unique Perks|Prestige]] the associated Character to '''Prestige 1, 2, 3''' respectively to unlock '''{{clr|2|Tier I}}, {{clr|3|Tier II}}, {{clr|4|Tier III}}''' of '''{{PAGENAME}}''' for all other Characters.
end

--THIS FUNCTION MUST BE CALLED BY OTHER FUNCTION SETTING loadoutTable VARIABLE!
function getLoadoutByCategory(cat, charType)
	charType = charType or utils.resolveParameter(cat, 2, true)
	cat = utils.resolveParameter(cat, 1)
	local searchedLoadout = various.getElementsByCategory(cat, loadoutTable, charType)
	
	utils.sortCosmeticsByRarityAndName(searchedLoadout)
	return ((searchedLoadout and not table.empty(searchedLoadout)) and searchedLoadout) or false
end

--THIS FUNCTION MUST BE CALLED BY OTHER FUNCTION SETTING loadoutTable VARIABLE!
function getLoadoutByOwner(params)
	local itemsCategoryParam = tonumber(utils.resolveParameter(params, "itemsCategory", true)) or utils.resolveParameter(params, "itemsCategory")
	local killerParam = tonumber(utils.resolveParameter(params, "killer", true)) or utils.resolveParameter(params, "killer")
	local categoryParam = tonumber(utils.resolveParameter(params, "category", true)) or utils.resolveParameter(params, "category")
	local decomParam = utils.bool(utils.resolveParameter(params, "decom", true))
	local unusedParam = utils.bool(utils.resolveParameter(params, "unused", true))
	local disableRestrictions = utils.bool(utils.resolveParameter(params, "disableRestrictions", true))

	local searchedLoadout = {}
	local category, itemsCategory
	
	local killersLogic = require("Module:Killers" .. utils.lang())
	local killer = (tonumber(killerParam) and killersLogic.getKillerById(killerParam) or tonumber(killerParam)) or killersLogic.getKillerByName(killerParam)

	local character = various.getCharacterByName(utils.resolveParameter(params, "name", true) or utils.resolveParameter(params, 1, true) or (type(params) == types.string and params) or utils.getPageTitle())

	for i, cat in pairs(loadoutData.categories or {}) do
		if itemsCategoryParam == cat then
			itemsCategory = i
			break
		end
		if categoryParam == cat then
			category = i
			break
		end
	end

	for loName, loadout in pairs(loadoutTable) do
		if	(	(loadout.itemsCategory == itemsCategoryParam or (itemsCategory and loadout.itemsCategory == itemsCategory)) 
			or	(type(killer) == types.table and (loadout.killer == killer.id or (type(loadout.killer) == types.table and table.contains(loadout.killer, killer.id))) or (type(killer) == types.number and loadout.killer == killer))
			or	(loadout.character and character and loadout.character == character.id and ((character.isKiller and loadout.charType == 'K') or (not character.isKiller and loadout.charType == 'S')))
			or	(loadout.category == categoryParam or (category and loadout.category == category))
			)
			and
			(
				(
					((decomParam and loadout.decom) or (not decomParam and not loadout.decom))
				and ((unusedParam and loadout.unused) or (not unusedParam and not loadout.unused)) 
				)
				or disableRestrictions
			)
		then
			local lo = table.copy(loadout)
			lo.name = lo.name or loName
			table.add(searchedLoadout, lo)
		end
	end

	utils.sortCosmeticsByRarityAndName(searchedLoadout)
	return ((searchedLoadout and not table.empty(searchedLoadout)) and searchedLoadout) or false
end

function getLoadoutCategoryTable(catTable, tabParams)
	local various = require("Module:Various")
	local result = cstr.empty
	if catTable and not table.empty(catTable) then
		local classElementAmounts
		if loadoutType == "skills" then
			classElementAmounts = getClassElAmounts(catTable)
		end
		local classCounter = 0 --for class tables so it can be grouped
		for loName, lo in pairs(catTable) do
			local loadout = table.copy(lo)
			loadout.name = loadout.name or loName
			loadout.finalDisplayName = (tabParams.disableNameLink and loadout.displayName or link(loadout.name, loadout.displayName)) --if tabParams.displayName == nill then the link() function will behave as no parameter was passed
			if loadout.realName then
				loadout.finalDisplayName = utils.tooltip(loadout.finalDisplayName, loadout.realName, true)
			end
			loadout.character = getLoadoutOwner(loadout)
			if classElementAmounts and classCounter == classElementAmounts[lo.class] then classCounter = 0 end
			
			result = result ..
				ntl .. ((loadout.decom and class("decom", "decom-" .. loadoutTypeLower)) or ((loadout.unused or loadout.unusedVisible) and class("unused", "unused-" .. loadoutTypeLower)) or cstr.empty) .. nl ..
				((tabParams.displayClass and classCounter == 0 and hl .. "rowspan = " .. classElementAmounts[lo.class] .. tl .. utils.assembleImage("class", loadoutData.classes[loadout.class]) .. nl) or cstr.empty) ..
				hl .. ((tabParams.assembleImage and (utils.assembleImage(loadoutTypeLower, loadout.name, nil, {rarity = loadout.rarity or (loadoutType == "perks" and 2) or 1}))) or file(utils.getIcon(loadout.name), loadout.name, "96px")) .. nl ..
				((tabParams.displayName and hl .. loadout.finalDisplayName .. nl) or cstr.empty) ..
				tl .. (loadout.desc or getLoadoutDescription(loadout)) .. nl .. --this should be optimised as this cause searching through the offering table twice (first is the off object here and second search is inside getOfferingDescription function)
				((tabParams.displayCost and tl .. class("BG-All", "BPBG-All", "BPBG") .. tl .. utils.clr(utils.resolveTextColorByBackground(utils.clr("Bright Red")), various.getElementCost(loadout)) .. nl) or cstr.empty) ..
				((tabParams.displaySlot and loadout.slot and hl .. loadout.slot .. nl) or cstr.empty) ..
				((tabParams.displayPortrait and 
					hl .. ((loadout.character and (link(utils.resolveCharacterIconLinkName(loadout.character), various.getCharacterFirstName(loadout.character)) .. br .. 
					various.getCharPortrait(loadout.character, 150, false)
					)) or span(dot, 'display-none') .. strings.all) .. nl) or cstr.empty)
			classCounter = classCounter + 1
		end
		if tabParams.displayHeader then
			result = ntl .. nl .. 
			((tabParams.displayClass and hl .. strings.class .. hl) or cstr.empty) .. 
			hl .. strings.icon .. 
			((tabParams.displayName and dhl .. strings.name) or cstr.empty) .. 
			dhl .. strings.desc .. 
			((tabParams.displayCost and dhl .. strings.cost) or cstr.empty) ..
			((tabParams.displaySlot and dhl .. strings.slot) or cstr.empty) ..
			((tabParams.displayPortrait and dhl .. strings.character) or cstr.empty) ..
				nl .. result
		end
		return utils.wrapBasicTable(result, "overflowScroll" .. ((tabParams.displayPortrait and space .. "sortable") or cstr.empty), nil, true)
	else
		result = {notFound = true, desc = strings.loadoutNotFound}
		postprocessDescription(result)
		return utils.wrapBasicTable(tl .. result.desc .. nl, "overflowScroll", nil, true)
	end
	
end

function getClassElAmounts(elTable)
	local result = {}
	for _, el in ipairs(elTable) do
		if not result[el.class] then
			result[el.class] = 1
		else
			result[el.class] = result[el.class] + 1
		end
	end
	return result
end

function getPortraitOfLoadoutOwner(loadoutObj)
	return loadoutObj.charType .. string.format("%02d", loadoutObj.character and loadoutObj.character.id or getLoadoutOwner(loadoutObj).id) .. '_charSelect_portrait.png'
end

function resolveLoadoutPage(loadoutObj, tabParams)
	local result = cstr.empty
	
	if loadoutObj then
		local various = require("Module:Various")
		local langs = require("Module:Languages" .. utils.lang())
		local isSecret = p.isLoadoutSecret(loadoutObj)
		loadoutObj.isUnique = loadoutObj.character or loadoutObj.survivor or loadoutObj.killer or false
		local loCharType = various.getCharTypeWord(loadoutObj, true)
		local rarity = false
		local rarityClr = false 
		local rarityName = cstr.empty
		
		if loadoutObj.rarity and various.rarity[loadoutObj.rarity] then
			rarity = various.rarity[loadoutObj.rarity]
			rarityClr = (rarity.clr == nil and loadoutObj.rarity) or rarity.clr or false
			rarityName = rarity.name or cstr.empty
		end
		
		local introduceLine
		if loadoutType == 'powers' then 
			loadoutObj.introductionLine = strings.page_powerDefault
			loadoutObj.killer = p.getPowerObjectByOwner(loadoutObj.killer)
			loadoutObj.owner = skip(the(loadoutObj.killer.name) .. utils.IconLink(loadoutObj.killer.name))
			introduceLine = processElementValues(loadoutObj, "introductionLine")
			postprocessDescription(loadoutObj)
		elseif loadoutType == 'perks' then
			lg("LANG: " .. langs.getLang() .. " | introductionLine_perks")
			if langs.getLang() == "fr" then
				--french version doesn't have indefinite articles at strings.page_aUnique and strings.page_aGeneral so they needs to be added accordingly
				--this is dues the article is bound to loadout type name (its gender) rather a rarity
				loadoutObj.introductionLine = 
					skip((loadoutObj.multiName and b(loadoutObj.displayName or loadoutObj.name) or link(loadoutObj.displayName or loadoutObj.name or "Error"))) .. space .. strings.page_is .. space ..
					((loadoutObj.character and strings.page_aUnique) or strings.page_aGeneral) .. space .. skip(article(strings["page_" .. loadoutTypeLower], true)) .. skip(utils.IconLink(strings["page_" .. loadoutTypeLower])) .. space ..
					getUniqueOwnerString(loadoutObj) ..
					word("dot") ..
					
					((loadoutObj.deprecated and strings.page_deprecated .. dot) or cstr.empty) ..
					((loadoutObj.notAvailable and strings.page_notAvailable .. dot) or cstr.empty) ..
					((loadoutObj.retired and strings.page_retired .. dot) or cstr.empty)
			else --en
				loadoutObj.introductionLine = 
					skip((loadoutObj.multiName and b(loadoutObj.displayName or loadoutObj.name) or link(loadoutObj.displayName or loadoutObj.name or "Error"))) .. space .. strings.page_is .. space ..
					((loadoutObj.character and strings.page_aUnique) or strings.page_aGeneral) .. space .. skip(utils.IconLink(strings["page_" .. loadoutTypeLower])) .. space ..
					getUniqueOwnerString(loadoutObj) ..
					word("dot") ..
					
					((loadoutObj.deprecated and strings.page_deprecated .. dot) or cstr.empty) ..
					((loadoutObj.notAvailable and strings.page_notAvailable .. dot) or cstr.empty) ..
					((loadoutObj.retired and strings.page_retired .. dot) or cstr.empty)
			end
		else
			if langs.getLang() == "fr" then
				loadoutObj.introductionLine = 
					skip((loadoutObj.multiName and b(loadoutObj.displayName or loadoutObj.name) or link(loadoutObj.displayName or loadoutObj.name or "Error"))) .. space .. strings.page_is .. space ..
					article(strings["page_" .. loadoutTypeLower], true) .. (rarityClr and bclr(rarityClr, langs.genderEvaluation_fr(strings["page_" .. loadoutTypeLower], rarity)) or b(rarityName)) .. space ..
					skip(utils.IconLink(strings["page_" .. loadoutTypeLower]))
					
				if table.contains({"offerings", "skills"}, loadoutType) then
					local ownerString = (loadoutObj.isUnique and getUniqueOwnerString(loadoutObj)) or strings.page_belongingTo .. space .. "aux " .. ((loCharType and skip(utils.IconLink(loCharType))) or strings.page_allPlayers)
					loadoutObj.introductionLine = loadoutObj.introductionLine ..
						space .. ownerString
				end
				if loadoutType == "addons" then
					loadoutObj.introductionLine = loadoutObj.introductionLine .. space .. getAddonOwnerString(loadoutObj)
				end
				loadoutObj.introductionLine = loadoutObj.introductionLine .. word("dot")
			end
		end

		result = result .. 
			utils.getDynamicString(
				{
					loadoutObj.introductionLine or
					
					skip((loadoutObj.multiName and b(loadoutObj.displayName or loadoutObj.name) or link(loadoutObj.displayName or loadoutObj.name or "Error"))) .. space .. strings.page_is .. space .. 
						((loadoutType == "perks" and (((loadoutObj.character and strings.page_aUnique) or (loadoutObj.charType and strings.page_aGeneral)) .. space)) or cstr.empty) ..
						
					((loadoutObj.rarity and various.rarity[loadoutObj.rarity] and article(rarity, true) .. (rarityClr and bclr(rarityClr, rarityName) or b(rarityName)) .. space) or (loadoutType ~= "perks" and article(loadoutTypeLower, true)) or cstr.empty) .. 
					skip(utils.IconLink(strings["page_" .. loadoutTypeLower])) .. 
					
					((table.contains({"offerings", "skills"}, loadoutType) and space ..  
						((loadoutObj.character and getUniqueOwnerString(loadoutObj)) or 
						 strings.page_belongingTo .. space .. ((loCharType and skip(utils.IconLink(loCharType))) or strings.page_allPlayers))) or cstr.empty) ..
					((loadoutType == "addons" and space .. getAddonOwnerString(loadoutObj)) or cstr.empty) ..
					((loadoutType == "perks" and space .. getUniqueOwnerString(loadoutObj)) or cstr.empty) ..
					word("dot"),
					
					((isSecret and strings.page_secret .. dot) or cstr.empty),
					((loadoutObj.deprecated and strings.page_deprecated .. dot) or cstr.empty),
					((loadoutObj.notAvailable and strings.page_notAvailable .. dot) or cstr.empty),
					((loadoutObj.retired and strings.page_retired .. dot) or cstr.empty),
					((loadoutObj.mobile and strings.page_mobile .. dot) or cstr.empty)
				},
				strings[loadoutType .. "HeaderTextOrder"]
			)
		result = result .. 
			((loadoutType == "perks" and br .. resolvePerkPrestige(loadoutObj)) or cstr.empty) .. dnl ..
			(
				(loadoutType == "perks" and getPerkPageTable(loadoutObj))
			or	(loadoutType == "powers" and loadoutObj.desc)
			or	getLoadoutTable(loadoutObj, tabParams)
			or "Error"
			) .. dnl .. 
			resolveLoadoutCategories(rarityName)
	else
		result = {notFound = true, desc = strings.loadoutNotFound}
		postprocessDescription(result)
		return result.desc
	end
	
	return result
end

function resolveLoadoutCategories(rarityName)
	local langs = require("Module:Languages" .. utils.lang())
	local result = cstr.empty
	if langs.getLang() == "en" then
		result = rarityName and category(rarityName .. space .. loadoutTypeDisplayGroup) or category(strings.unknownCategory)
	end
	return result
end

function getAddonOwnerString(loadoutObj)
	local langs = require("Module:Languages" .. utils.lang())
	local itemsArticle = cstr.empty
	if langs.getLang() == "fr" then
		itemCat = loadoutData.categories[loadoutObj.itemsCategory]
		itemsArticle = itemCat and string.lower(article(itemCat, false)) or cstr.empty
	end
	local result = 
		(loadoutObj.killer and getPowerString(loadoutObj)) or --addons for Killer's power
		(loadoutObj.itemsCategory and strings.page_forItems .. space .. itemsArticle .. skip(utils.IconLink(loadoutData.categories[loadoutObj.itemsCategory]))) --Survivor addons for items
		or nil
	return (result and space .. result) or cstr.empty
end

function getUniqueOwnerString(loadoutObj)
	if not loadoutObj then return strings.perkNotFound .. space .. brackets(strings.name .. colon .. space .. quotes(loadoutObj.name or cstr.empty)) end
	
	if not loadoutObj.character then
		local langs = require("Module:Languages" .. utils.lang())
		local generalPreposition = cstr.empty
		--if langs.getLang() == "fr" then
		--	generalPreposition = "aux "
		--end
		return strings.page_availableToAll .. space .. generalPreposition .. skip(utils.IconLink(((loadoutObj.charType == 'S' and strings.page_survivors) or strings.page_killers))) --actually shouldn!t be used at all?
	end
	
	local owner = getLoadoutOwner(loadoutObj)
	local isKiller = utils.isKiller(owner)
	
	local langs = require("Module:Languages" .. utils.lang())
	local dativeForm = cstr.empty--((isKiller and the(owner)) or cstr.empty) .. ((not isKiller and owner.shortName) or owner.name)
	if langs.nonEn() then
		dativeForm = langs.evaluateDative(owner, {firstName = (isKiller and owner.name) or owner.name})
		return 
			strings.page_belongingTo .. space ..
			skip(
				dativeForm .. utils.IconLink(utils.resolveCharacterIconLinkName(owner))
			)
	end
	
	return 
		strings.page_belongingTo .. space ..
		utils.IconLink(
			utils.resolveCharacterIconLinkName(owner), 
			((isKiller and the(owner)) or cstr.empty) .. ((not isKiller and owner.shortName) or owner.name)
		)
end


function getLoadoutOwner(loadoutObj)
	return utils.getCharacter(loadoutObj)
end

--[order] - #th character from DLC
function getCharLoadoutByDLC(charType, index, dlcName)
	local dlcsLogic = require("Module:DLCs" .. utils.lang())
	dlcName = dlcsLogic.getShortName(dlcName)
	local dlc = dlcsLogic.getDlcByName(dlcName)
	local characters = various.getCharsByDlc(dlc, charType) --various.getCharsByDlc(dlc, charType)

	if #characters > 0 then
		return getLoadoutByOwner((characters[index] or characters[1]).name)
	end
end

function getPowerString(loadoutObj)
	local killersLogic = require("Module:Killers" .. utils.lang())
	local langs = require("Module:Languages" .. utils.lang())
	local result = strings.page_forItems .. space
	local killerPowerList = {}

	if loadoutObj.killer then
		if type(loadoutObj.killer) == types.table and table.count(loadoutObj.killer) > 1 then
			result = space .. strings.page_andShared .. space
		else
			if loadoutObj.killer == 0 then 
				return strings.page_availableToAll .. space .. skip(utils.IconLink(strings.page_killers))
			end
			loadoutObj.killer = {loadoutObj.killer}
		end
		
		for i, killerId in ipairs(loadoutObj.killer) do
			local pwr = killersLogic.getKillerPower(killersLogic.getKillerById(killerId))
			if pwr then
				if langs.getLang() == "en" then
					result = result .. skip(utils.IconLink(pwr.name))
				else
					result = result .. string.lower(the(pwr)) .. skip(utils.IconLink(pwr.name))
				end
			end
		end
	end
	
	return result
end

function p.isLoadoutSecret(loadout)
	return utils.isTagPresent(loadout, "Secret")
end

function getLoadoutHistory(loadoutList, tabParams)
	local historyData = mw.loadData("Module:Datatable/Loadout/History")
	local result = cstr.empty
	
	local prevDesc
	for loName, lo in pairs(loadoutList) do
		local loadout = table.copy(lo)
		loadout.name = loadout.name or loName
		local tabberTable = {}
		loadout.history = historyData[loadoutTypeLower .. "DescriptionsHistory"][loadout.name]
		if loadout.history then
			for i, historyRecordObj in ipairs(loadout.history) do
				local historyRecord = table.copy(historyRecordObj)
				local tabber = {}
				local iconObj = utils.getIconObject(historyRecord.icon or loadout.name)
				local icon = cstr.empty
				if iconObj.assembly or loadoutType == "perks" then
					icon = utils.assembleImage(loadoutTypeLower, (historyRecord.icon or loadout.name), 96, {rarity = historyRecord.rarity or 1})
				else
					icon = file(utils.getIcon(historyRecord.icon or loadout.name), "96px")
				end
				historyRecord.name = loadout.name
				
				postprocessDescription(historyRecord)
				
				local notice = cstr.empty
				local lookAheadObj = loadout.history[i+1] and table.copy(loadout.history[i+1])
				local noticeRow = function(note) return '<div style = "display: list-item; margin-left: 20px;">' .. note .. '</div>' .. nl end
				if lookAheadObj and lookAheadObj.desc then
					postprocessDescription(lookAheadObj)
					if historyRecord.desc == lookAheadObj.desc then
						if lookAheadObj.rarity ~= historyRecord.rarity then notice = notice .. noticeRow(historyData.loadoutStrings.notice_rarityText) .. nl end
						if lookAheadObj.icon ~= historyRecord.icon then notice = notice .. noticeRow(historyData.loadoutStrings.notice_iconText) .. nl end
					end
				end
				if historyRecord.notice then notice = notice .. noticeRow(historyRecord.notice) .. nl end
				if historyRecord.ptbOnly then notice = notice .. noticeRow(historyData.loadoutStrings.notice_ptbOnly) .. nl end

				tabber.header = historyRecord.displayVersion or historyRecord.version
				tabber.content = 
					'<div class = "noticeWrapper inline-flex flexColumn" style = "width: 100%">' .. nl ..
						'<div class = "historyTableWrapper flex" style = "z-index: 1;">' .. nl ..
							'<div class = "flex flexColumn minContent" style = "max-width: 100%">' .. nl ..
								'<div class = "divTable flex ">' .. nl ..
									'<div class = "flex flexColumn maxContent">' .. nl ..
										'<div class = "divTableHeader">' .. strings.icon .. '</div>' .. nl ..
										'<div class = "divTableCell flex flexCenterItems" style = "height: 100%;">' .. icon .. '</div>' .. nl ..
									'</div>' .. nl ..
									'<div class = "flex flexColumn maxContent">' .. nl ..
										'<div class = "divTableHeader">' .. strings.desc .. '</div>' .. nl ..
										'<div class = "divTableCell textCell flexGrow">' .. historyRecord.desc .. '</div>' .. nl ..
									'</div>' .. nl ..
								'</div>' .. nl
								if notice ~= cstr.empty then
									tabber.content = tabber.content .. 
										'<div class = "historyNotice flexCenterItems flex ">' .. nl ..
											'<div class = "flex historyNoticeText">' .. postprocessText(notice) .. '</div>' .. nl ..
										'</div>' .. nl
								end
								tabber.content = tabber.content .. 
							'</div>' .. nl ..
						'</div>' .. nl ..
					'</div>'
				dmp(tabber)
				table.add(tabberTable, tabber)
			end
			result = result .. utils.getTabberFromTable(tabberTable) .. nl
		end
	end
	return result
end

--------------------------------------------------------------------------------
function wrapPtb(loadout)
	if loadout.desc ~= nil then
		local processedString = processString(strings.ptbHeader, loadout)
		loadout.desc = ptb(loadout.desc, processedString, loadout.patch)
	end
	return loadout
end

local function wrapWipBox(loadout)
	if loadout.desc ~= nil then
		local wipHeader = processString(strings.wipHeader, loadout)
		loadout.desc = utils.wipBox(loadout.desc, wipHeader, loadout.patch)
	end
	return loadout
end

function processString(ptbString, loadout)
	local patchSub = "#patch#"
	local latestPatchSub = "#lpatch#"
	
	local ptbString = ptbString:gsub(patchSub, (loadout and loadout.patch) or cstr.empty):gsub(latestPatchSub, data.latestPatch.patch)
	
	return ptbString
end
--------------------------------------------------------------------------------

function subNames(loadout)
	local regexStrings = {"#pn", "#name"}
	if loadout.desc ~= nil then
		for _, regex in ipairs(regexStrings) do
			for m in loadout.desc:gmatch(regex) do
				loadout.desc = loadout.desc:gsub(regex, i(loadout.name))
			end
		end
	end
	return loadout
end

function subValues(loadoutObj)
	local regexString = "#pl%((%d)%)" -- looking and extracting number from "#pl(x)"
	if loadoutObj and loadoutObj.desc ~= nil then
		for m in loadoutObj.desc: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
			loadoutObj.desc = loadoutObj.desc:gsub(currentRegexString, pl(loadoutObj, tonumber(m)))
			--mw.log(loadoutObj.desc)
		end
	end
	--mw.log("PL: " .. pl)
	return loadoutObj --result
end

function processElementValues(loadout, elementString)
	for pattern, value in pairs(loadout) do
		if pattern ~= "desc" then --skip the description itself
			if type(value) == types.table then --Handling multiple values
				local regexString = "#" .. pattern .. "%((%d)%)"
				
				for index, search in loadout.desc:gmatch(regexString) do
					local i = tonumber(index)
					local currentRegexString = "#" .. pattern .. "%(" .. tonumber(i) .. "%)"
					if elementString and loadout[elementString] then
						loadout[elementString] = loadout[elementString]:gsub(currentRegexString, (tostring(value[i]):gsub("%%", "%%%%")))
					else
						loadout.desc = loadout.desc:gsub(currentRegexString, (tostring(value[i]):gsub("%%", "%%%%")))
					end
				end
			else --should be single value
				--the inner gsub must be in brackets as the function returns trwo values, parsed values and the second one count of replacements: gsub => parsedValues, countOfParsedvalues )
				--this means that if there is no percentage it returns 0, and the 4th parameter of gsub is n for how many replacement is supposed to be done.
				if elementString and loadout[elementString] then
					loadout[elementString] = loadout[elementString]:gsub("#" .. pattern, (tostring(value):gsub("%%", "%%%%")))
				else
					loadout.desc = loadout.desc:gsub("#" .. pattern, (tostring(value):gsub("%%", "%%%%")))
				end
			end
		end
	end
	
	return loadout
end

function pl(loadoutObj, tripplet)
	tripplet = tonumber(tripplet)
	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
	local unit = getUnitById(loadoutObj.units[tripplet]) --If unit is empty, then don't apply bold function
	local rarity = loadoutObj.rarity or 2
	return bclr(rarity, loadoutObj.values[index]) .. "/" .. bclr(rarity + 1, loadoutObj.values[index + 1]) .. "/" .. bclr(rarity + 2, loadoutObj.values[index + 2]) .. space .. ((unit ~= cstr.empty and b(unit)) or cstr.empty) --Units, Tripplet is an index in 'units' list
end

function getUnitById(id)
	local perksData = mw.loadData("Module:Datatable/Loadout" .. utils.lang())
	for _, unit in ipairs(perksData.units) do
		if unit.id == id then return unit.value end
	end
	return strings.unitNotFound
end

return p