Module:CharacterTier

From Another Eden Wiki

Documentation for this module may be created at Module:CharacterTier/doc

local p = {}
local ROW_GRANULARITY = 0.5
local rate_dict = {
    ["10"] = "SS",
    ["9.5"] = "S+",
    ["9.0"] = "S",
    ["8.5"] = "S-",
    ["8.0"] = "A",
    ["7.5"] = "B",
    ["7.0"] = "C",
    ["6.0"] = "D",
    ["5.0"] = "D-"
}
local team_dict = {
    ["Fire"] = "Fire",
    ["Water"] = "Water",
    ["Earth"] = "Earth",
    ["Wind"] = "Wind",
    ["Slash"] = "Slash",
    ["Pierce"] = "Pierce",
    ["Blunt"] = "Blunt",
    ["Magic"] = "Magic",
    ["Lunar"] = "Thunder_Shadow_Crystal",
    ["Thunder"] = "Thunder",
    ["Crystal"] = "Crystal",
    ["Shade"] = "Shade"
}

function p.render(frame)
	mw.log('Module:CharacterTier: render start: ' .. os.clock())
	
	-- collect team filter
	local filter = {}
	if frame.args['team'] then
		filter['team'] = frame.args['team']
	end
	
	-- fetch character ratings
	mw.log('Module:CharacterTier: fetch characters start: ' .. os.clock())
	local data = p.fetchCharacters(frame, filter)
	mw.log('Module:CharacterTier: fetch characters end: ' .. os.clock())
		
	local teams = {}
	local teamcount = 0
	for ix,character in pairs(data) do
		if (character.team ~= nil) and not (teams[character.team]) then
			teams[character.team] = true
			teamcount = teamcount + 1
			if teamcount > 1 then
				break
			end
		end
	end
	
	local result = {}
	
	if teamcount > 1 then
		table.insert(result, '<table class="wikitable tierlist tierlist-multi"><tr><th>Rating</th><th>Fire</th><th>Water</th><th>Earth</th><th>Wind</th><th>Slash</th><th>Pierce</th><th>Blunt</th><th>Magic</th><th>Lunar</th><th>Thunder</th><th>Crystal</th><th>Shade</th></tr>')
		table.insert(result, p.makeRatingTable(data, 'teams', 70))
		table.insert(result, '</table>')
	else
		table.insert(result, '<table class="wikitable tierlist tierlist-single">')
		table.insert(result, '<tr><th>Rating</th><th>Characters</th></tr>')
		table.insert(result, p.makeRatingTable(data, nil, 100))
		table.insert(result, '</table>')
	end
	
	table.insert(result, '<table class="wikitable tierlist-details" style="max-width: 725px;"><tr><th>Icon</th><th>Name/Type/Rating</th><th style="min-width: 475px">Remarks</th></tr>')
	table.insert(result, p.makeRemarksTable(data))
	table.insert(result, '</table>')
	
	mw.log('Module:CharacterTier: render end: ' .. os.clock())
	return table.concat(result, '')
end

