Module:Pit

From Mighty Party Wiki
Jump to navigation Jump to search

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

local p = {}

local _tier_constants = {
	{
		['from'] = 0,
		['to'] = 16000,
		['multiplier'] = 3.4,
		['level_inc'] = {
			--[[boss_1 : 334=--]] 0,
			--[[boss_2 : 334=--]] 0,
			--[[boss_3 : 334=--]] 0,
			--[[boss_4 : 334=--]] 0,
			--[[boss_5 : 334=--]] 0,
			--[[boss_6 : 335=--]] 1,
			--[[boss_7 : 336=--]] 2,
			--[[boss_8 : 336=--]] 2,
			--[[boss_9 : 337=--]] 3,
			--[[boss_10: 337=--]] 3,
		},
	},
	{
		['from'] = 16000,
		['to'] = 18000,
		['multiplier'] = 3.2,
		['level_inc'] = {
			--[[boss_1 : 334=--]] 0,
			--[[boss_2 : 334=--]] 0,
			--[[boss_3 : 334=--]] 0,
			--[[boss_4 : 334=--]] 0,
			--[[boss_5 : 335=--]] 1,
			--[[boss_6 : 336=--]] 2,
			--[[boss_7 : 336=--]] 2,
			--[[boss_8 : 337=--]] 3,
			--[[boss_9 : 337=--]] 3,
			--[[boss_10: 337=--]] 3,
		},
	},
	{
		['from'] = 18000,
		['to'] = 22000,
		['multiplier'] = 2.9,
		['level_inc'] = {
			--[[boss_1 : 334=--]] 0,
			--[[boss_2 : 334=--]] 0,
			--[[boss_3 : 335=--]] 1,
			--[[boss_4 : 335=--]] 1,
			--[[boss_5 : 335=--]] 1,
			--[[boss_6 : 336=--]] 2,
			--[[boss_7 : 336=--]] 2,
			--[[boss_8 : 337=--]] 3,
			--[[boss_9 : 337=--]] 3,
			--[[boss_10: 337=--]] 3,
		},
	},
	{
		['from'] = 22000,
		['to'] = 27000,
		['multiplier'] = 2.6,
		['level_inc'] = {
			--[[boss_1 : 334=--]] 0,
			--[[boss_2 : 335=--]] 1,
			--[[boss_3 : 335=--]] 1,
			--[[boss_4 : 335=--]] 1,
			--[[boss_5 : 336=--]] 2,
			--[[boss_6 : 337=--]] 3,
			--[[boss_7 : 337=--]] 3,
			--[[boss_8 : 337=--]] 3,
			--[[boss_9 : 338=--]] 4,
			--[[boss_10: 338=--]] 4,
		},
	},
	{
		['from'] = 27000,
		['to'] = 33600,
		['multiplier'] = 2.5,
		['level_inc'] = {
			--[[boss_1 : 335=--]] 1,
			--[[boss_2 : 335=--]] 1,
			--[[boss_3 : 335=--]] 1,
			--[[boss_4 : 335=--]] 1,
			--[[boss_5 : 336=--]] 2,
			--[[boss_6 : 337=--]] 3,
			--[[boss_7 : 337=--]] 3,
			--[[boss_8 : 337=--]] 3,
			--[[boss_9 : 338=--]] 4,
			--[[boss_10: 338=--]] 4,
		},
	},
	{
		['from'] = 33600,
		['to'] = 39285,
		['multiplier'] = 2.2,
		['level_inc'] = {
			--[[boss_1 : 335=--]] 1,
			--[[boss_2 : 335=--]] 1,
			--[[boss_3 : 336=--]] 2,
			--[[boss_4 : 336=--]] 2,
			--[[boss_5 : 337=--]] 3,
			--[[boss_6 : 337=--]] 3,
			--[[boss_7 : 338=--]] 4,
			--[[boss_8 : 338=--]] 4,
			--[[boss_9 : 338=--]] 4,
			--[[boss_10: 338=--]] 4,
		},
	},
	{
		['from'] = 39285,
		['to'] = 43335,
		['multiplier'] = 2.1,
		['level_inc'] = {
			--[[boss_1 : 335=--]] 1,
			--[[boss_2 : 335=--]] 1,
			--[[boss_3 : 336=--]] 2,
			--[[boss_4 : 337=--]] 3,
			--[[boss_5 : 337=--]] 3,
			--[[boss_6 : 338=--]] 4,
			--[[boss_7 : 338=--]] 4,
			--[[boss_8 : 338=--]] 4,
			--[[boss_9 : 338=--]] 4,
			--[[boss_10: 338=--]] 4,
		},
	},
	{
		['from'] = 43335,
		['to'] = 49645,
		['multiplier'] = 1.9,
		['level_inc'] = {
			--[[boss_1 : 335=--]] 1,
			--[[boss_2 : 336=--]] 2,
			--[[boss_3 : 336=--]] 2,
			--[[boss_4 : 337=--]] 3,
			--[[boss_5 : 338=--]] 4,
			--[[boss_6 : 338=--]] 4,
			--[[boss_7 : 338=--]] 4,
			--[[boss_8 : 338=--]] 4,
			--[[boss_9 : 338=--]] 4,
			--[[boss_10: 339=--]] 5,
		},
	},
	{
		['from'] = 49645,
		['to'] = 62000,
		['multiplier'] = 1.8,
		['level_inc'] = {
			--[[boss_1 : 336=--]] 2,
			--[[boss_2 : 336=--]] 2,
			--[[boss_3 : 337=--]] 3,
			--[[boss_4 : 338=--]] 4,
			--[[boss_5 : 338=--]] 4,
			--[[boss_6 : 338=--]] 4,
			--[[boss_7 : 338=--]] 4,
			--[[boss_8 : 338=--]] 4,
			--[[boss_9 : 339=--]] 5,
			--[[boss_10: 339=--]] 5,
		},
	},
	{
		['from'] = 62000,
		['to'] = 67600,
		['multiplier'] = 1.7,
		['level_inc'] = {
			--[[boss_1 : 336=--]] 2,
			--[[boss_2 : 337=--]] 3,
			--[[boss_3 : 337=--]] 3,
			--[[boss_4 : 338=--]] 4,
			--[[boss_5 : 338=--]] 4,
			--[[boss_6 : 338=--]] 4,
			--[[boss_7 : 339=--]] 5,
			--[[boss_8 : 339=--]] 5,
			--[[boss_9 : 339=--]] 5,
			--[[boss_10: 340=--]] 6,
		},
	},
	{
		['from'] = 67600,
		['to'] = 75300,
		['multiplier'] = 1.5,
		['level_inc'] = {
			--[[boss_1 : 337=--]] 3,
			--[[boss_2 : 337=--]] 3,
			--[[boss_3 : 338=--]] 4,
			--[[boss_4 : 338=--]] 4,
			--[[boss_5 : 339=--]] 5,
			--[[boss_6 : 339=--]] 5,
			--[[boss_7 : 339=--]] 5,
			--[[boss_8 : 339=--]] 5,
			--[[boss_9 : 340=--]] 6,
			--[[boss_10: 340=--]] 6,
		},
	},
	{
		['from'] = 75300,
		['to'] = 82470,
		['multiplier'] = 1.1,
		['level_inc'] = {
			--[[boss_1 : 337=--]] 3,
			--[[boss_2 : 337=--]] 3,
			--[[boss_3 : 338=--]] 4,
			--[[boss_4 : 339=--]] 5,
			--[[boss_5 : 339=--]] 5,
			--[[boss_6 : 339=--]] 5,
			--[[boss_7 : 339=--]] 5,
			--[[boss_8 : 340=--]] 6,
			--[[boss_9 : 340=--]] 6,
			--[[boss_10: 340=--]] 6,
		},
	},
	{
		['from'] = 82470,
		['to'] = 88043,
		['multiplier'] = 1,
		['level_inc'] = {
			--[[boss_1 : 337=--]] 3,
			--[[boss_2 : 338=--]] 4,
			--[[boss_3 : 338=--]] 4,
			--[[boss_4 : 339=--]] 5,
			--[[boss_5 : 339=--]] 5,
			--[[boss_6 : 339=--]] 5,
			--[[boss_7 : 340=--]] 6,
			--[[boss_8 : 340=--]] 6,
			--[[boss_9 : 340=--]] 6,
			--[[boss_10: 340=--]] 6,
		},
	},
	{
		['from'] = 88043,
		['to'] = 102858,
		['multiplier'] = 1,
		['level_inc'] = {
			--[[boss_1 : 338=--]] 4,
			--[[boss_2 : 338=--]] 4,
			--[[boss_3 : 339=--]] 5,
			--[[boss_4 : 339=--]] 5,
			--[[boss_5 : 340=--]] 6,
			--[[boss_6 : 340=--]] 6,
			--[[boss_7 : 340=--]] 6,
			--[[boss_8 : 340=--]] 6,
			--[[boss_9 : 340=--]] 6,
			--[[boss_10: 340=--]] 6,
		},
	},
	{
		['from'] = 102858,
		['to'] = 107450,
		['multiplier'] = 0.9,
		['level_inc'] = {
			--[[boss_1 : 338=--]] 4,
			--[[boss_2 : 339=--]] 5,
			--[[boss_3 : 339=--]] 5,
			--[[boss_4 : 340=--]] 6,
			--[[boss_5 : 340=--]] 6,
			--[[boss_6 : 340=--]] 6,
			--[[boss_7 : 340=--]] 6,
			--[[boss_8 : 340=--]] 6,
			--[[boss_9 : 341=--]] 9,
			--[[boss_10: 341=--]] 9,
		},
	},
	{
		['from'] = 107450,
		['to'] = 112240,
		['multiplier'] = 0.9,
		['level_inc'] = {
			--[[boss_1 : 339=--]] 5,
			--[[boss_2 : 339=--]] 5,
			--[[boss_3 : 340=--]] 6,
			--[[boss_4 : 340=--]] 6,
			--[[boss_5 : 341=--]] 9,
			--[[boss_6 : 341=--]] 9,
			--[[boss_7 : 341=--]] 9,
			--[[boss_8 : 341=--]] 9,
			--[[boss_9 : 341=--]] 9,
			--[[boss_10: 341=--]] 9,
		},
	},
	{
		['from'] = 112240,
		['to'] = 142330,
		['multiplier'] = 0.8,
		['level_inc'] = {
			--[[boss_1 : 339=--]] 5,
			--[[boss_2 : 339=--]] 5,
			--[[boss_3 : 340=--]] 6,
			--[[boss_4 : 341=--]] 9,
			--[[boss_5 : 341=--]] 9,
			--[[boss_6 : 341=--]] 9,
			--[[boss_7 : 341=--]] 9,
			--[[boss_8 : 341=--]] 9,
			--[[boss_9 : 341=--]] 9,
			--[[boss_10: 341=--]] 9,
		},
	},
	{
		['from'] = 142330,
		['to'] = 191530,
		['multiplier'] = 0.6,
		['level_inc'] = {
			--[[boss_1 : 340=--]] 6,
			--[[boss_2 : 340=--]] 6,
			--[[boss_3 : 341=--]] 9,
			--[[boss_4 : 341=--]] 9,
			--[[boss_5 : 341=--]] 9,
			--[[boss_6 : 341=--]] 9,
			--[[boss_7 : 341=--]] 9,
			--[[boss_8 : 341=--]] 9,
			--[[boss_9 : 342=--]]11,
			--[[boss_10: 342=--]]11,
		},
	},
	{
		['from'] = 191530,
		['to'] = 250000,
		['multiplier'] = 0.6,
		['level_inc'] = {
			--[[boss_1 : 340=--]] 6,
			--[[boss_2 : 341=--]] 9,
			--[[boss_3 : 341=--]] 9,
			--[[boss_4 : 341=--]] 9,
			--[[boss_5 : 342=--]]11,
			--[[boss_6 : 342=--]]11,
			--[[boss_7 : 342=--]]11,
			--[[boss_8 : 342=--]]11,
			--[[boss_9 : 342=--]]11,
			--[[boss_10: 343=--]]12,
		},
	},
	{
		['from'] = 250000,
		['to'] = 300000,
		['multiplier'] = 0.4,
		['level_inc'] = {
			--[[boss_1 : 341=--]] 9,
			--[[boss_2 : 341=--]] 9,
			--[[boss_3 : 341=--]] 9,
			--[[boss_4 : 342=--]]11,
			--[[boss_5 : 343=--]]12,
			--[[boss_6 : 343=--]]12,
			--[[boss_7 : 344=--]]13,
			--[[boss_8 : 345=--]]14,
			--[[boss_9 : 346=--]]15,
			--[[boss_10: 347=--]]16,
		},
	},
	{
		['from'] = 300000,
		['to'] = 350000,
		['multiplier'] = 0.4,
		['level_inc'] = {
			--[[boss_1 : 342=--]]11,
			--[[boss_2 : 342=--]]11,
			--[[boss_3 : 342=--]]11,
			--[[boss_4 : 343=--]]12,
			--[[boss_5 : 344=--]]13,
			--[[boss_6 : 344=--]]13,
			--[[boss_7 : 345=--]]14,
			--[[boss_8 : 346=--]]15,
			--[[boss_9 : 347=--]]16,
			--[[boss_10: 348=--]]17,
		},
	},
	{
		['from'] = 350000,
		['to'] = 400000,
		['multiplier'] = 0.3,
		['level_inc'] = {
			--[[boss_1 : 342=--]]11,
			--[[boss_2 : 343=--]]12,
			--[[boss_3 : 343=--]]12,
			--[[boss_4 : 344=--]]13,
			--[[boss_5 : 344=--]]13,
			--[[boss_6 : 345=--]]14,
			--[[boss_7 : 346=--]]15,
			--[[boss_8 : 347=--]]16,
			--[[boss_9 : 348=--]]17,
			--[[boss_10: 349=--]]18,
		},
	},
	{
		['from'] = 400000,
		['to'] = 430000,
		['multiplier'] = 0.3,
		['level_inc'] = {
			--[[boss_1 : 343=--]]12,
			--[[boss_2 : 344=--]]13,
			--[[boss_3 : 344=--]]13,
			--[[boss_4 : 345=--]]14,
			--[[boss_5 : 345=--]]14,
			--[[boss_6 : 346=--]]15,
			--[[boss_7 : 347=--]]16,
			--[[boss_8 : 348=--]]17,
			--[[boss_9 : 349=--]]18,
			--[[boss_10: 350=--]]19,
		},
	},
	{
		['from'] = 430000,
		['to'] = 470000,
		['multiplier'] = 0.3,
		['level_inc'] = {
			--[[boss_1 : 344=--]]13,
			--[[boss_2 : 345=--]]14,
			--[[boss_3 : 345=--]]14,
			--[[boss_4 : 346=--]]15,
			--[[boss_5 : 346=--]]15,
			--[[boss_6 : 347=--]]16,
			--[[boss_7 : 348=--]]17,
			--[[boss_8 : 349=--]]18,
			--[[boss_9 : 350=--]]19,
			--[[boss_10: 351=--]]20,
		},
	},
	{
		['from'] = 470000,
		['to'] = 520000,
		['multiplier'] = 0.2,
		['level_inc'] = {
			--[[boss_1 : 344=--]]13,
			--[[boss_2 : 345=--]]14,
			--[[boss_3 : 346=--]]15,
			--[[boss_4 : 347=--]]16,
			--[[boss_5 : 347=--]]16,
			--[[boss_6 : 348=--]]17,
			--[[boss_7 : 349=--]]18,
			--[[boss_8 : 350=--]]19,
			--[[boss_9 : 351=--]]20,
			--[[boss_10: 352=--]]21,
		},
	},
	{
		['from'] = 520000,
		['to'] = 550000,
		['multiplier'] = 0.2,
		['level_inc'] = {
			--[[boss_1 : 345=--]]14,
			--[[boss_2 : 346=--]]15,
			--[[boss_3 : 347=--]]16,
			--[[boss_4 : 348=--]]17,
			--[[boss_5 : 348=--]]17,
			--[[boss_6 : 349=--]]18,
			--[[boss_7 : 350=--]]19,
			--[[boss_8 : 351=--]]20,
			--[[boss_9 : 352=--]]21,
			--[[boss_10: 353=--]]22,
		},
	},
	{
		['from'] = 550000,
		['to'] = 650000,
		['multiplier'] = 0.2,
		['level_inc'] = {
			--[[boss_1 : 346=--]]15,
			--[[boss_2 : 347=--]]16,
			--[[boss_3 : 348=--]]17,
			--[[boss_4 : 349=--]]18,
			--[[boss_5 : 349=--]]18,
			--[[boss_6 : 350=--]]19,
			--[[boss_7 : 351=--]]20,
			--[[boss_8 : 352=--]]21,
			--[[boss_9 : 353=--]]22,
			--[[boss_10: 354=--]]22,
		},
	},
	{
		['from'] = 650000,
		['to'] = 12000000,
		['multiplier'] = 0.2,
		['level_inc'] = {
			--[[boss_1 : 346=--]]15,
			--[[boss_2 : 347=--]]16,
			--[[boss_3 : 348=--]]17,
			--[[boss_4 : 349=--]]18,
			--[[boss_5 : 350=--]]19,
			--[[boss_6 : 351=--]]20,
			--[[boss_7 : 352=--]]21,
			--[[boss_8 : 353=--]]22,
			--[[boss_9 : 354=--]]22,
			--[[boss_10: 708=--]]23,
		},
	},
}

