-The Dream Cancel Wiki has successfully upgraded it's wiki software and editing has returned. Tables for data have returned.

Module:Parser

From Dream Cancel Wiki
Revision as of 21:15, 4 September 2022 by Franck Frost (talk | contribs)
Jump to navigation Jump to search

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

local p = {}
local cargo = mw.ext.cargo

-- Color frame advantage text based on value (positive or negative)
function p.colorizeFrameAdvantage(frame)
	-- Set the string to the first argument given
	local str = frame.args[1]
	
	-- Surrond all plus values with a span with style color green
	str = string.gsub(str, "(%+%d+)", "<span style=\"color:green;\">%1</span>")
	-- Surrond all negative values with a span with style color green
	str = string.gsub(str, "(%-%d+)", "<span style=\"color:red;\">%1</span>")
	
	-- Return the string
	return str
end

-- Create a wikitext hyperlink that links to the frame data page section of a move
-- Assumes the section header is the same as moveInput
function p.createFrameDataLink(frame)
	-- Target cargo table to query
	local cargoTable = frame.args['cargoTable']
	-- MoveId to query
	local moveId = frame.args['moveId']
	-- Field in to return in query
	local fields = "input"
	-- Args for query
	local args = { where = "moveId = '" .. moveId .. "'"}
	
	-- Beginning of wikitext to return
	local wikitext = "[[/Data#"
	
	-- Cargo query for moveInput
	local results = cargo.query( cargoTable, fields, args )
	local moveInput = results[1]['input']
	
	-- Replaces any left and right square brackets with their ascii code to make it wikitext compatible
	moveInput = string.gsub(moveInput, '%[', "&#91;")
	moveInput = string.gsub(moveInput, "%]", "&#93;")
	
	-- TODO: Maybe add alternate text instead of it just saying '/Data#moveInput'?
	-- Concatenates 'wikitext' with 'moveInput' and ending square brackets
	wikitext = wikitext .. moveInput .. "]]"
	
	-- Returns the wikitext
	return wikitext 
end

-- TODO: I might delete this one? I dunno. I'll take a look at it again later. Not going to bother comment it for the time being as it's not really being used except for displaying on the frame data page, which I might change?
-- Take a list of comma separated files names and transcribe them to wikitext file markup
function p.parseImages(frame)
	local listOfFiles = frame.args[1] -- List of files
	local extraParameters = frame.args[2] -- Extra parameters to use in each file
	local addLineBreaks = frame.args['addLineBreaks'] -- Add extra line breaks if set to "true"
	local lineBreak = ""
	local wikitextFiles = ""
	
	if addLineBreaks == "true" then lineBreak = "<br>" end
	
	if listOfFiles and string.len(listOfFiles) > 0 then
		if extraParameters and string.len(extraParameters) > 0 then
			extraParameters = "|" .. string.gsub(extraParameters, ",", "|")
		else
			extraParameters = ""
		end
		
		-- Replaces any left and right square brackets with their ascii code to make it wikitext compatible
		wikitextFiles = string.gsub(listOfFiles, '%[', "&#91;")
		wikitextFiles = string.gsub(listOfFiles, '%]', "&#93;")
		wikitextFiles = string.gsub(listOfFiles,",", extraParameters .. "]]" .. lineBreak .. "[[File:")
		wikitextFiles = "[[File:" .. wikitextFiles .. extraParameters .. "]]"
	end
	
	return wikitextFiles
end

-- Return string 's' with all leading and trailing spaces trimmed away
function trim(s)
	if s then
		return (string.gsub(s, "^%s*(.-)%s*$", "%1"))
	else
		return s
	end
end

-- Franck made this to take a list of items separated by a delimiter and return a specific one (or all)
-- The default delimiter is a comma to parse through moveIds
function p.parseMoves(frame)
	local moves = frame.args[1]
	local s = frame.args[2]
	local delimiter = frame.args['delimiter'] or ","
	local listOfMoves = split(moves,delimiter)
	local result = ""
	if s then
		result = result .. trim(listOfMoves[s+0])
	else
		for r = 1, #listOfMoves do
			result = result .. trim(listOfMoves[r])
		end
	end
	return result
end

