Module:SkillHeroList

From Fire Emblem Heroes Wiki
Jump to: navigation, search
Template-info.svg Documentation

Used by {{Skill Hero List}} on individual skill pages, to show which heroes learn this skill.

local Util = require('Module:Util')
local HeroUtil = require 'Module:HeroUtil'
local cargo = mw.ext.cargo
local p = {}
local List = require 'Module:ListUtil'
local Hash = require 'Module:HashUtil'
local escq = require 'Module:EscQ'.main1

-- unobtainable rarity value
local BAD_RARITY = 5 + 1

-- breadth-first search, input node goes to index 0
local bfs = function (node, graph)
	local i = 0
	local list = {[i] = node}
	local included = {[node] = true}

	while true do
		local current = list[i]
		if not current then
			break
		end
		i = i + 1
		local neighbours = graph[current]
		if neighbours then
			for _, v in ipairs(neighbours) do
				if not included[v] then
					list[#list + 1] = v
					included[v] = true
				end
			end
		end
	end

	return list
end

local getPreEvolved = function (wikiName, pagename)
	local frame = mw.getCurrentFrame()
	local baseWeaponQuery = cargo.query('WeaponEvolutions,Skills', 'Skills.Name=name', {
		join = 'WeaponEvolutions.BaseWeapon=Skills.WikiName',
		where = ("EvolvesInto='%s'"):format(escq(wikiName)),
		groupBy = 'BaseWeapon',
	})
	if #baseWeaponQuery > 0 then
		local links = List.map(baseWeaponQuery, function (v)
			return frame:expandTemplate{title = 'Wt', args = {v.name}}
		end)
		local last = table.remove(links)
		local wepList = #links == 0 and last or (table.concat(links, ', ') .. (#links >= 2 and ', or ' or ' or ') .. last)
		return ('[[Category:Evolved weapons]][[%s]] can be evolved from %s using the [[Weapon Refinery]].<br>'):format(pagename, wepList)
	else
		return ''
	end
end

local heroList = function (args)
	local pagename = args[1]
	-- any skill from the page can be used
	local skillQuery = cargo.query('Skills', 'WikiName,Scategory', {
		where = ("_pageName='%s'"):format(escq(pagename)),
		limit = 1,
	})[1] or {}
	local wikiName = skillQuery.WikiName or ''
	local cat = skillQuery.Scategory or ''
	local header = cat == 'weapon' and getPreEvolved(wikiName, pagename) or ''

	local heroesWithSkill = cargo.query(
		'Units,UnitSkills',
		"Units._pageName=page,Units.WikiName=wikiname,IFNULL(CONCAT(Name,': ',Title),Name)=name", {
			join = 'Units.WikiName=UnitSkills.WikiName',
			where = ("skill='%s'"):format(escq(wikiName)),
			groupBy = 'Units._pageName',
			orderBy = "IFNULL(Properties__full,'') NOT LIKE '%%enemy%%'",
			limit = 500,
		})
	if #heroesWithSkill == 0 then
		return header .. "This skill is currently not owned by any unit."
	end

	local avail = HeroUtil.getLowestRarities {current = true}

	local heroSkills = Hash.from_ipairs(heroesWithSkill, function (hero)
		local lowestRarity = avail[hero.page] and avail[hero.page]:bounds() or BAD_RARITY
		return hero.name, List.map_self(cargo.query('UnitSkills,Skills', 'Skills.WikiName=tag,Skills.Name=name,Skills._pageName=page,defaultRarity=dr,unlockRarity=ur', {
			join = 'Skills.WikiName=UnitSkills.skill',
			where = ("UnitSkills.WikiName='%s' AND Scategory='%s'"):format(escq(hero.wikiname), escq(cat)),
			limit = 1000,
		}), function (v)
			v.rarity = math.max(math.min(tonumber(v.dr) or BAD_RARITY, tonumber(v.ur) or BAD_RARITY), lowestRarity)
			return v
		end)
	end)

	local skillSet = {}
	for _, skills in pairs(heroSkills) do
		for _, skill in ipairs(skills) do
			skillSet[skill.tag] = true
		end
	end

	local prereq_graph, after_graph = Util.getSkillChains(Hash.keys(skillSet))
	local prereqs = List.reverse_self(bfs(wikiName, prereq_graph))
	local afters = bfs(wikiName, after_graph)
	local skillOrder = Hash.invert(List.concat(prereqs, List.concat({wikiName}, afters)))
	Hash.map_self(heroSkills, function (skills)
		local chainSkills = List.select(skills, function (skill) return skillOrder[skill.tag] end)
		table.sort(chainSkills, function (lhs, rhs) return skillOrder[lhs.tag] < skillOrder[rhs.tag] end)
		return chainSkills
	end)

	local maxColumns = math.max(0, unpack(List.map(Hash.values(heroSkills), function (skills) return #skills end)))

	-- Initialize the table
	local tbl = mw.html.create('table')
		:addClass('wikitable striped sortable')
		:css('text-align','center')

	-- Table Headers
	tbl:tag('th')
		:wikitext('Unit')
	tbl:tag('th')
		:attr('colspan', maxColumns)
		:wikitext('Skill chain')
	
	local RARITY_TXT = Util.getRarityTexts()
	RARITY_TXT[BAD_RARITY] = 'N/A'

	for _, hero in ipairs(heroesWithSkill) do
		local cells = List.map(heroSkills[hero.name], function (skill)
			return ("[[%s%s]]<br>%s"):format(skill.page, skill.page == skill.name and '' or ('|' .. skill.name), RARITY_TXT[skill.rarity])
		end)

		local tr = tbl:tag('tr')

		-- Hero Name
		tr:tag('td')
			:tag('div')
			:wikitext(Util.getHeroIcon(hero.page, '50px') .. '<br>')
			:css('margin-top','5px')
			:done()
			:tag('div')
			:wikitext('[[' .. hero.page .. '|' .. hero.name .. ']]')

		for j = 1, maxColumns do
			tr:tag('td')
				:wikitext(cells[j] or '–')
				:css('min-width','80px')
		end
	end

	return header .. tostring(tbl)
end

return require 'Module:MakeMWModule'.makeMWModule {heroList = heroList}