local function _get_boss_position_in_rotation(rotation, boss)
	for i=1,10 do
		local boss_data = rotation['boss'..i] or {}
		if boss_data.name == boss then
			return i
		end
	end
	return -1
end

local function _locate_boss_in_rotations(all_rotations, boss)
	local sorted_by_id = {}
	for rotation_id in pairs(all_rotations) do
		-- tonumber() is necessary, otherwise '10' < '2'
		table.insert(sorted_by_id, tonumber(rotation_id))
	end
	table.sort(sorted_by_id)
	
	local result = {}
	for _, rotation_id in pairs(sorted_by_id) do
		local rotation = all_rotations[tostring(rotation_id)]
		local pos = _get_boss_position_in_rotation(rotation, boss)
		if pos >= 0 then
			local rotation_summary = {
				['id'] = rotation_id,
				['position'] = pos,
				['rotation'] = rotation,
			}
			table.insert(result, rotation_summary)
		end
	end
	return result
end

local function _wiki_link_for_boss(boss_name)
	local data = mw.loadData("Module:Pit/data")
	local boss = data.boss[boss_name] or {}
	if boss.wiki_name then
		return '[['..boss.wiki_name..'|'..boss_name..']]'
	end
	return '[['..boss_name..']]'
end

local function _infobox_reward(boss_num)
	if     boss_num ==  1 then return '3x {{icon|pitbigrarechest}}'
    elseif boss_num ==  2 then return '3x {{icon|pitbigrarechest}}'
    elseif boss_num ==  3 then return '1x {{icon|pitepicchest}}'
    elseif boss_num ==  4 then return '2x {{icon|pitepicchest}}'
    elseif boss_num ==  5 then return '3x {{icon|pitepicchest}}'
    elseif boss_num ==  6 then return '1x {{icon|pitlegendarychest}}'
    elseif boss_num ==  7 then return '1x {{icon|pitlegendarychest}}'
    elseif boss_num ==  8 then return '1x {{icon|pitlegendarychest}}'
    elseif boss_num ==  9 then return '1x {{icon|pitlegendarychest}}'
    elseif boss_num == 10 then return '1x {{icon|pitlegendarychest}}'
    end
    return ''