-- Franck edited this function to also get hitboxes and to take into account display parameters
-- Cargo queries a comma separated list of moveIds and returns their images or hitboxes as wikitext
function p.parseImagesQueryF(frame)
	-- Target cargo table to query
	local cargoTable = frame.args[1]
	-- List of moves queried by their arbitrary moveId
	local moves = frame.args[2]
	-- Choice between querying images or hitboxes
	local type = frame.args[3]
	-- Display parameters to use in each file
	local display = frame.args['display']
	-- List of captions entered by the user on function call
	-- In other words, these captions are not queried from cargos
	local captions = frame.args['captions']
	-- Wikitext to return after function is completed
	local wikitext = ""
	-- List of moves as a table
	local listOfMoves = {}
	-- List of captions as a table
	local listOfCaptions = {}
	
	if display and string.len(display) > 0 then
		display = "|" .. string.gsub(display, ",", "|") .. "]]"
	else
		display = "|175x250px|center]]"
	end

	-- Assign listOfMoves with the values 'moves' split by the delimiter ','
	listOfMoves = split(moves,",")
	-- If captions exist
	if captions then
		-- Assign listOfCaptions with the values 'captions' split by the delimiter ','
		listOfCaptions = split(captions, ",")
	end
	
	-- 'where' part of the cargo query
	local whereQuery = ""
	-- boolean just to track if it's the first where argument and if we should add ' OR ' to the query
	local firstWhere = true
	
	-- Iterate through every value in 'listOfMoves'
	for i,v in ipairs(listOfMoves) do
		-- If it's the first where argument, don't prefix with ' OR '
		if firstWhere then
			firstWhere = false
		else
			whereQuery = whereQuery .. " OR "
		end
		
		-- Concatenate the current whereQuery with the current moveId
		whereQuery = whereQuery .. "moveId = '" .. trim(v) .. "'"
	end

	-- 'fields' part of the cargo query
	local fields = ""
	-- We need to grab only the first instance of each image/hitbox sorted by orderId
	if type == "images" then fields = "images, MIN(orderId)=sort" end
	if type == "hitboxes" then fields = "hitboxes, MIN(orderId)=sort" end
	
	-- 'args' part of the Lua cargo query
	local args = {
		-- 'whereQuery' from above which goes by moveIds
		where = whereQuery,
		-- 'group by' is used to remove duplicate images from this query
		groupBy = type,
		-- 'order by' goes off of the first instance based on moveId
		orderBy = "sort"
	}

	-- Cargo query
	local results = cargo.query( cargoTable, fields, args )

	-- Iterate through results
	for r = 1, #results do
		-- The item at the current index
		local result = results[r]
		
		-- The caption to add below the image
		local caption = ""
		
		-- If the caption exists
		if listOfCaptions[r] and string.len(listOfCaptions[r]) >0 then
			-- Then assign 'caption' in MoveData format
			caption = "<span class=caption><small>" .. trim(listOfCaptions[r]) .. "</small></span>"
		end

		-- Add this item's image/hitbox and caption to the wikitext
		wikitext = wikitext .. "<div>[[File:" .. result[type] .. display .. caption .. "</div>"
	end

	-- Return the wikitext
	return wikitext
end

