Module:Status

From Granblue Fantasy Wiki
Jump to navigation Jump to search

local p = {}
local getArgs = require('Module:Arguments').getArgs

function p.fetchStatus(name)
	local data = mw.loadData('Module:StatusList')
	--local buffs = data.buffs
	--local debuffs = data.debuffs
	
	-- 'groups', 'categories', 
	local fields = { 'wiki_guide', 'wiki_name', 'wiki_detail', 'wiki_comment', 'name', 'detail', 'status', 'icon', 'category', 'page' }
	local result = {}
	local fetch_name = name
	repeat
		while data.aliases[fetch_name] ~= nil do
			fetch_name = data.aliases[fetch_name]
		end
		local status = data.buffs[fetch_name]
		if status == nil then
			status = data.debuffs[fetch_name]
		end
		if status == nil then
			status = data.effects[fetch_name]
		end

		if status == nil then
			return nil
		end

		for ix,field in pairs(fields) do
			if (result[field] == nil) and (status[field] ~= nil) then
				result[field] = status[field]
			end
		end
		
		result.ref = status.name
		
		if status.link ~= nil then
			fetch_name = status.link
		end
	until (status == nil) or (status.link == nil)
	
	return result
end

function p.format2(frame)
	local args = getArgs(frame, {
		trim = true,
		removeBlanks = true,
	})
	return p._format2(args)
end