end

local function _render_infobox(boss, image, quote, rotations)
	local infobox_wiki = mw.html.create('infobox'):attr('type', 'hero')
	
	infobox_wiki
		:tag('title')
			:tag('default')
				:wikitext(boss)
				
	infobox_wiki
		:tag('group'):attr('layout', 'horizontal')
			:tag('image')
				:tag('default'):wikitext(image)
	
	if quote ~= '' then
		infobox_wiki:tag('group'):attr('layout', 'horizontal')
			:tag('data')
				:tag('format')
					:tag('div')
						:css('text-align', 'center')
						:css('font-style', 'italic')
						:wikitext(quote)
	end
	
	for _, rotation_summary in pairs(rotations) do
		local prev_boss = 'N/A'
		if rotation_summary.position > 1 then
			local prev_pos = rotation_summary.position-1
			prev_boss = _wiki_link_for_boss(rotation_summary.rotation['boss'..prev_pos].name)
		end
		
		local next_boss = 'N/A'
		if rotation_summary.position < 10 then
			local next_pos = rotation_summary.position+1
			next_boss = _wiki_link_for_boss(rotation_summary.rotation['boss'..next_pos].name)
		end
		
		infobox_wiki:tag('group'):attr('layout', 'horizontal')
			:tag('header')
				:wikitext('Rotation #'..rotation_summary.id..', boss #'..rotation_summary.position)
			:done()
			:tag('data')
					:tag('label'):wikitext('Previous boss'):done()
					:tag('default'):wikitext(prev_boss):done()
			:done()
			:tag('data')
					:tag('label'):wikitext('Reward'):done()
					:tag('default')
						:wikitext(_infobox_reward(rotation_summary.position))
					:done()
			:done()
			:tag('data')
					:tag('label'):wikitext('Next boss'):done()
					:tag('default'):wikitext(next_boss):done()
			:done()
	end
	
	return mw.getCurrentFrame():preprocess(tostring(infobox_wiki))
