Module:Weapon

From Granblue Fantasy Wiki
Jump to navigation Jump to search

Tests[edit source]

☑Y All tests passed.

Name Expected Actual
☑Y test_renderObtain

local p = {}
-- require('Module:Weapon/testcases').run()
local getArgs = require('Module:Arguments').getArgs
local str = require('Module:String')
local util = require('Module:Util')
local htmlparser = require('Module:HtmlParser')

function p.formatLink(link)
	if string.find(link, '[[', 1, true) ~= nil then
		return link
	elseif string.find(link, '<a', 1, true) ~= nil then
		return link
	else
		return '[[' .. link .. ']]'
	end
end

function p.lineBreak(text)
	local result = text
	-- replace "%s/%s" of root level text nodes with "<br />"
	local tmp = '<root>' .. text .. '</root>'
	local root = htmlparser.parse_html(tmp)
	local children = root[1]['childNodes']
	for _,e in ipairs(children) do
		if e['tagName'] == 'textNode' then
			local value = e['value']

			local fake_frame = { args = {
				['source'] = value,
				['pattern'] = '%s/%s',
				['replace'] = '<br />',
				['plain'] = 'False'
			} }
			local new_value = str.replace(fake_frame)
			
			fake_frame = { args = {
				['source'] = result,
				['pattern'] = value,
				['replace'] = new_value,
				['plain'] = 'True'
			} }
			result = str.replace(fake_frame)
		end
	end
	return result
end

