Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

This module uses info gathered from the API and stored in the data page to:

It is also invoked by other modules, such as Module:CosmeticMachine and Module:CosmeticCrates.


local getArgs = require('Module:Arguments').getArgs

local cosmeticData = mw.loadData('Module:CosmeticInfo/Data')
if not cosmeticData then
	return "Error: Unable to load data"
end

local obtainabilityData = mw.loadData("Module:CosmeticInfo/Obtainability/Data")

local p = {}

local function getCosmetic(args)
	return cosmeticData["cosmetics"][args.name]
end

local function isAnimated(frame, filename)
	if not frame.preprocess then
		return true
	end
	local content = frame:preprocess('{{:File:' .. filename .. '}}')
	return content:find('Category:Animated images') ~= nil
end

local function getSimpleField(field)
	return function(frame)
		local args = getArgs(frame)
		local cosmetic = getCosmetic(args)
		return cosmetic and cosmetic[field]
	end
end

local function isInCollection(collections)
	return function(frame)
		local args = getArgs(frame)
		local cosmetic = getCosmetic(args)
		return (cosmetic and collections[cosmetic.collection]) or false
	end
end

local collectionSets = {
	isInCrateCollection = {
		"Mechanical","Natural","Oceanic","Magical","Mythic","Slimy","Cuckoo",
		"Arena","Cloudy","Ninja","Explosive","Mystical Aquatic",
		"Neon Galaxy","Candy Factory","Ancient Jungle","Seasonal Variety"
	},
	isFishingCosmetic = { "Fishing" },
	isArcaneGateCosmetic = { "Arcane Gate" }
}

for key, list in pairs(collectionSets) do
	local set = {}
	for _, name in ipairs(list) do
		set[name] = true
	end
	p[key] = isInCollection(set)
end

local function formatGlyphs(description)
	local glyphs = {
		["<glyph:\"mcc:icon.tooltips.veteran\"> "] = "<br />[[File:Icon-Veteran.png|16px]] <span style=\"color: #52C8FF; font-weight: bold\">",
		["<glyph:\"mcc:icon.tooltips.squidtek\"> "] = "<br />[[File:Icon-Squidtek.png|16px]] <span style=\"color: #52C8FF; font-weight: bold\">",
		["<glyph:\"mcc:icon.info_blue\"> "] = "<br />[[File:Icon-Info-Blue.png|16px]] <span style=\"color: #55FF55; font-weight: bold\">",
	}
	
	local openSpan = false
	for key, val in pairs(glyphs) do
		if description:find(key) then
			description = description:gsub(key, (openSpan and val or "</span>" .. val))
			openSpan = true
		end
	end
	return openSpan and (description .. "</span>") or nil
end

local function formatArcaneBonus(description)
	if description:find("Arcane Bonus") then
		description = description:gsub("Arcane Bonus", "<br /><span style=\"color: #64FCFC\"><span class=\"white-text\">Arcane Bonus</span>")
		description = description:gsub("(Grants you)(.-)( while)", function(startText, middle, endText)
			return startText .. "<span class=\"white-text\">" .. middle .. "</span>" .. endText
		end)
		return description .. "</span>"
	end
	return description
end

local function stripDescription(description)
	description = description:gsub("<glyph:\".-\"> ?", "")
	description = description:gsub("Arcane Bonus.*", "")
	return mw.text.trim(description)
end

function p.getDescription(frame)
	local args = getArgs(frame)
	local cosmetic = getCosmetic(args)
	if not cosmetic then return "No data found" end
	
	local plain = args.plain == "true"
	if plain then
		return stripDescription(cosmetic.description)
	end
	
	return formatGlyphs(cosmetic.description) or formatArcaneBonus(cosmetic.description)
end

p.getCategory = getSimpleField("category")
p.getCollection = getSimpleField("collection")
p.getRarity = getSimpleField("rarity")
p.isColorable = getSimpleField("colorable")
p.getTrophiesAwarded = getSimpleField("trophies")
p.isBonusTrophies = getSimpleField("isBonusTrophies")
p.canBeDonated = getSimpleField("canBeDonated")

function p.getDonationLimit(frame)
	local args = getArgs(frame)
	local cosmetic = getCosmetic(args)
	return (cosmetic and cosmetic.royalReputation and cosmetic.royalReputation.donationLimit) or 0
end

function p.getReputationAmount(frame)
	local args = getArgs(frame)
	local cosmetic = getCosmetic(args)
	return (cosmetic and cosmetic.royalReputation and cosmetic.royalReputation.reputationAmount) or 0
end

p.getGlobalNumberOwned = getSimpleField("globalNumberOwned")
p.getObtainmentHint = getSimpleField("obtainmentHint")

function firstToUpper(str)
	return (str:gsub("^%l", string.upper))
end

function p.getType(frame)
	local args = getArgs(frame)
	local cosmetic = getCosmetic(args)
	return firstToUpper(cosmetic["type"]:lower())
end

function p.getFishingClimate(frame)
	local args = getArgs(frame)
	local cosmetic = getCosmetic(args)
	if not cosmetic or not p.isFishingCosmetic({name = args.name}) then
		return nil
	end

	local hint = cosmetic.obtainmentHint or ""
	local climate = hint:match("while fishing in the (.-) Climate")
	if climate then
		return firstToUpper(climate)
	end

	return nil
end

function p.getLastUpdatedDate()
	local lang = mw.language.getContentLanguage()
	return lang:formatDate( 'd F Y, h:i a', '@' .. cosmeticData['last_updated'], false ) .. ' UTC'
end