function p.fetchCharacters(f, filter)
	mw.log('Module:CharacterTier: fetchCharacters start: ' .. os.clock())
	
	local queryFields = 'id,name,icon,element,weapon,team,rating,reasons'
	local queryArgs = {limit = 1000}
	if type(filter) == "table" then
		local v = filter['team']
		if type(v) == "string" then
			queryArgs.where = ('team=%s'):format(v)
		end
	end
	
	local result = {}
	for _, row in ipairs(mw.ext.cargo.query('CharacterRatings', queryFields, queryArgs)) do
		result[#result+1] = row
		
		local args = row
		args.ix = #result
	end
	mw.log('Module:Character.fetchCharacters end: ' .. os.clock())
	return result
end

function p.makeRatingTable(data, columns, iconsize)
	local result = {}
	local ratings = {}
	local teams = {}
	if columns == 'teams' then
		teams = {'Fire','Water','Earth','Wind','Slash','Pierce','Blunt','Magic','Lunar','Thunder','Crystal','Shade'}
	elseif #data > 0 then
		teams = {data[1].team}
	end
	
	local rating_keys = {}
	for _, charInfo in pairs(data) do
		local realRating = tonumber(charInfo.rating)
		if realRating then
			local rating
			if #teams > 1 then
				rating = realRating - realRating % ROW_GRANULARITY
			else
				rating = realRating
			end
			if ratings[rating] == nil then
				ratings[rating] = {}
				rating_keys[#rating_keys + 1] = rating
			end
			charInfo.numRating = realRating
			table.insert(ratings[rating], charInfo)
		end
	end
	table.sort(rating_keys)
	
	for i=#rating_keys, 1, -1 do
		local characters = ratings[rating_keys[i]]
		table.sort(characters, function(a,b)
			if a.numRating ~= b.numRating then
				return a.numRating > b.numRating
			end
			return a.name < b.name
		end)
		table.insert(result, '<tr style="vertical-align: top;">')
		local label, hasRatingRange = "?", false
		if characters[1].rating ~= characters[#characters].rating then
			label, hasRatingRange = characters[1].rating .. " - " .. characters[#characters].rating, true
		elseif (tonumber(characters[1].rating) or 0) ~= 0 then
			label = characters[1].rating
		end
		table.insert(result, '<td style="font-weight: bold;vertical-align: middle;text-align: center;">' .. rate_dict[label] .. '</td>')
		for ix2,team in pairs(teams) do
			table.insert(result, '<td>')
			local lastRating = false
			for key,character in pairs(characters) do
				if character.team == team then
					if hasRatingRange and lastRating ~= character.numRating then
						if lastRating then
							table.insert(result, '<hr class="filterable-divider" title="' .. character.rating .. '">')
						end
						lastRating = character.numRating
					end
					table.insert(result, '<span data-short-id="' .. character.icon .. '">[[File:' .. character.icon .. ' rank5_command.png|'..iconsize..'px|link=#ref' .. character.ix .. '|' .. character.name .. ']]</span>')
				end
			end
			table.insert(result, '</td>\n')
		end
		table.insert(result, '</tr>\n')
	end
	return table.concat(result, '')
end

function p.makeRemarksTable(data)
	table.sort(data, function(a,b)
		if a.name == b.name then
			if a.team == "Fire" then
				return true
			elseif a.team == "Water" then
				if b.team == "Fire" then
					return false
				end
				return true
			elseif a.team == "Earth" then
				for index, value in ipairs({"Fire","Water"}) do
		        	if value == b.team then
		            	return false
		            end
				end
	            return true
	        elseif a.team == "Wind" then
				for index, value in ipairs({"Fire","Water","Earth"}) do
		        	if value == b.team then
		            	return false
		            end
				end
	            return true
	        elseif a.team == "Slash" then
				for index, value in ipairs({"Fire","Water","Earth","Wind"}) do
		        	if value == b.team then
		            	return false
		            end
				end
	            return true
	        elseif a.team == "Pierce" then
				for index, value in ipairs({"Fire","Water","Earth","Wind","Slash"}) do
		        	if value == b.team then
		            	return false
		            end
				end
	            return true
	        elseif a.team == "Blunt" then
				for index, value in ipairs({"Fire","Water","Earth","Wind","Slash","Pierce"}) do
		        	if value == b.team then
		            	return false
		            end
				end
	            return true
	        elseif a.team == "Magic" then
				for index, value in ipairs({"Fire","Water","Earth","Wind","Slash","Pierce","Blunt"}) do
		        	if value == b.team then
		            	return false
		            end
				end
	            return true
	        elseif a.team == "Lunar" then
				for index, value in ipairs({"Fire","Water","Earth","Wind","Slash","Pierce","Blunt","Magic"}) do
		        	if value == b.team then
		            	return false
		            end
				end
	            return true
	        elseif a.team == "Thunder" then
				for index, value in ipairs({"Fire","Water","Earth","Wind","Slash","Pierce","Blunt","Magic","Lunar"}) do
		        	if value == b.team then
		            	return false
		            end
				end
	            return true
	        elseif a.team == "Crystal" then
				for index, value in ipairs({"Fire","Water","Earth","Wind","Slash","Pierce","Blunt","Magic","Lunar","Thunder"}) do
		        	if value == b.team then
		            	return false
		            end
				end
	            return true
	        end
	        return false
		else
			return a.name < b.name
		end
	end)
	
	local result = {}
	local frame = mw.getCurrentFrame()
	local weaponsCache = {}
	for ix,character in pairs(data) do
		if (character ~= nil) and (character.name ~= nil) then
			local name = character.name
			local icon = character.icon
			local rating = character.rating or 0
			local remarks = '-'
			if character.reasons ~= nil then
				remarks = character.reasons
			end
			local element = frame:expandTemplate{ title = 'Element icon', args = { character.element, size = '23px' } }
			local weapon = frame:expandTemplate{ title = 'Weapon icon', args = { character.weapon, size = '23px' } }

			table.insert(result, '<tr style="text-align: center;">')
			table.insert(result, '<td><div id="ref'..character.ix..'">[[File:' .. icon .. ' rank5_command.png|80px|link=[[' .. name .. ']]]]</div></td>')
			table.insert(result, '<td style="white-space: nowrap;">[['..name..']]<hr>'..element..' '..weapon..'<hr>[[File:Stance '..team_dict[character.team]..'.png|x23px]]<hr>'..rate_dict[rating]..'</td>')
			table.insert(result, '<td style="text-align: left; vertical-align: top; min-width: 475px;">\n'..remarks..'\n</td>')
			table.insert(result, '</tr>')
		end
	end
	return table.concat(result, '\n')
end

return p