function p._renderObtain(args)
	local lang = mw.getContentLanguage()
	local obtain = mw.text.trim(args['obtain'] or '')
	local obtain_text = mw.text.trim(args['obtain_text'] or '')
	local limit = mw.text.trim(args['limit'] or 999)
	local release_date = mw.text.trim(args['release_date'] or '')
	local format = mw.text.trim(args['format'] or '')
	local result = {}

	if obtain == '' then
		if format == 'div' then
			return '<div>' .. obtain_text .. '</div>'
		else
			return obtain_text
		end
	end
	
	local obtains = mw.text.split(obtain, ';')
	for _,line in ipairs(obtains) do
		line = mw.text.trim(line)
		local parts = mw.text.split(line, ',')
		local maintype = parts[1]
		
		if maintype == 'arcarum' then
			table.insert(result, '[[File:Location Arcarum.png|56px|link=Arcarum]] [[Arcarum]]')
		elseif maintype == 'event' then
			local type = parts[2] or ''
			if type == 'rotb' then
				table.remove(parts, 2)
				table.remove(parts, 1)
				local link = table.concat(parts, ',')
				if (string.len(link) > 0) then table.insert(result, p.formatLink(link))
				else table.insert(result, '[[Rise of the Beasts]]')
				end
			elseif type == 'xeno' then table.insert(result, p.formatLink(parts[3]))
			end
		elseif maintype == 'premium' then
			local type = parts[2] or ''
			if type == 'normal' then 
				table.insert(result, '[[Premium Draw]]')
				table.insert(result, '[[Surprise Ticket]]')
			elseif type == 'gala' then 
				local subtype = parts[3] or ''
				if subtype == 'normal' then table.insert(result, '[[Premium Gala]]')
				elseif subtype == 'flash' then table.insert(result, '[[Flash Gala]]')
				end
			elseif type == 'summer' then table.insert(result, '[[Summer Premium Draw]]')
			elseif type == 'halloween' then table.insert(result, '[[Halloween Premium Draw]]')
			elseif type == 'holiday' then table.insert(result, '[[Holiday Premium Draw]]')
			elseif type == 'valentine' then table.insert(result, '[[Valentine Premium Draw]]')
			elseif type == 'ticket' then table.insert(result, '[[Surprise Ticket]]')
			elseif type == 'zodiac' then 
				local year = parts[3] or '????'
				table.insert(result, '[[Premium Gala]] ('..year..' Zodiac Weapon)')
			elseif type == 'special' then 
				local link = parts[3] or ''
				if link ~= '' then table.insert(result, p.formatLink(link)) end
			end
		elseif maintype == 'raid' then
			local chapter = parts[2]
			table.remove(parts, 2)
			table.remove(parts, 1)
			local link = table.concat(parts, ',')
			if (string.len(link) > 0) then 
				link = p.formatLink(link)
				if string.len(chapter) > 0 then link = '{{location|'..chapter..'|icon}}'..' '..link end
				table.insert(result, link)
			end
		elseif maintype == 'shop' then
			local type = parts[2] or ''
			if type == 'pendants' then
				-- pendands,[type],[cost]
				local subtype = parts[3] or ''
				local count = parts[4] or '??'
				if (subtype == 'renown') or (subtype == 'prestige') then
					subtype = lang:ucfirst(subtype)
					table.insert(result, '{{itm|'..subtype..' Pendant,'..count..'|png}}')
				end
			elseif type == 'quest items' then
				-- quest items,[category],[subcategory],[items]
				local category = parts[3] or ''
				local subcategory = parts[4] or ''
				table.remove(parts, 4)
				table.remove(parts, 3)
				table.remove(parts, 2)
				table.remove(parts, 1)
				local items = table.concat(parts, ',')
				local link = '[[Shop#Quest_Items|Treasure Trade]]'
				if (subcategory == 'Primal Weapons') or (subcategory == 'Beast Weapons') or
					 (subcategory == 'Epic Weapons') or (subcategory == 'Cosmos Weapons') or
					 (subcategory == 'Beast Weapons') or (subcategory == 'Rose Weapons') or
					 (subcategory == 'Relics') or (subcategory == 'Hollowsky Weapons') or
					 (subcategory == 'Dark Opus Weapons') then 
					link = link .. ' ([[Shop#'..subcategory..'|'..subcategory..']])'
				elseif subcategory == 'Upgraders' then 
					link = link .. ' ([[Shop#Misc_Weapons|Upgraders]])'
				end
				if string.len(items) > 0 then
					link = '{{tt|1='..link..'|2=<div style="text-align:left;>Required treasure:<br />'..items..'</div>}}'
				end
				table.insert(result, link)
			elseif type == 'trade moons' then
				-- trade moons,[type],[cost]
				local subtype = parts[3] or ''
				local count = parts[4] or '??'
				if (subtype == 'bronze') or (subtype == 'silver') or (subtype == 'gold') then 
					subtype = lang:ucfirst(subtype)
					table.insert(result, '{{itm|'..subtype..' Moon,'..count..'}}')
				end
			end
		end
	end

	for index,row in ipairs(result) do
		if (row == '[[Surprise Ticket]]') and (string.len(release_date) > 0) then
			local release_time = mw.text.split(release_date, '-')
			release_time = os.time{year=release_time[1],month=release_time[2],day=release_time[3]}
			local now_time = os.time(os.date("!*t"))
			local diff = now_time - release_time
			if diff < (140 * 24 * 60 * 60) then
				result[index] = '{{tt|1='..row..' <span style="color:red;">(!)</span>|2=This is a recently released weapon and may not yet be available via [[Surprise Ticket]]s.}}'
			end
		end
	end
	
	if table.getn(result) > 0 then
		if format == 'list' then
			return '<li>'..table.concat(result, '</li><li>')..'</li>'
		elseif format == 'div' then
			return '<div class="obtain-list-item">'..table.concat(result, '</div><div class="obtain-list-item">')..'</div>'
		else
			return table.concat(result, '<br />')
		end
elseif string.len(obtain_text) > 0 then
		if format == 'div' then
			return '<div>' .. obtain_text .. '</div>'
		else
			return obtain_text
		end
	else
		return obtain
	end
end

function p.renderObtain(frame)
	local args = getArgs(frame)	
	return frame:preprocess(p._renderObtain(args))
end

function p.renderOugi(frame)
	local args = getArgs(frame)
	local text = args['text'] or ''
	return p.lineBreak(text)