function p._format2(args)
	-- format2 is a conversion of the templates Status, Status/Simple and Tt to lua code
	local name = args[1] or args['name'] or false
	assert(name, 'Status param 1 or name missing.');
	local only_icon = (args[2] == 'icon') or (args[3] == 'icon') or false

	local status = p.fetchStatus(name)
	if status == nil then
		return p._formatOld(name, args)
	end

	status.fetch_name = name
	
	status.time = args['t'] or args['time'] or p.checkParam(status.time) or nil
	status.amount = args['a'] or args['amount'] or p.checkParam(status.amount) or nil
	status.amount_max = args['am'] or args['amount_max'] or p.checkParam(status.amount_max) or nil
	status.probability = args['p'] or args['probability'] or p.checkParam(status.probability) or nil
	status.accuracy = args['acc'] or args['accuracy'] or p.checkParam(status.accuracy) or nil
	status.stacking = args['s'] or args['stacking'] or p.checkParam(status.stacking) or nil
	status.multiplier = args['m'] or args['multiplier'] or p.checkParam(status.multiplier) or nil
	status.comment = args['c'] or args['comment'] or nil
	status.n = args['n'] or args['times'] or p.checkParam(status.n) or nil
	status.hide = ',' .. (status.hide or '') .. ',' .. (args['hide'] or '') .. ','
	status.page = p.checkParam(status.page)
	status.icon = p.checkParam(status.icon)
	status.category = p.checkParam(status.category)
	status.ref = p.checkParam(status.ref)

	status.wiki_guide = status.wiki_guide or false
	status.wiki_name = p.checkParam(p.formatAmount(status.wiki_name, status.amount or '', status.amount_max or '', status.probability or ''))
	status.wiki_detail = p.checkParam(p.formatAmount(p.formatTime(status.wiki_detail, status.time or ''), status.amount or '', status.amount_max or '', status.probability or ''))
	status.wiki_comment = p.checkParam(p.formatAmount(p.formatTime(status.wiki_comment, status.time or ''), status.amount or '', status.amount_max or '', status.probability or ''))
	
	if args['template'] or false then
		local frame = mw.getCurrentFrame()
		return frame:expandTemplate{ title = args['template'], args = status }
	else
		-- this is Template:Status/Simple converted to lua
		local status_name = status.wiki_name or status.name or status.fetch_name
		local status_name_n = status_name .. ((status.n ~= nil) and (' ('..status.n..(status.n == '1' and ' time' or ' times')..')') or '')
		local status_detail = {}
		
		if only_icon then
			table.insert(status_detail, status_name_n .. '<span class="hr"></span>')
		end
		
		table.insert(status_detail, status.wiki_detail or status.detail or '')
		table.insert(status_detail, '<br />')

		if ((status.name == "Shielded" or status.name == "DMG Mitigation") and status.amount ~= nil) then
			local shield_value = string.gsub(status.amount, ",", "")
			if tonumber(shield_value) ~= nil then
				status_name_n = status_name_n .. ' (' .. status.amount .. ')'
			end
		elseif (status.name == "Burned" and status.amount ~= nil) then
			local burn_value = string.gsub(status.amount, ",", "")
			if tonumber(burn_value) ~= nil then
				status_name_n = status_name_n .. ' (' .. status.amount .. ')'
			end
		elseif (status.name == "Poisoned" and status.amount ~= nil) then
			local poisoned_value = string.gsub(status.amount, ",", "")
			if tonumber(poisoned_value) ~= nil then
				status_name_n = status_name_n .. ' (' .. status.amount .. ')'
			end
		elseif (status.name == "Suffocate" and status.amount ~= nil) then
			local suffocate_value = string.gsub(status.amount, ",", "")
			if tonumber(suffocate_value) ~= nil then
				status_name_n = status_name_n .. ' (' .. status.amount .. ')'
			end
		elseif (status.name == "Putrefied" and status.amount ~= nil) then
			local putrefied_value = string.gsub(status.amount, ",", "")
			if tonumber(putrefied_value) ~= nil then
				status_name_n = status_name_n .. ' (' .. status.amount .. ')'
			end
		elseif (status.name == "Recovery Cap Boosted" and status.amount ~= nil) then
			local recoverycapboosted_value = string.gsub(status.amount, ",", "")
			if tonumber(recoverycapboosted_value) ~= nil then
				status_name_n = status_name_n .. ' (' .. recoverycapboosted_value .. ')'
			end
		elseif (status.name == "Max HP Capped" and status.amount ~= nil) then
			local recoverycapboosted_value = string.gsub(status.amount, ",", "")
			if tonumber(recoverycapboosted_value) ~= nil then
				status_name_n = status_name_n .. ' (' .. status.amount .. ')'
			end
		elseif (status.name == "Supplemental DMG" and status.amount ~= nil) then
			local a_value = string.gsub(status.amount, ",", "")
			if tonumber(a_value) ~= nil then
				status_name_n = status_name_n .. ' (' .. status.amount .. ')'
			end
		end	

		if status.probability then
			if string.find(status_name, 'Critical', 1, true) then
				table.insert(status_detail, 
					'<span class="hr"></span><strong>Strength</strong>: ' ..
				    status.probability .. ' chance of dealing ' .. (status.amount or '??') .. ' more damage.')
			end
			if string.find(status_name, 'Armored', 1, true) then
				table.insert(status_detail, 
					'<span class="hr"></span><strong>Strength</strong>: ' ..
				    status.probability .. ' chance of taking ' .. (status.amount or '??') .. ' less damage.')
			end
		elseif status.amount and (string.find(status.hide, ',amount,', 1, true) == nil) then
			table.insert(status_detail, '<span class="hr"></span><strong>Strength</strong>: ' .. status.amount)
			if (status.amount_max) then
				table.insert(status_detail, ' (Max: ' .. status.amount_max .. ')')
			end
		end
		
		if status.multiplier then
			table.insert(status_detail, '<span class="hr"></span><strong>Multiplier</strong>: ')
			if status.multiplier == 'n' then
				table.insert(status_detail, '[[Damage Formula/Detailed Damage Formula|Normal]]')
			elseif status.multiplier == 'm' then
				table.insert(status_detail, '[[Damage Formula/Detailed Damage Formula|Omega]]')
			elseif status.multiplier == 'ex' then
				table.insert(status_detail, '[[Damage Formula/Detailed Damage Formula|Ex]]')
			elseif status.multiplier == 'unk' then
				table.insert(status_detail, '[[Damage Formula/Detailed Damage Formula|Ex]] ([[Ranko Kanzaki Summon|Mysterious]])')
			elseif status.multiplier == 'el' then
				table.insert(status_detail, '[[Damage Formula/Detailed Damage Formula|Elemental]]')
			elseif status.multiplier == 'u' then
				table.insert(status_detail, '[[Damage Formula/Detailed Damage Formula#Total Char Unique ATK boosts|Unique]]')
			elseif status.multiplier == 'us' then
				table.insert(status_detail, '[[Damage Formula/Detailed Damage Formula#Unique Stackable boost|Unique Stackable]]')
			elseif status.multiplier == 's' then
				table.insert(status_detail, '[[Damage Formula/Detailed Damage Formula#Seraphic boost|Seraphic]]')
			elseif status.multiplier == 'p' then
				table.insert(status_detail, '[[Damage_Formula/Detailed_Damage_Formula#Perpetuity boost|Perpetuity]]')
			elseif status.multiplier == 'ass' then
				table.insert(status_detail, '[[Damage_Formula/Detailed_Damage_Formula#Assassin boost|Assassin]]')
			elseif status.multiplier == 'nen' then
				table.insert(status_detail, '[[Damage_Formula/Detailed_Damage_Formula#Normal.2FOmega.2FEX_Enmity_and_Stamina_boosts|Normal Enmity]]')
			elseif status.multiplier == 'nst' then
				table.insert(status_detail, '[[Damage_Formula/Detailed_Damage_Formula#Normal.2FOmega.2FEX_Enmity_and_Stamina_boosts|Normal Stamina]]')
			elseif status.multiplier == 'en' then
				table.insert(status_detail, '[[Damage_Formula/Detailed_Damage_Formula#Char_Enmity.2FStamina_boost|Character Enmity]]')
			elseif status.multiplier == 'st' then
				table.insert(status_detail, '[[Damage_Formula/Detailed_Damage_Formula#Char_Enmity.2FStamina_boost|Character Stamina]]')
			elseif status.multiplier == 'add' then
				table.insert(status_detail, 'Additive')
			elseif status.multiplier == 'mul' then
				table.insert(status_detail, 'Multiplicative')
			else 
				table.insert(status_detail, '?')
			end
		end
	
		if status.accuracy then
			if status.accuracy == 'g' then
        			table.insert(status_detail, '<span class="hr"></span>[[Debuff Resistance|Base Accuracy]]: ' .. 'Guaranteed to land if the foe does not have 100% debuff resistance (10,000% base accuracy)')
			else 
        			table.insert(status_detail, '<span class="hr"></span>[[Debuff Resistance|Base Accuracy]]: ' .. status.accuracy)
			end
		end
		
		local half_turn = false
		if status.time then
			table.insert(status_detail, '<span class="hr"></span><strong>Duration</strong>: ')
			if tonumber(string.sub(status.time, -2, -2)) ~= nil then
				local unit = string.sub(status.time, -1, -1)
				if (unit == 'T') or (unit == 't') then
					local turns = string.sub(status.time, 0, -2)
					table.insert(status_detail, turns .. ((turns == '1') and ' turn ' or ' turns'))
					if (string.sub(status.time, -3, -2) == '.5') then
						half_turn = true
					end
				elseif (unit == 'S') or (unit == 's') then
					table.insert(status_detail, string.sub(status.time, 0, -2) .. ' seconds')
				elseif (unit == 'M') or (unit == 'm') then
					table.insert(status_detail, string.sub(status.time, 0, -2) .. ' minutes')
				else
					table.insert(status_detail, status.time)
				end
			elseif (status.time == 'I') or (status.time == 'i') then
				table.insert(status_detail, 'Indefinite')
			else
				table.insert(status_detail, status.time)
			end
		end
	
		if status.stacking then
			table.insert(status_detail, '<span class="hr"></span><strong>Stacking</strong>: ')
			if status.stacking == 's' then
				table.insert(status_detail, 'Single')
			elseif status.stacking == 'd' then
				table.insert(status_detail, 'Dual')
			elseif status.stacking == 'ca' then
				table.insert(status_detail, 'Charge Attack')
			elseif status.stacking == 'smn' then
				table.insert(status_detail, 'Summon')
			elseif status.stacking == 'cs' then
				table.insert(status_detail, 'Charge Attack / Summon')
			elseif status.stacking == 'st' then
				table.insert(status_detail, 'Single Target')
			elseif status.stacking == 'tw' then
				table.insert(status_detail, 'Teamwide')
			elseif status.stacking == 'p' then
				table.insert(status_detail, 'Permanent')
			elseif status.stacking == 'sp' then
				table.insert(status_detail, 'Special Buff')
			elseif status.stacking == 'bs2' then
				table.insert(status_detail, '[[Battle System 2.0]]')
			elseif status.stacking == 'u' then
				table.insert(status_detail, 'Unique')
			else
				table.insert(status_detail, status.stacking)
			end
		end
	
		if status.comment then
			table.insert(status_detail, '<span class="hr"></span>' .. status.comment)
		end
		
		if status.wiki_comment and (string.find(status.hide, ',wiki_comment,', 1, true) == nil) then
			table.insert(status_detail, '<span class="hr"></span>' .. status.wiki_comment)
		end
	
		if half_turn then
			if string.sub(status.time, 0, -1) == '0.5' then
				table.insert(status_detail, '<span class="hr"></span>[[Half_Turns|Applied during the attack phase and lasts until the turn ends.]]<br />')
			else
				table.insert(status_detail, '<span class="hr"></span>[[Half_Turns|Applied during the attack phase.]]<br />On the next turn, it\'ll have ')
				local is_one = False
				if string.match(status.time, '%d.%d-%d.%d[tT]') == nil then
					local time_value = string.sub(status.time, 0, -4)
					table.insert(status_detail, time_value)
					is_one = (time_value == '1')
				else
					local time_parts = mw.text.split(status.time, '-', true)
					table.insert(status_detail,  string.sub(time_parts[1], 0, -3) .. '-' .. string.sub(time_parts[2], 0, -4))
				end
				table.insert(status_detail, ' ' .. (is_one and 'turn' or 'turns') .. ' remaining.')
			end
		end
	
		status_detail = table.concat(status_detail)
		
		local status_label = {}
		local status_link = status.page or ('Status Effects')
		
		if status.category then
			table.insert(status_label, '[[Category:' .. status.category .. ']]')
		end
		if status.icon then
			table.insert(status_label, '[[File:' ..status.icon .. '|25px|link=' .. status_link .. ']]')
		end
		if not only_icon then
			table.insert(status_label, '[[' .. status_link .. '|' .. status_name_n .. ']]')
		end
		status_label = table.concat(status_label)
		
		-- Format like tt template call {{tt|status_label|status_detail|line=false}}
		
		local result = '<span class="tooltip' .. (status.wiki_guide and ' status_guide' or '') .. 
			'" style="border-bottom: 0;">' .. status_label ..
			'<span class="tooltiptext">' .. status_detail .. '</span></span>'
			
		if status.wiki_guide then
			local frame = mw.getCurrentFrame()
			result = frame:preprocess(result)
		end
		
		if status.icon then
			return '<span class="image_link">' .. result .. '</span>'
		else
			return result
		end
	end