-- Cargo queries a comma separated list of moveIds and returns their images as wikitext
function p.parseImagesQuery(frame)
	-- Target cargo table to query
	local cargoTable = frame.args['cargoTable']
	-- List of moves queried by their arbitrary moveId
	local moves = frame.args['moveIds']
	-- List of captions entered by the user on function call
	-- In other words, these captions are not queried from cargos
	local captions = frame.args['captions']
	-- TODO: Figure out how I want to implement hitbox images
	--local listOfHitboxCaptions = frame.args['hitboxCaptions']
	-- Wikitext to return after function is completed
	local wikitext = ""
	-- List of moves as a table
	local listOfMoves = {}
	-- List of captions as a table
	local listOfCaptions = {}
	
	-- Assign listOfMoves with the values 'moves' split by the delimiter ','
	listOfMoves = split(moves,",")
	-- If captions exist
	if captions then
		-- Assign listOfCaptions with the values 'captions' split by the delimiter ','
		listOfCaptions = split(captions, ",")
	end
	
	-- 'where' part of the cargo query
	local whereQuery = ""
	-- boolean just to track if it's the first where argument and if we should add ' OR ' to the query
	local firstWhere = true
	
	-- Iterate through every value in 'listOfMoves'
	for i,v in ipairs(listOfMoves) do
		-- If it's the first where argument, don't prefix with ' OR '
		if firstWhere then
			firstWhere = false
		else
			whereQuery = whereQuery .. " OR "
		end
		
		-- Concatenate the current whereQuery with the current moveId
		whereQuery = whereQuery .. "moveId = '" .. trim(v) .. "'"
	end
	
	-- 'fields' part of the cargo query
	-- We need to grab only the first instance of each image "sorted" by moveId
	local fields = "images, MIN(moveId)=sort"
	
	-- 'args' part of the Lua cargo query
	local args = {
		-- 'whereQuery' from above which goes by moveIds
		where = whereQuery,
		-- 'order by' goes off of the first instance based on moveId
		orderBy = "sort",
		-- 'group by' is used to remove duplicate images from this query
		groupBy = "images"
	}
	
	-- Cargo query
	local results = cargo.query( cargoTable, fields, args )
	
	-- Iterate through results
	for r = 1, #results do
		-- The item at the current index
		local result = results[r]
		
		-- The caption to add below the image
		local caption = ""
		
		-- If the caption exists
		if listOfCaptions[r] and string.len(listOfCaptions[r]) >0 then
			-- Then assign 'caption'
			caption = "<br>" .. trim(listOfCaptions[r])
		end
		
		-- Add this item's image and caption to the wikitext
		wikitext = wikitext .. "<div>[[File:" .. result.images .. "|175px]]" .. caption .. "</div>"
	end
	
	-- Return the wikitext
	return wikitext
end

-- Cargo queries for a list of comma separated moveIds
function p.parseFrameData(frame)
	-- Target cargo table to query
	local cargoTable = frame.args['cargoTable']
	-- 'fields' part of the cargo query
	local fields = frame.args['fields']
	-- moveIds to query
	local moveIds = frame.args['moveIds']
	-- template to use for the result of this query
	local template = frame.args['template']
	-- descriptions to display below template
	local descriptions = frame.args['descriptions']
	
	-- A table made from splitting 'moveIds' at every ','
	local listOfMoves = split(moveIds, ",")
	-- A table made from splitting 'fields' at every ','
	local listOfFields = split(fields,",")
	-- A table made from splitting 'descriptions' at every ';'
	local listOfDescriptions = {}
	
	-- If descriptions exist
	if descriptions then
		-- Assign listOfDescriptions with the values 'descriptions' split by the delimiter ';'
		listOfDescriptions = split(descriptions, ";")
	end
	
	-- Wikitext to return at the end
	local wikitext = ""
	
	-- 'where' part of the cargo query
	local whereQuery = ""
	-- boolean just to track if it's the first where argument and if we should add ' OR ' to the query
	local firstWhere = true
	
	-- Iterate through every value in 'listOfMoves'
	for i,v in ipairs(listOfMoves) do
		-- If it's the first where argument, don't prefix with ' OR '
		if firstWhere then
			firstWhere = false
		else
			whereQuery = whereQuery .. " OR "
		end
		
		-- Concatenate the current whereQuery with the current moveId
		whereQuery = whereQuery .. "moveId = '" .. trim(v) .. "'"
	end
	
	-- 'args' portion of cargo query
	local args = {
		-- 'whereQuery' from above
		where = whereQuery,
		-- 'order by' moveId
		orderBy = "moveId"
	}
	-- Cargo query
	local results = cargo.query( cargoTable, fields, args )
	
	-- Iterate through all results of the query to put them into the target template
	for r = 1, #results do
		-- Arguments for the template
		local arguments = {}
		
		-- Iterate through all values in listOfFields
		for j,k in ipairs(listOfFields) do
			-- Remove all leading and trailing spaces in the value
			local item = trim(k)
			-- Add the passed in arguments of the current move into arguments
			arguments[item] = results[r][item]
		end
		
		-- If the a description exists
		if listOfDescriptions[r] and string.len(listOfDescriptions[r]) >0 then
			-- Then assign the argument of index 'descriptions'
			arguments['descriptions'] = trim(listOfDescriptions[r])
		end
		
		-- Add the move expanded into the target template into the wikitext
		wikitext = wikitext .. frame:expandTemplate{ title = template, args = arguments }
	end
	
	-- Return the wikitext
	return wikitext