end

--- infobox returns a wiki-formatted infobox card for a Pit boss.
-- Expected parameters from the frame:
--  1) "boss"
-- @param frame MediaWiki frame
-- @return wiki-formatted card or empty string, if boss is unknown.
function p.infobox(frame)
	local data = mw.loadData("Module:Pit/data")
	local boss = frame:getParent().args['boss'] or frame.args[1]
	
	local boss_data = data.boss[boss] or {}
	local image = boss_data['image'] or 'Icon question.png'
	local quote = boss_data['quote'] or ''
	
	local rotations = _locate_boss_in_rotations(data.rotation, boss)
	
	return _render_infobox(boss, image, quote, rotations)
end



local function _render_rotation_bosses(rotation_summary)
	local result = {}
	table.insert(result, '{|class="wikitable" width="100%" style="text-align:center"')
	for i=1,10 do
		table.insert(result, '!style="min-width:95px;"|Boss #'..i)
	end
	table.insert(result, '|-')
	for i=1,10 do
		local boss_data = rotation_summary.rotation['boss'..i] or {}
		local boss_name = boss_data['name'] or 'N/A'
		if i == rotation_summary.rotation then
			table.insert(result, "|'''"..boss_name.."'''")
		else
			table.insert(result, '|'.._wiki_link_for_boss(boss_name))
		end
	end
	table.insert(result, '|}')
	return table.concat(result, '\n')