end

function p._formatOld(name, args)
	-- icon_name = (args[2] or name) .. '.png'
	return name .. 
	       '[[Category:Undefined Status]]' ..
	       '[[Category:Undefined Status: ' .. name .. ']]'
	       --'<span class="image_link">' ..
	       -- '[[File:Status ' .. icon_name .. '|link=Status Effects|25px]]' .. 
	       -- '[[Status Effects|'.. name .. ']]' ..
	       -- '</span>[[Category:Old Status System]]'
end

function p.format(frame)
	local result = {}
	
	local name = nil
	if frame.args.name ~= nil then
		name = frame.args.name
	elseif frame.args[1] ~= nil then
		name = frame.args[1]
	end
	if name == nil then
		error('Status name missing')
	end
	
	local amount = ''
	if frame.args.amount ~= nil then
		amount = frame.args.amount
	end
	
	local time = ''
	if frame.args.time ~= nil then
		time = frame.args.time
	elseif frame.args[2] ~= nil then
		time = frame.args[2]
	end
	
	-- reconstruct a status 
	local status = p.fetchStatus(name)
	if status == nil then
		return 'not found'
	end
	status.fetch_name = name
	status.only_icon = frame.args.only_icon
	if (frame.args.time ~= nil) and (string.len(frame.args.time) > 0) then
		status.time = frame.args.time
	end
	if (frame.args.amount ~= nil) and (string.len(frame.args.amount) > 0) then
		status.amount = frame.args.amount
	end
	if (frame.args.probability ~= nil) and (string.len(frame.args.probability) > 0) then
		status.probability = frame.args.probability
	end
	if (frame.args.accuracy ~= nil) and (string.len(frame.args.accuracy) > 0) then
		status.accuracy = frame.args.accuracy
	end
	if (frame.args.accuracy ~= nil) and (string.len(frame.args.accuracy) > 0) then
		status.accuracy = frame.args.accuracy
	end
	if (frame.args.stacking ~= nil) and (string.len(frame.args.stacking) > 0) then
		status.stacking = frame.args.stacking
	end
	if (frame.args.multiplier ~= nil) and (string.len(frame.args.multiplier) > 0) then
		status.multiplier = frame.args.multiplier
	end
	status.comment = frame.args.comment
	if frame.args.hide ~= nil then
		if status.hide ~= nil then
			status.hide = ',' .. status.hide .. ',' .. frame.args.hide .. ','
		else
			status.hide = ',' .. frame.args.hide .. ','
		end
	elseif status.hide ~= nil then
		status.hide = ',' .. status.hide .. ','
	end
	
	status.wiki_name = p.formatAmount(status.wiki_name, frame.args.amount, frame.args.amount_max, frame.args.probability)
	status.wiki_detail = p.formatAmount(p.formatTime(status.wiki_detail, frame.args.time), frame.args.amount, frame.args.amount_max, frame.args.probability)
	status.wiki_comment = p.formatAmount(p.formatTime(status.wiki_comment, frame.args.time), frame.args.amount, frame.args.amount_max, frame.args.probability)
	
	return frame:expandTemplate{ title = frame.args.template, args = status }