end

-- Split string 's' at every 'delimiter' into an array
function split(s, delimiter)
	-- Array to return
	local result = {}
	
	-- Iterate through the string finding every section split with delimiter
	for match in (s..delimiter):gmatch("(.-)"..delimiter) do
		-- Add the match to the result array
		table.insert(result, match)
	end
	
	-- Return the result
	return result
end

-- Split string 's' at every 'delimiter' into an array
function splitKeepDelimiter(s, delimiter)
	-- Array to return
	local result = {}
	
	-- Boolean to check if this is the firstSplit (only applicable when keeping delimiter)
	local firstSplit = true
	
	-- Iterate through the string finding every section split with delimiter
	for match in (s..delimiter):gmatch("(.-)"..delimiter) do
		-- Don't add the delimiter until after the first split
		if firstSplit then
			firstSplit = false
		else
			table.insert(result, delimiter)
		end
		
		-- Remove a '%' character if it's leftover from a delimiter
		local matchWithEscapedRemoved = match
		if string.find(string.sub(match,-1, string.len(match)),"%%") then
			mw.log(match)
			matchWithEscapedRemoved = string.sub(match, 1, -2)
		end
		
		-- Add the match to the result array
		table.insert(result, matchWithEscapedRemoved)
	end
	
	-- Return the result
	return result
end
	