end

local function _render_rotation_tier_boss(tier_constants, inc_from_tier, boss_data, boss_skill_context, rotation_boss_config)
	local result = {}
	table.insert(result, '{|class="wikitable" width="100%"')
	table.insert(result, '!style="width:100px"|{{icon|coppercoin|link=yes}}')
	table.insert(result, '!style="width:80"|{{icon|hp|text=yes}}')
	table.insert(result, '!style="width:50px"|Skill Type')
	table.insert(result, '!Description')
	
	local tier_boss_level = 1 + inc_from_tier + rotation_boss_config.level_inc
	local tier_data = boss_data.level[tostring(tier_boss_level)]
	if not tier_data then
		table.insert(result, '|}')
		return table.concat(result, '\n')
	end
	
	local hp = tier_data.hp
	local reward = math.floor(tier_constants['multiplier']*hp)
	
	table.insert(result, '|-')
	table.insert(result, string.format(
		'|rowspan="%d" style="border-bottom:1px; text-align:center;"|%d{{icon|coppercoin}}',
		boss_skill_context.skill_count, reward
	))
	table.insert(result, string.format(
		'|rowspan="%d" style="border-bottom:1px; text-align:center;"|%d{{icon|hp}}',
		boss_skill_context.skill_count, hp
	))
	
	for i=1,boss_skill_context.skill_count do
		local skill_type = boss_data['skill'..i..'_type']
		table.insert(result, string.format('|{{icon|%s}}', skill_type))
		
		local desc = boss_data['skill'..i..'_desc']
		local val = tier_data['skill'..i]
		if val then
			if skill_type == 'summon' then
				-- This is a special case.
				-- First, val denotes the level increase, not the level inself.
				-- Second, we have to substitute it twice: for the label
				-- and for the tooltip markup.
				-- Third, the resulting value could exceed the largest known
				-- level and this hurts tooltip template.
				local capped_level = boss_data['skill'..i..'_max_summon_level']
				if capped_level then
					val = math.min(val+1, capped_level)
				else
					val = val + 1
				end
				desc = string.format(desc, val, val)
			else
				local times = boss_data['skill'..i..'_substitute_times']
				if times then
					local fmt_args = {}
					for i=1,times do
						table.insert(fmt_args, val)
					end
					desc = string.format(desc, unpack(fmt_args))
				else
					desc = string.format(desc, val)
				end
			end
		end
		table.insert(result, '|'..desc)
		
		if i<boss_skill_context.skill_count then
			table.insert(result, '|-')
		end
	end

	table.insert(result, '|}')
	return table.concat(result, '\n')