end

function p.renderSkill(frame)
	local args = getArgs(frame)
	local text = args['text'] or ''
	return p.lineBreak(text)
end

function p.renderList(frame)
	mw.log('render start: ' .. os.clock())
	local filter = {}
	for ix,fil in pairs({'category','rarity','element','weapon','max_evo'}) do
		if frame.args[fil] then
			local text, matches = string.gsub(frame.args[fil], ',', '\|')
			filter[fil] = text
		end
	end

	local fields = {
		'%PAGE%',
		'id'
	}
	if frame.args.fields ~= nil then
		for w in (frame.args.fields):gmatch('[^,]+') do
			table.insert(fields, w)
		end
	end

	mw.log('load data: ' .. os.clock())
	local weapons = p.fetchWeapons(frame, filter, fields)
	
	mw.log('expanding templates: ' .. os.clock())
	local result = {}
	local template = frame.args.template
	for key,weapon in pairs(weapons) do
		table.insert(result, frame:expandTemplate{title = template, args = weapon})
	end
	mw.log('done: ' .. os.clock())
	return table.concat(result, '')
end

function p.fetchWeapons(frame, filter, fields)
	local argList = fields
	local params = {
		category = 'Weapons',
		notcategory = 'Templates',
		notnamespace = 'Template|User',
		include = '{Weapon}:' .. table.concat(argList, ':'),
		mode = 'userformat',
		secseparators = '====',
		multisecseparators = '===='
	}
	if (filter ~= nil) then
		local match = {}
		for key,part in pairs(filter) do
			if key == 'category' then
				params['category'] = part
			else
				table.insert(match, key..'\\s*=\\s*'..part)
			end
		end
		if #match > 0 then
			params.includematch = '/^(?=.*\\b' .. table.concat(match, '\\b)(?=.*\\b') .. '\\b).*$/is'
		end
	end
	mw.logObject(params)
	local data = frame:callParserFunction('#dpl:', params)
	argList[1] = 'link'
	return p.indexDPLTable(frame, argList, data)
end

function p.indexDPLTable(frame, argList, data)
	local result = {}
	local ix = 0
	local rows = util.string.split(data, '====')
	for rowix,row in pairs(rows) do
		local args = {}
		local i = 0
		local sargs = util.string.split(row, '\n|')
		for argix,arg in pairs(sargs) do
			i = i+1
			if arg ~= '' then
				local key = argList[i]
				args[key] = arg
			end
		end
		if args.link ~= nil then
			ix = ix + 1
			args.ix = ix
			
			-- some housework
			if (args.id ~= nil) then
				-- rarity .. type .. seq
				local id_type = string.sub(args.id, 4, 5)
				if (id_type == '99') then
					id_type = 'B'
				else
					id_type = string.sub(args.id, 5, 5)
				end
				args.short_id = string.sub(args.id, 3, 3) .. id_type .. string.sub(args.id, 6, 8)
			else
				args.short_id = nil
			end
			table.insert(result, args)
		end
	end

	table.sort(result, function(a,b) 
		return a.link < b.link
	end)

	return result
end

function p.test()
	mw.log(os.clock())
	local frame = mw.getCurrentFrame()
	mw.log(os.clock())
	local items = p.fetchWeapons(frame, { rarity = 'ssr' }, { '%PAGE%','id','rarity','element','weapon'})
	mw.log(os.clock())
	local count = 0
    for k,v in pairs(items) do 
    	mw.logObject(v)
    	--mw.log(v.id .. ' ' .. v.short_id .. ' ' .. v.link)
    	count = count + 1 
    end
	mw.log(os.clock())
    return count
end