-- List of valid inputs to be used for p.inputsToIcons
local validInputs = {
	-- Connectors
	["~"]	= "[[File:UNI_Input_Follow_up.png|x22px|~]]", -- Follow-up
	["%+"]	= "[[File:UNI_Input_Plus.png|x22px|+]]", -- Plus
	["/"]	= "[[File:UNI_Input_Or.png|x22px|/]]", -- Or
	
	-- Motions
	["360"]	= "[[File:UNI_Input_360.png|x22px|360]]",
	["41236"]	= "[[File:UNI_Input_4.png|x22px|4]][[File:UNI_Input_1.png|x22px|1]][[File:UNI_Input_2.png|x22px|2]][[File:UNI_Input_3.png|x22px|3]][[File:UNI_Input_6.png|x22px|6]]",
	["63214"]	= "[[File:UNI_Input_6.png|x22px|6]][[File:UNI_Input_3.png|x22px|3]][[File:UNI_Input_2.png|x22px|2]][[File:UNI_Input_1.png|x22px|1]][[File:UNI_Input_4.png|x22px|4]]",
	["236"]		= "[[File:UNI_Input_2.png|x22px|2]][[File:UNI_Input_3.png|x22px|3]][[File:UNI_Input_6.png|x22px|6]]",
	["214"]		= "[[File:UNI_Input_2.png|x22px|2]][[File:UNI_Input_1.png|x22px|1]][[File:UNI_Input_4.png|x22px|4]]",
	["623"]		= "[[File:UNI_Input_6.png|x22px|6]][[File:UNI_Input_2.png|x22px|2]][[File:UNI_Input_3.png|x22px|3]]",
	["421"]		= "[[File:UNI_Input_4.png|x22px|4]][[File:UNI_Input_2.png|x22px|2]][[File:UNI_Input_1.png|x22px|1]]",
	["22"]		= "[[File:UNI_Input_2.png|x22px|2]][[File:UNI_Input_2.png|x22px|2]]",
	["66"]		= "[[File:UNI_Input_66.png|x22px|66]]",
	["[4]6"]	= "[[File:UNI_Input_Hold.png|x22px|]][[File:UNI_Input_4.png|x22px|[4]]][[File:UNI_Input_Then.png|x22px|]][[File:UNI_Input_6.png|x22px|6]]",
	["[6]4"]	= "[[File:UNI_Input_Hold.png|x22px|]][[File:UNI_Input_6.png|x22px|[6]]][[File:UNI_Input_Then.png|x22px|]][[File:UNI_Input_4.png|x22px|4]]",
	["[2]8"]	= "[[File:UNI_Input_Hold.png|x22px|]][[File:UNI_Input_2.png|x22px|[2]]][[File:UNI_Input_Then.png|x22px|]][[File:UNI_Input_8.png|x22px|8]]",
	["[8]2"]	= "[[File:UNI_Input_Hold.png|x22px|]][[File:UNI_Input_8.png|x22px|[8]]][[File:UNI_Input_Then.png|x22px|]][[File:UNI_Input_2.png|x22px|2]]",
	
	-- Directions
	["5"]	= "", -- Neutral (No image needed)
	["6"]	= "[[File:UNI_Input_6.png|x22px|6]]", -- Forward
	["3"]	= "[[File:UNI_Input_3.png|x22px|3]]", -- Down-foward
	["2"]	= "[[File:UNI_Input_2.png|x22px|2]]", -- Down
	["1"]	= "[[File:UNI_Input_1.png|x22px|1]]", -- Down-back
	["4"]	= "[[File:UNI_Input_4.png|x22px|4]]", -- Back
	["9"]	= "[[File:UNI_Input_9.png|x22px|9]]", -- Up-Forward
	["8"]	= "[[File:UNI_Input_8.png|x22px|8]]", -- Up
	["7"]	= "[[File:UNI_Input_7.png|x22px|7]]", -- Up-back
	
	-- Special Buttons
	["throw"]	= "[[File:UNI_Input_throw.png  |x22px|throw]]",
	["assault"]	= "[[File:UNI_Input_assault.png|x22px|assault]]",
	["cvo"]	= "[[File:UNI_Input_cvo.png|x22px|cvo]]", -- Cross cast veil off
	["cs"]	= "[[File:UNI_Input_cs.png |x22px|cs]]", -- Chain shift
	
	-- Special Notation
	["land"] = "[[File:UNI_Input_land.png|x22px|land]]", -- Land
	["jc"]   = "[[File:UNI_Input_jc.png  |x22px|jc]]", -- Jump cancel
	
	-- Prefixes
	["d"]	= "[[File:UNI_Input_Delay.png|x22px|d]]", -- Delay
	["j"]	= "[[File:UNI_Input_Jump.png |x22px|j]]", -- Jump
	["w"]	= "[[File:UNI_Input_Whiff.png|x22px|w]]", -- Whiff
	
	-- Buttons with '5' in alt text
	["5A"]	= "[[File:UNI_Input_A.png|x22px|5A]]",
	["5B"]	= "[[File:UNI_Input_B.png|x22px|5B]]",
	["5C"]	= "[[File:UNI_Input_C.png|x22px|5C]]",
	["5D"]	= "[[File:UNI_Input_D.png|x22px|5D]]",
	["5X"]	= "[[File:UNI_Input_X.png|x22px|5X]]",
	
	-- Buttons
	["A"]	= "[[File:UNI_Input_A.png |x22px|A]]",
	["B"]	= "[[File:UNI_Input_B.png |x22px|B]]",
	["C"]	= "[[File:UNI_Input_C.png |x22px|C]]",
	["D"]	= "[[File:UNI_Input_D.png |x22px|D]]",
	["X"]	= "[[File:UNI_Input_X.png |x22px|X]]",
	["vo"]	= "[[File:UNI_Input_vo.png|x22px|vo]]", -- Veil off
	["ff"]	= "[[File:UNI_Input_ff.png|x22px|ff]]", -- Force function
	-- Hold Buttons
	["[A]"]	= "[[File:UNI_Input_Hold.png|x22px|]][[File:UNI_Input_A.png|x22px|[A]]]",
	["[B]"]	= "[[File:UNI_Input_Hold.png|x22px|]][[File:UNI_Input_B.png|x22px|[B]]]",
	["[C]"]	= "[[File:UNI_Input_Hold.png|x22px|]][[File:UNI_Input_C.png|x22px|[C]]]",
	["[D]"]	= "[[File:UNI_Input_Hold.png|x22px|]][[File:UNI_Input_D.png|x22px|[D]]]",
	["[X]"]	= "[[File:UNI_Input_Hold.png|x22px|]][[File:UNI_Input_X.png|x22px|[X]]]",
	["[vo]"]	= "[[File:UNI_Input_vo_Hold.png|x22px|[vo]]]", -- Veil off
	["[ff]"]	= "[[File:UNI_Input_ff_Hold.png|x22px|[ff]]]", -- Force function
	-- Release Buttons
	["]A["]	= "[[File:UNI_Input_A_Release.png|x22px|]A[]]",
	["]B["]	= "[[File:UNI_Input_B_Release.png|x22px|]B[]]",
	["]C["]	= "[[File:UNI_Input_C_Release.png|x22px|]C[]]",
	["]D["]	= "[[File:UNI_Input_D_Release.png|x22px|]D[]]",
	["]X["]	= "[[File:UNI_Input_X_Release.png|x22px|]X[]]",
	["]vo["]	= "[[File:UNI_Input_vo_Release.png|x22px|]vo[]]", -- Veil off
	["]ff["]	= "[[File:UNI_Input_ff_Release.png|x22px|]ff[]]", -- Force function
	
	[" "]		= "[[File:UNI_Input_next.png|x22px|>]]" -- Next Input
}