end

local function _render_rotation_tier_squad(inc_from_tier, rotation_boss_config)
	local params = {}
	for i=1,8 do
		local squad_hero = rotation_boss_config['hero'..i]
		if squad_hero then
			params['hero'..i] = squad_hero['name']
			local inc_from_rotation = squad_hero['level_inc']
			if inc_from_rotation ~= nil then
				params['hero'..i..'level'] = (
					1 + inc_from_rotation + inc_from_tier)
			end
		end
	end
	
	return mw.getCurrentFrame():expandTemplate{title='Squad', args=params}
end

local function _render_rotation_tier(tier, tier_constants, boss_num, boss_data, boss_skill_context, rotation_boss_config)
	local result = {}
	table.insert(result, string.format('|-|Tier %d=', tier))
	
	table.insert(result, string.format('[[Army Might]]: %d — %d<br/>',
		tier_constants['from'],
		tier_constants['to']-1
	))
	
	local inc_from_tier = tier_constants.level_inc[boss_num]
	
	table.insert(result, "'''Boss'''")
	table.insert(result,
		_render_rotation_tier_boss(
			tier_constants, inc_from_tier,
			boss_data, boss_skill_context, rotation_boss_config))
	
	table.insert(result, "'''Squad'''")
	table.insert(result,
		_render_rotation_tier_squad(inc_from_tier, rotation_boss_config))
	return table.concat(result, '\n')