end

function p.formatAmount(text, amount, amount_max, probability)
	if (text == nil) then
		return nil
	end
	
	local result = text
	result = string.gsub(result, "%%a[m]?%%", function(a) 
		if (a == '%a%') then
			local unit = string.sub(amount, -1)
			if (unit == '%') then
				if (probability ~= nil) and (string.len(probability) > 0) then
					return probability .. ' / ' .. amount
				else
					return amount
				end
			end
			return ''
		elseif (a == '%am%') then
			if (string.len(amount_max) > 0) then
				return ' / Max: ' .. amount_max
			else
				return ''
			end
		end
	end)

	return result 
end

function p.formatTime(text, time)
	if (text == nil) then
		return nil
	end
	
	local si,ei = string.find(text, '%t%', 1, true)
	if si == nil then
		return text
	end
	
	if (time == nil) then
		time = ''
	end

	local unit = string.sub(time, -1)
	local result = ''
	if (unit == 'T') then
		local s = string.sub(time, 1, -2)
		if (s == '0') then
			result = '0 turns'
		elseif (s == '1') then
			result = '1 turn'
		else
			result = s .. ' turns'
		end
	elseif (unit == 's') then
		local s = string.sub(time, 1, -2)
		result = s .. ' seconds'
	elseif (unit == 'm') then
		local s = string.sub(time, 1, -2)
		if (s == '1') then
			result = '1 minute'
		else
			result = s .. ' minutes'
		end
	elseif (unit == 'h') then
		local s = string.sub(time, 1, -2)
		if (s == '1') then
			result = '1 hour'
		else
			result = s .. ' hourss'
		end
	else
		result = time
	end

	if si > 1 then
		result = string.sub(text, 1, si-1) .. result
	end
	if ei < string.len(text) then
		result = result .. string.sub(text, ei+1)
	end
	
	return result 