-- Change a string of characters to a list of images in wikitext format (reference above table)
function p.inputsToIcons(frame)
	-- Get string
	local inputText = frame.args[1]
	local ret = ""
	
	-- Check if current inputText is matching with any validInputs
	local checkInputText = type(validInputs[inputText]) == "function" and validInputs[inputText]() or validInputs[inputText] or nil
	
	-- If it doesn't match, then keep checking down
	if checkInputText==nil then
		-- If text contains spaces, split it down
		if string.find(inputText, " ") then
			local inputTextAsList = splitKeepDelimiter(inputText, " ")
			for i,v in ipairs(inputTextAsList) do
				ret = ret..p.inputsToIcons({args={v}})
			end
		-- If text contains '~', split it down
		elseif string.find(inputText,"~") then
			local inputTextAsList = splitKeepDelimiter(inputText, "~")
			for i,v in ipairs(inputTextAsList) do
				ret = ret..p.inputsToIcons({args={v}})
			end
		-- If text contains '+', split it down
		elseif string.find(inputText,"%+") then
			local inputTextAsList = splitKeepDelimiter(inputText, "%+")
			for i,v in ipairs(inputTextAsList) do
				ret = ret..p.inputsToIcons({args={v}})
			end
		-- If text contains '/', split it down
		elseif string.find(inputText,"/") then
			local inputTextAsList = splitKeepDelimiter(inputText, "/")
			for i,v in ipairs(inputTextAsList) do
				ret = ret..p.inputsToIcons({args={v}})
			end
		-- If text contain a digit then a letter, then split at that point
		elseif string.find(inputText,"%d%a") then
			local frontEnd,backEnd = string.find(inputText,"%d%a")
			ret = ret..p.inputsToIcons({args={string.sub(inputText,1,frontEnd)}})..p.inputsToIcons({args={string.sub(inputText,backEnd)}})
		-- If text contain a digit then a left square bracket, then split at that point
		elseif string.find(inputText,"%d%[") then
			local frontEnd,backEnd = string.find(inputText,"%d%[")
			ret = ret..p.inputsToIcons({args={string.sub(inputText,1,frontEnd)}})..p.inputsToIcons({args={string.sub(inputText,backEnd)}})
		-- If text starts with d, j, w, A, B, C, D, then split after that first character
		elseif	string.sub(inputText,1,1) == "d" or
			string.sub(inputText,1,1) == "j" or
			string.sub(inputText,1,1) == "w" or
			string.sub(inputText,1,1) == "A" or
			string.sub(inputText,1,1) == "B" or
			string.sub(inputText,1,1) == "C" or
			string.sub(inputText,1,1) == "D" then
			ret = ret..p.inputsToIcons({args={string.sub(inputText,1,1)}})..p.inputsToIcons({args={string.sub(inputText,2)}})
		-- Otherwise, return the text in parenthesis (Which I don't want to happen, because this would look bad if parsed poorly!!!)
		else
			ret = "("..inputText..")"
		end
	else
		ret = checkInputText
	end
	
	-- Return the result
	return ret
end

return p