end

local function _render_rotation_tiers(boss_num, boss_data, rotation_boss_config)
	local skill_count = 0
	for i=1,4 do
		if boss_data['skill'..i..'_type'] then
			skill_count = skill_count + 1
		end
	end
	local boss_skill_context = {
		['skill_count'] = skill_count,
	}
	
	local result = {}
	table.insert(result, '=== Boss Tiers ===')
	table.insert(result, '<tabber>')
	for tier, tier_constants in ipairs(_tier_constants) do
		table.insert(result, _render_rotation_tier(
			tier, tier_constants,
			boss_num, boss_data, boss_skill_context,
			rotation_boss_config))
	end
	table.insert(result, '</tabber>')
	return table.concat(result, '\n')
end

local function _render_rotation_field(field)
	local result = {}
	table.insert(result, '=== Starting Field ===')
	if not field then
		table.insert(result,  "'''Unspecified. Please update [[Module:Pit/data]].'''")
		return table.concat(result, '\n')
	end
	
	table.insert(result, '{|class="wikitable" style="margin:auto;"')
	table.insert(result, '!colspan="3"|Player Field')
	table.insert(result, '|style="border: none; width: 5px;"|')
	table.insert(result, '!colspan="3"|Enemy Field')
	for x=0,3 do
		table.insert(result, '|-')
		for y=1,6 do
			local cell_wiki_template = '|style="width: 100px;"|%s<br/>%s<br/>%s'
			local cell = string.char(x+string.byte('A'))..y
			
			local obj = '&nbsp;'
			local desc = '&nbsp;'
			local cell_data = field[cell]
			if cell_data then
				obj = '[['..cell_data.object..']]'
				desc = cell_data.desc or ''
			end
			table.insert(result, string.format(cell_wiki_template, cell, obj, desc))
			
			if y == 3 then
				table.insert(result, '|style="border: none; width: 5px;"|')
			end
		end
	end
	table.insert(result, '|}')
	return table.concat(result, '\n')