end

function p.checkParam(text)
	if (text == nil) or (text and (text == '')) then
		return nil
	end
	return mw.text.trim(text)
end

function p.test1()
	local frame = mw.getCurrentFrame()
	frame.args.template = 'Status/Simple'
	frame.args.name = 'ATK Boosted'
	frame.args.time = '180s'
	frame.args.amount = '70%'
	frame.args.probability = ''
	return p.format(frame)
end

function p.test2()
	local frame = mw.getCurrentFrame()
	frame.args.template = 'Status/Simple'
	frame.args.name = '90% Cut'
	return p.format(frame)
end

function p.test3()
	local frame = mw.getCurrentFrame()
	frame.args.template = 'Status/Simple'
	frame.args.name = 'Critical Hit Rate Boosted'
	frame.args.time = '180s'
	frame.args.amount = '70%'
	frame.args.probability = '90%'
	return p.format(frame)
end

function p.test4()
	local frame = mw.getCurrentFrame()
	frame.args.name = 'Critical Hit Rate Boosted'
	frame.args.time = '180s'
	frame.args.amount = '70%'
	frame.args.probability = '90%'
	return p.format2(frame)
end

function p.test5()
	return p._format2({
		name = 'Counters on DMG',
		a = '100%',
		t = '3T',
		c = 'Counter attacks grant 4% charge bar per hit (includes Recklessness Incarnate).',
		hide = 'wiki_comment',
	})