function p.lastUpdatedIcon()
	return string.format(
		'<sup><abbr title="Last updated: %s" tabindex="0">ⓘ</abbr></sup>',
		p.getLastUpdatedDate()
	)
end

local function getOwnedNumber(raw)
	raw = tostring(raw or "0"):gsub(",", ""):gsub("%+", "")
	return tonumber(raw) or 0
end

local function sortCosmetics(list, order)
	table.sort(list, function(a, b)
		return order == "desc" and a.owned > b.owned or a.owned < b.owned
	end)
end

local function formatCosmeticList(cosmetics)
	local result = {}
	for _, c in ipairs(cosmetics) do
		table.insert(result, string.format("* %s (%s)", c.name, c.display or c.owned))
	end
	return table.concat(result, "\n")
end

function p.sortListByNumberOwned(frame)
	local args = getArgs(frame)
	local names = mw.text.split(args.names or "", ",%s*")
	local order = args.order or "asc"
	local unformatted = args.unformatted or ""

	local cosmetics = {}
	for _, name in ipairs(names) do
		local data = cosmeticData["cosmetics"][name]
		if data and data.globalNumberOwned then
			table.insert(cosmetics, {
				name = name,
				owned = getOwnedNumber(data.globalNumberOwned),
				display = data.globalNumberOwned
			})
		end
	end

	sortCosmetics(cosmetics, order)
	if unformatted ~= "" then
		return cosmetics
	else
		return formatCosmeticList(cosmetics)
	end
end

function p.getNumberOwnedListByFieldValue(frame)
	local args = getArgs(frame)
	local field = args.field
	local target = args.value
	local order = args.order or "asc"

	local filterValue = ({
		["true"] = true,
		["false"] = false
	})[target] or target

	local filtered = {}
	for name, data in pairs(cosmeticData["cosmetics"]) do
		if data[field] == filterValue and data.globalNumberOwned then
			table.insert(filtered, {
				name = name,
				owned = getOwnedNumber(data.globalNumberOwned),
				display = data.globalNumberOwned
			})
		end
	end

	sortCosmetics(filtered, order)
	return formatCosmeticList(filtered)
end

function p.tradeablesPage(frame)
	local function getGroup(cosmetic)
		local obtainability = obtainabilityData[cosmetic.name]
		local cType = p.getType({name = cosmetic.name})
		
		if obtainability == "Grand Auction" then
			return obtainability
		elseif obtainability == "Returning" then
			return "Returning in a future event"
		elseif obtainability == "Unobtainable" then
			return "Unobtainable other than trading"
		elseif cType == "Collector" then
			return cType
		else
			return "Unobtainable once this season ends"
		end
	end

	local function getOwnershipBand(owned)
		if owned == "10000+" or owned == "1000+" then
			return owned .. " owned"
		else
			return "Less than 1000 owned"
		end
	end
	
	local function groupCosmetics(cosmeticList)
		local grouped = {}
		for _, cosmetic in ipairs(cosmeticList) do
			local category = getGroup(cosmetic)
			local band = getOwnershipBand(cosmetic.display)
			grouped[category] = grouped[category] or {}
			grouped[category][band] = grouped[category][band] or {}
			table.insert(grouped[category][band], cosmetic)
		end
		return grouped
	end
	
	local function renderTableForGroup(group, title)
		local out = {'<h2>' .. title .. '</h2>'}
		local bandOrder = { "Less than 1000 owned", "1000+ owned", "10000+ owned"  }

		for _, band in ipairs(bandOrder) do
			local list = group[band]
			if list then
				sortCosmetics(list)
				if title ~= "Collector" and title ~= "Grand Auction" then
					table.insert(out, '<h3>' .. band .. '</h3>')
				end
				table.insert(out, '<div class="tradeable-display">')
				local count = 0
				for _, item in ipairs(list) do
					if count > 0 and count % 4 == 0 then
						--table.insert(out, '<div>')
					end

					local name, owned = item.name, item.display
					local filename = name .. '.png'
					local cell

					if band == "Less than 1000 owned" then
						cell = string.format('<div><center><div class="afix" style="width: 100px">[[File:%s|link=%s]]</div></center><div style="word-wrap:break-word; text-align:center"><small>[[%s]]</small><br />Owned: <b>%s</b></div></div>', filename, name, name, owned)
					else
						cell = string.format('<div><center><div class="afix" style="width: 100px">[[File:%s|link=%s]]</div></center><div style="word-wrap:break-word; text-align:center"><small>[[%s]]</small></div></div>', filename, name, name)
					end

					table.insert(out, cell)
					count = count + 1
				end
				table.insert(out, '</div>')
			end
		end

		return table.concat(out, '\n')
	end
	
	local tradeables = {}
	for name, _ in pairs(cosmeticData["cosmetics"]) do
		local cType = p.getType({ name = name })
		if cType == "Limited" or cType == "Collector" then
			local data = cosmeticData["cosmetics"][name]
			if data and data.globalNumberOwned then
				table.insert(tradeables, {
					name = name,
					display = data.globalNumberOwned,
					owned = getOwnedNumber(data.globalNumberOwned)
				})
			end
		end
	end
	
	local grouped = groupCosmetics(tradeables)
	
	local output = {}
	local categoryOrder = {
		"Returning in a future event", "Unobtainable other than trading", "Unobtainable once this season ends",
		"Collector", "Grand Auction"
	}

	for _, category in ipairs(categoryOrder) do
		if grouped[category] then
			table.insert(output, renderTableForGroup(grouped[category], category))
		end
	end
	
	return table.concat(output, '\n\n')
end

return p