function p.testLineBreak()
	return p.lineBreak([[
		Massive Wind damage to a foe. / All allies 
		gain <span class="image_link"><div class="tooltip" style="border-bottom: 0;">
		<a href="/Status_Effects#Wind_ATK_Boosted" title="Status Effects#Wind ATK Boosted">
		<img alt="Status WindAtkUp.png" src="/images/thumb/4/4d/Status_WindAtkUp.png/25px-Status_WindAtkUp.png" width="25" height="25" srcset="/images/thumb/4/4d/Status_WindAtkUp.png/38px-Status_WindAtkUp.png 1.5x, /images/thumb/4/4d/Status_WindAtkUp.png/50px-Status_WindAtkUp.png 2x" /></a>
		<a href="/Status_Effects#Wind_ATK_Boosted" title="Status Effects">25% Wind ATK Up</a>
		<span class="tooltiptext" style="">Wind ATK is boosted<br /><hr />
		<strong>Strength</strong>: 25%<hr /><strong>Duration</strong>: 3.5 turns<hr />
		<a href="/Half_Turns" class="mw-redirect" title="Half Turns">Applied during the attack phase.</a><br />
		On the next turn, it\'ll have 3 turns remaining.</span></div></span>.<br />
		<span class="skill-upgrade-text">Additional effect at 4★:</span><br />
		Gain <span class="image_link"><div class="tooltip" style="border-bottom: 0;">
		<a href="/Status_Effects#Critical_Hit_Rate_Boosted" title="Status Effects#Critical Hit Rate Boosted">
		<img alt="Status CriticalUp.png" src="/images/thumb/a/a2/Status_CriticalUp.png/25px-Status_CriticalUp.png" width="25" height="25" srcset="/images/thumb/a/a2/Status_CriticalUp.png/38px-Status_CriticalUp.png 1.5x, /images/thumb/a/a2/Status_CriticalUp.png/50px-Status_CriticalUp.png 2x" /></a>
		<a href="/Status_Effects#Critical_Hit_Rate_Boosted" title="Status Effects">
		??% / 30% Critical Hit Rate Up</a><span class="tooltiptext" style="">
		DMG is slightly boosted for critical hits<br /><hr /><strong>Strength</strong>:
		&#160;??% chance of dealing 30% more damage.<hr /><strong>Duration</strong>: 3.5 turns<hr />
		<a href="/Half_Turns" class="mw-redirect" title="Half Turns">Applied during the attack phase.</a><br />
		On the next turn, it\'ll have 3 turns remaining.</span></div></span>.
		<sup id="cite_ref-Hecate_mk2_Orleans_1-1" class="reference">
		<a href="#cite_note-Hecate_mk2_Orleans-1">&#91;1&#93;</a></sup>
	]])
end

function p.testObtain()
	mw.log(p._renderObtain({
		['obtain'] = '',
		['obtain_text'] = ''
	}))

	mw.log(p._renderObtain({
		['obtain'] = 'event,xeno,Xeno Vohu Manah Clash',
		['obtain_text'] = '[[Xeno Vohu Manah Clash]]'
	}))

	mw.log(p._renderObtain({
		['obtain'] = 'event,xeno,Xeno Vohu Manah Clash',
		['obtain_text'] = ''
	}))

	mw.log(p._renderObtain({
		['obtain'] = '',
		['obtain_text'] = '[[Xeno Vohu Manah Clash]]'
	}))

	mw.log(p._renderObtain({
		['obtain'] = 'premium,zodiac,2015',
		['obtain_text'] = '',
		['limit'] = 1
	}))

	mw.log(p._renderObtain({
		['obtain'] = 'premium,zodiac,2015;premium,valentine;premium,gala,normal;premium,gala,flash;premium,special,[[Premium Draw|Christmas Holiday Draw Set]]',
		['obtain_text'] = ''
	}))

	mw.log(p._renderObtain({
		['obtain'] = '',
		['obtain_text'] = '[[Premium Draw]]<br />[[Rise of the Beasts|Qinglong Showdown Extreme]]'
	}))

	mw.log(p._renderObtain({
		['obtain'] = '[[Premium Draw]]<br />[[Rise of the Beasts|Qinglong Showdown Extreme]]',
		['obtain_text'] = ''
	}))
end

return p