end

function p.test6()
	return p._format2({
		name = 'Counters on DMG',
		a = '100%',
		t = '1.5T',
		c = 'Counter attacks grant 4% charge bar per hit (includes Recklessness Incarnate).',
		hide = 'wiki_comment',
	})
end

function p.test7()
	return p._format2({
		name = 'Counters on DMG',
		a = '100%',
		t = '1T',
		c = 'Counter attacks grant 4% charge bar per hit (includes Recklessness Incarnate).',
		hide = 'wiki_comment',
	})
end

function p.test8()
	return p._format2({
		name = 'Counters on DMG',
		a = '100%',
		t = '1T',
		n = '1',
		hide = 'wiki_comment',
	}) .. p._format2({
		name = 'Counters on DMG',
		a = '100%',
		t = '1T',
		n = '17',
		hide = 'wiki_comment',
	})
end

function p.test9()
	return p._format2({
		wiki_name = 'Shield',
		name = 'Shielded',
		a = '',
		t = '3.5T',
	})
end

function p.test10()
	return p._format2({
		wiki_name = '%a% ATK Up (Stackable%am%)',
		name = 'ATK Boosted (Stackable)',
		a = '15%',
		am = '45%',
		t = '3.5T',
	})
end

function p.test11()
	return p._format2({
		wiki_name = 'Shield',
		name = 'Shielded',
		a = '10,900',
		t = '3.5T',
	})
end

function p.test12()
	return p._format2({
		wiki_name = 'DMG Mitigation',
		name = 'DMG Mitigation',
		a = '8,900',
		t = '2.5T',
	})
end

return p