end

local function _render_rotation_section(rotation_summary, boss_data)
	local result = {}
	table.insert(result,
		string.format('== Rotation #%d, boss #%d ==',
			rotation_summary.id, rotation_summary.position))

	table.insert(result, _render_rotation_bosses(rotation_summary))
		
	local rotation_boss_config =
		rotation_summary.rotation['boss'..rotation_summary.position] or {}
	
	table.insert(result, _render_rotation_tiers(
		rotation_summary.position, boss_data, rotation_boss_config))

	table.insert(result,
		_render_rotation_field(rotation_boss_config['field']))
	
	return table.concat(result, '\n')
end

local function _render_rotations(rotation_summaries, boss_data)
	local result = {}
	for _, rotation_summary in pairs(rotation_summaries) do
		local page_section = _render_rotation_section(rotation_summary, boss_data)
		table.insert(result, page_section)
	end
	return mw.getCurrentFrame():preprocess(table.concat(result, '\n'))
end

--- infobox returns a wiki-formatted page split into sections, each of which
--- corresponds to a specific rotation.
-- Expected parameters from the frame:
--  1) "boss"
-- @param frame MediaWiki frame
-- @return wiki-formatted page
function p.rotations(frame)
	local data = mw.loadData("Module:Pit/data")
	local boss = frame:getParent().args['boss'] or frame.args[1]
	
	local boss_data = data.boss[boss] or {}
	local rotation_summaries = _locate_boss_in_rotations(data.rotation, boss)
	
	return _render_rotations(rotation_summaries, boss_data)
end

return p