Permanently protected module

Difference between revisions of "Module:DM Fusion display"

From Yugipedia
Jump to: navigation, search
(At this level of the code, it doesn't make sense to have a query, so I'm moving it one level below. Also fixing ordering error caused by pairs (replaced with ipairs))
(Fixed (yet another) sorting issue regarding pairs (yet again fixed by using ipairs). Fixed combinations for a same Fusion not being grouped if all of them had no description)
 
(5 intermediate revisions by the same user not shown)
Line 137: Line 137:
 
-- The attributes that should be returned
 
-- The attributes that should be returned
 
local required_attributes = {  
 
local required_attributes = {  
"Fusion Material 1", "Fusion Material 2", "Fusion result", "Description", "Columns", -- Attributes about the Fusion combination
+
"Fusion Material 1", "Fusion Material 2", "Fusion result", "Description", "Columns",                                                                                                 -- Attributes about the Fusion combination
"Fusion Material 1.English name = English name 1", "Fusion Material 1.Level = Level 1", "Fusion Material 1.Card number = Card number 1", -- Attributes about each card listed as Material 1
+
"Fusion Material 1.English name = English name 1", "Fusion Material 1.Level = Level 1", "Fusion Material 1.Card number = Card number 1", "Fusion Material 1.Card type = Card type 1", -- Attributes about each card listed as Material 1
"Fusion Material 2.English name = English name 2", "Fusion Material 2.Level = Level 2", "Fusion Material 2.Card number = Card number 2", -- Attributes about each card listed as Material 2
+
"Fusion Material 2.English name = English name 2", "Fusion Material 2.Level = Level 2", "Fusion Material 2.Card number = Card number 2", "Fusion Material 2.Card type = Card type 2", -- Attributes about each card listed as Material 2
"Fusion result.English name = English name 3",    "Fusion result.Level = Level 3",    "Fusion result.Card number = Card number 3",    -- Attributes about each card listed as Result
+
"Fusion result.English name = English name 3",    "Fusion result.Level = Level 3",    "Fusion result.Card number = Card number 3",    "Fusion result.Card type = Card type 3",    -- Attributes about each card listed as Result
"Fusion result = Fusion Material 3" -- Small hack to help iterate below
+
"Fusion result = Fusion Material 3"                                               -- Small hack to help iterate below
 
}
 
}
 
 
 
local query = mw.smw.ask( conditions .. "|?" .. table.concat(required_attributes, "|?")) or {}
 
local query = mw.smw.ask( conditions .. "|?" .. table.concat(required_attributes, "|?")) or {}
 +
local nonMonsterOffset
 
 
 
-- Iterates over both Fusion Material lists, and adds each card to the table
 
-- Iterates over both Fusion Material lists, and adds each card to the table
 
for _, combination in ipairs(query) do
 
for _, combination in ipairs(query) do
 
for combi_index = 1,3 do
 
for combi_index = 1,3 do
-- Pre-processing on the multi-value attributes
+
-- MW doesn't return multiple-valued properties as tables, so we convert them here
 
materials = toTable(combination["Fusion Material " .. combi_index])
 
materials = toTable(combination["Fusion Material " .. combi_index])
 
en_names  = toTable(combination["English name " .. combi_index])
 
en_names  = toTable(combination["English name " .. combi_index])
 
levels    = toTable(combination["Level " .. combi_index])
 
levels    = toTable(combination["Level " .. combi_index])
 
numbers  = toTable(combination["Card number " .. combi_index])
 
numbers  = toTable(combination["Card number " .. combi_index])
 +
types    = toTable(combination["Card type " .. combi_index])
 
 
    for i, fm in pairs(materials) do
+
-- Since non-Monsters don't have Levels, the properties get returned with some lines missing for Monster-specific properties (e.g. Level)
 +
-- This variable is used to offset that
 +
nonMonsterOffset = 0
 +
 +
    for i, fm in ipairs(materials) do
 
    fm = unlink(fm)
 
    fm = unlink(fm)
 
    if not CARD_INFO[fm] then
 
    if not CARD_INFO[fm] then
 
    CARD_INFO[fm] = {
 
    CARD_INFO[fm] = {
 
    ["name"]  = en_names[i],
 
    ["name"]  = en_names[i],
    ["level"]  = levels[i],
+
    ["level"]  = levels[i-nonMonsterOffset],
 
    ["number"] = numbers[i]
 
    ["number"] = numbers[i]
 
    }
 
    }
 +
end
 +
if unlink(types[i]) ~= 'Monster Card' then
 +
    nonMonsterOffset = nonMonsterOffset + 1
 +
    CARD_INFO[fm]["level"] = nil
 
     end
 
     end
 
     end
 
     end
Line 184: Line 194:
 
end
 
end
 
end
 
end
materialIndex = materialIndex or 1
+
 +
-- Also checks the second list to see if the material is present there
 +
if materialIndex then
 +
for _, mat in ipairs(mats2) do
 +
if unlink(mat) == _pageName then
 +
materialIndex = materialIndex + 1
 +
break
 +
end
 +
end
 +
else
 +
materialIndex = 1
 +
end
 
 
 
-- Assigns correct material and exception list, based on the index found
 
-- Assigns correct material and exception list, based on the index found
if materialIndex == 1 then
+
-- Index 3 means that it was found in both lists
 +
if materialIndex == 1 or materialIndex == 3 then
 
mats = mats1
 
mats = mats1
 
for _, combi_ex in ipairs(_exceptionsQuery) do
 
for _, combi_ex in ipairs(_exceptionsQuery) do
Line 194: Line 216:
 
end
 
end
 
end
 
end
else
+
end
mats = mats2
+
 
 +
if materialIndex == 2 or materialIndex == 3 then
 +
if not mats then mats = mats2
 +
else
 +
-- Joins both tables
 +
for _, m2 in ipairs(mats2) do
 +
if unlink(m2) ~= _pageName then -- Don't include the same article twice
 +
table.insert(mats, m2)
 +
end
 +
end
 +
end
 +
 
for _, combi_ex in ipairs(_exceptionsQuery) do
 
for _, combi_ex in ipairs(_exceptionsQuery) do
 
if unlink(combi_ex["Fusion Material exception 2"]) == _pageName then
 
if unlink(combi_ex["Fusion Material exception 2"]) == _pageName then
Line 239: Line 272:
 
     currentTable = fillTableForFusionResult(currentTable, mats1, mats2, columns, combination_exceptions)
 
     currentTable = fillTableForFusionResult(currentTable, mats1, mats2, columns, combination_exceptions)
 
    
 
    
     previousDescription = description or previousDescription
+
     previousDescription = description
 
     end
 
     end
 
end
 
end
Line 246: Line 279:
 
-- @description Creates the table to be displayed for Fusion Material monsters
 
-- @description Creates the table to be displayed for Fusion Material monsters
 
local function createFusionMaterialTable()
 
local function createFusionMaterialTable()
local previousDescription, currentTable
+
local previousDescription, previousResult, currentTable
 
      
 
      
 
     -- Creates tables/rows for each combination found
 
     -- Creates tables/rows for each combination found
Line 253: Line 286:
 
     combination[1], combination["Fusion Material 1"], combination["Fusion Material 2"], combination["Fusion result"], combination["Description"], combination["Columns"] or 1
 
     combination[1], combination["Fusion Material 1"], combination["Fusion Material 2"], combination["Fusion result"], combination["Description"], combination["Columns"] or 1
 
     local cur_combi_exceptions = {}
 
     local cur_combi_exceptions = {}
    mw.log(result)
+
 
   
 
 
     -- MW attributes don't return a table if there is only 1 value. Converting both to tables to facilitate future processing
 
     -- MW attributes don't return a table if there is only 1 value. Converting both to tables to facilitate future processing
 
     mats1 = toTable(mats1)
 
     mats1 = toTable(mats1)
Line 261: Line 293:
 
     -- Build the materials and exceptions lists
 
     -- Build the materials and exceptions lists
 
     local mats, excepts = setListsForFusionMaterialDisplay(mats1, mats2)
 
     local mats, excepts = setListsForFusionMaterialDisplay(mats1, mats2)
 +
   
 +
    -- Dynamically calculate the number of columns
 +
    if #mats > 17 then
 +
    columns = 3
 +
    elseif #mats > 8 then
 +
    columns = 2
 +
    end
 
    
 
    
 
     -- Creates a new table on the first itteration, and when a description changes
 
     -- Creates a new table on the first itteration, and when a description changes
if not currentTable or description ~= previousDescription then
+
if not currentTable or description ~= previousDescription or result ~= previousResult then
 +
mw.log(result .. ": " .. (description or 'nil') .. " " .. (previousDescription or 'nil'))
 
currentTable = _fusionDisplay:tag("table"):attr("class", "wikitable")
 
currentTable = _fusionDisplay:tag("table"):attr("class", "wikitable")
 
currentTable:tag("ul"):tag("li"):wikitext(formatCardDisplay(unlink(result)) .. (description and (" (" .. description .. ')') or ''))
 
currentTable:tag("ul"):tag("li"):wikitext(formatCardDisplay(unlink(result)) .. (description and (" (" .. description .. ')') or ''))
Line 272: Line 312:
 
     currentTable = fillTableForFusionMaterial(currentTable, mats, columns, excepts)
 
     currentTable = fillTableForFusionMaterial(currentTable, mats, columns, excepts)
 
    
 
    
     previousDescription = description or previousDescription
+
     previousDescription = description
 +
    previousResult = result or previousResult
 
     end
 
     end
 
end
 
end
Line 301: Line 342:
 
      
 
      
 
     _fusionDisplay = HTML()
 
     _fusionDisplay = HTML()
     _pageName = _args[1] or "Enchanting Mermaid (DOR)"
+
     _pageName = _args[1] or "Mavelus (FMR)"
 
end
 
end
  
-- @name fusion_result
+
-- @name fusionResult
 
-- @description Generates the material lists for a given Fusion result. To be called through #invoke.
 
-- @description Generates the material lists for a given Fusion result. To be called through #invoke.
local function fusion_result( frame )
+
local function fusionResult( frame )
 
initParams(frame)
 
initParams(frame)
 
 
Line 316: Line 357:
 
end
 
end
  
-- @name fusion_material
+
-- @name fusionMaterial
 
-- @description Generates the list of other materials and Fusion result for a given material. To be called through #invoke.
 
-- @description Generates the list of other materials and Fusion result for a given material. To be called through #invoke.
local function fusion_material( frame )
+
local function fusionMaterial( frame )
 
initParams(frame)
 
initParams(frame)
 
      
 
      
Line 333: Line 374:
 
-- @exports  
 
-- @exports  
 
return {
 
return {
['fusion_result'] = fusion_result,
+
['fusionResult'] = fusionResult,
['fusion_material'] = fusion_material
+
['fusionMaterial'] = fusionMaterial
 
};
 
};
 
-- </pre>
 
-- </pre>

Latest revision as of 00:05, 22 May 2024

-- <pre>
-- @name DM Fusion display
-- @description A module used to display Fusion combinations in their respective card articles
-- @author [[User:JustGian]]
-- @contact [[User talk:JustGian]]

----------------
-- Load modules:
----------------
local DATA = require( 'Module:Data' );
local UTIL = require( 'Module:Util' );

--------------------
-- Module variables:
--------------------
-- Parser state:
local
	PAGENAME,
	NAMESPACE,
	_frame,
	_args,
	_fusionDisplay,
	_pageName,
	_combinationsQuery,
	_exceptionsQuery
;

-- Stores properties for cards to avoid making a new query each time they are needed
local CARD_INFO = {}

---------------------
-- Utility functions:
---------------------
-- mw functions:
local HTML = mw.html.create;

--------------------
-- Module functions:
--------------------
--# Local functions

--[[Doc
@function U unlink
@description (From Module:Card type) If a string is passed, unlinks
and returns the page name (not the label, unless «getLabel»).
If a table is passed, unlinks every instance of it
and returns the table with the page names for each respective entry.
@parameter {*} v Any value to unlink.
@parameter {boolean} getLabel Makes the function return the label instead.
@return {*} The unlinked value.
]]
local function unlink( v, getLabel )
	local function __unlink( v, getLabel )
		return UTIL.trim( v ) and (getLabel and v:match( '%[%[:?.-|(.-)]]' ) or v:match( '%[%[:?(.-)[|%]]' )) or UTIL.trim( v );
	end
	if type( v ) == 'string' then
		return __unlink( v, getLabel );
	elseif type( v ) == 'table' then
		local t = {};
		for key, value in ipairs( v ) do
			table.insert( t, __unlink( value, getLabel ) );
		end
		return t;
	else
		return v; -- @@@ error()
	end
end

-- @name toTable
-- @description Receives an object, and converts it to a table containing itself, unless it is already a table
local function toTable(obj)
	return type(obj) == "table" and obj or { obj }
end

-- @name formatCardDisplay
-- @description Receives a card page name and returns the expected string to be displayed
local function formatCardDisplay(card)
	attr = CARD_INFO[card]
	return string.format("#%s: %s\"[[%s|%s]]\"", attr["number"], attr["level"] and ( attr["level"] .. " [[File:CG Star.svg|15px|alt=|class=noviewer]] " ) or '', card, attr["name"])
end

-- @name fillTableForFusionResult
-- @description Appends the row with the materials of the current combination to a table belonging to a Fusion Result section
local function fillTableForFusionResult( currentTable, mats1, mats2, columns, mats_exceptions )
	local matsRow = currentTable:tag("tr")
	local mat1Cell = matsRow:tag("td"):tag("ul")
	local mat2Cell = matsRow:tag("td"):tag("div"):attr("style", "column-width: 13em; column-count: " .. columns):tag("ul")
	
	-- Prints the first cell
	for _, card in ipairs(mats1) do
		card = unlink(card)
		mat1Cell:tag("li"):wikitext(formatCardDisplay(card))
	end
	
	-- The second cell also includes the "except with" addition
	for _, card in ipairs(mats2) do
		card = unlink(card)
		mat2Cell:tag("li"):wikitext(formatCardDisplay(card) .. (mats_exceptions[card] and string.format(" (except with \"[[%s|%s]]\")", mats_exceptions[card], CARD_INFO[mats_exceptions[card]]["name"]) or ''))
	end
	
	return currentTable
end

-- @name fillTableForFusionMaterial
-- @description Appends the row with the materials of the current combination to a table belonging to a Fusion Material section
local function fillTableForFusionMaterial( currentTable, mats, columns, excepts )
	local matsRow = currentTable:tag("tr"):tag("td"):tag("div"):attr("style", "column-width: 13em; column-count: " .. columns):tag("ul")

	-- Print other materials, but don't include those present in the exceptions table
	for _, card in ipairs(mats) do
		if not excepts[card] then
			card = unlink(card)
			matsRow:tag("li"):wikitext(formatCardDisplay(card))
		end
	end
	
	return currentTable
end

-- @name runCombinationExceptionsQuery
-- @description Gets the combinations exceptions for a query of combinatigons
local function runCombinationExceptionsQuery()
	local fusion_ids = {}
	for _, combination in ipairs(_combinationsQuery) do
		table.insert(fusion_ids, unlink(combination[1]))
	end
	
	local query = mw.smw.ask( string.format("[[For Fusion::%s]] |?Fusion Material exception 1 |?Fusion Material exception 2 |?For Fusion", table.concat(fusion_ids, "||")) ) or {}
	
	return query
end

-- @name processCombinationsQuery
-- @description Gets the required attributes for a Fusion query by running the provided conditions. 
-- By running the query only once, the module saves a considerable amount of time that would be spent querying each card's name every time it is required
local function processCombinationsQuery(conditions)
	-- The attributes that should be returned
	local required_attributes = { 
		"Fusion Material 1", "Fusion Material 2", "Fusion result", "Description", "Columns",                                                                                                  -- Attributes about the Fusion combination
		"Fusion Material 1.English name = English name 1", "Fusion Material 1.Level = Level 1", "Fusion Material 1.Card number = Card number 1", "Fusion Material 1.Card type = Card type 1", -- Attributes about each card listed as Material 1
		"Fusion Material 2.English name = English name 2", "Fusion Material 2.Level = Level 2", "Fusion Material 2.Card number = Card number 2", "Fusion Material 2.Card type = Card type 2", -- Attributes about each card listed as Material 2
		"Fusion result.English name = English name 3",     "Fusion result.Level = Level 3",     "Fusion result.Card number = Card number 3",     "Fusion result.Card type = Card type 3",     -- Attributes about each card listed as Result
		"Fusion result = Fusion Material 3"																										                                              -- Small hack to help iterate below
		}
	
	local query = mw.smw.ask( conditions .. "|?" .. table.concat(required_attributes, "|?")) or {}
	local nonMonsterOffset
	
	-- Iterates over both Fusion Material lists, and adds each card to the table
	for _, combination in ipairs(query) do
		for combi_index = 1,3 do
			-- MW doesn't return multiple-valued properties as tables, so we convert them here
			materials = toTable(combination["Fusion Material " .. combi_index])
			en_names  = toTable(combination["English name " .. combi_index])
			levels    = toTable(combination["Level " .. combi_index])
			numbers   = toTable(combination["Card number " .. combi_index])
			types     = toTable(combination["Card type " .. combi_index])
			
			-- Since non-Monsters don't have Levels, the properties get returned with some lines missing for Monster-specific properties (e.g. Level)
			-- This variable is used to offset that
			nonMonsterOffset = 0
			
	    	for i, fm in ipairs(materials) do
	    		fm = unlink(fm)
	    		if not CARD_INFO[fm] then
	    			CARD_INFO[fm] = {
	    				["name"]   = en_names[i],
	    				["level"]  = levels[i-nonMonsterOffset],
	    				["number"] = numbers[i]
	    			}
				end
				if unlink(types[i]) ~= 'Monster Card' then
    				nonMonsterOffset = nonMonsterOffset + 1
    				CARD_INFO[fm]["level"] = nil
    			end
    		end
		end
	end

	return query
end

-- @name setListsForFusionMaterialDisplay
-- @description Creates the material and exception lists for a Fusion Material display
local function setListsForFusionMaterialDisplay(mats1, mats2)
	local materialIndex, mats
	local excepts = {}
	
	-- Checks to which of the two lists should be displayed (if a card belongs to Materials 2, display Materials 1)
	for _, mat in ipairs(mats1) do
		if unlink(mat) == _pageName then
			materialIndex = 2
			break
		end
	end
	
	-- Also checks the second list to see if the material is present there
	if materialIndex then
		for _, mat in ipairs(mats2) do
			if unlink(mat) == _pageName then
				materialIndex = materialIndex + 1
				break
			end
		end
	else
		materialIndex = 1
	end
	
	-- Assigns correct material and exception list, based on the index found
	-- Index 3 means that it was found in both lists
	if materialIndex == 1 or materialIndex == 3 then
		mats = mats1
		for _, combi_ex in ipairs(_exceptionsQuery) do
			if unlink(combi_ex["Fusion Material exception 1"]) == _pageName then
				excepts[combi_ex["Fusion Material exception 2"]] = true
			end
		end
	end

	if materialIndex == 2 or materialIndex == 3 then
		if not mats then mats = mats2
		else
			-- Joins both tables
			for _, m2 in ipairs(mats2) do
				if unlink(m2) ~= _pageName then -- Don't include the same article twice
					table.insert(mats, m2)
				end
			end
		end
		
		for _, combi_ex in ipairs(_exceptionsQuery) do
			if unlink(combi_ex["Fusion Material exception 2"]) == _pageName then
				excepts[combi_ex["Fusion Material exception 1"]] = true
			end
		end
	end

	return mats, excepts
end

-- @name createFusionResultTable
-- @description Creates the table to be displayed for Fusion Result monsters
local function createFusionResultTable()
	local previousDescription, currentTable
    
    -- Creates tables/rows for each combination found
    for _, combination in ipairs(_combinationsQuery) do
    	local combi_id, mats1, mats2, description, columns = combination[1], combination["Fusion Material 1"], combination["Fusion Material 2"], combination["Description"], combination["Columns"] or 1
    	local combination_exceptions = {}
    	
    	-- MW attributes don't return a table if there is only 1 value. Converting both to tables to facilitate future processing
    	mats1 = toTable(mats1)
    	mats2 = toTable(mats2)
		
		for _, combi_ex in ipairs(_exceptionsQuery) do
			if combi_ex["For Fusion"] == combi_id then
				combination_exceptions[unlink(combi_ex["Fusion Material exception 1"])] = unlink(combi_ex["Fusion Material exception 2"])
			end
		end
    	
    	-- Creates a new table on the first itteration, and when a description changes
		if not currentTable or description ~= previousDescription then
			currentTable = _fusionDisplay:tag("table"):attr("class", "wikitable")
			currentTable:tag("ul"):tag("li"):wikitext(description or '')
			
			local header = currentTable:tag("tr")
			header:tag("th"):attr("scope", "col"):wikitext("Material 1")
			header:tag("th"):attr("scope", "col"):wikitext("Material 2")
		end
    	
    	-- Appends materials to the table
    	currentTable = fillTableForFusionResult(currentTable, mats1, mats2, columns, combination_exceptions)
    	
    	previousDescription = description
    end
end

-- @name createFusionMaterialTable
-- @description Creates the table to be displayed for Fusion Material monsters
local function createFusionMaterialTable()
	local previousDescription, previousResult, currentTable
    
    -- Creates tables/rows for each combination found
    for _, combination in ipairs(_combinationsQuery) do
    	local combi_id, mats1, mats2, result, description, columns = 
    		combination[1], combination["Fusion Material 1"], combination["Fusion Material 2"], combination["Fusion result"], combination["Description"], combination["Columns"] or 1
    	local cur_combi_exceptions = {}

    	-- MW attributes don't return a table if there is only 1 value. Converting both to tables to facilitate future processing
    	mats1 = toTable(mats1)
    	mats2 = toTable(mats2)
    	
    	-- Build the materials and exceptions lists
    	local mats, excepts = setListsForFusionMaterialDisplay(mats1, mats2)
    	
    	-- Dynamically calculate the number of columns
    	if #mats > 17 then
    		columns = 3
    	elseif #mats > 8 then
    		columns = 2
    	end
    	
    	-- Creates a new table on the first itteration, and when a description changes
		if not currentTable or description ~= previousDescription or result ~= previousResult then
			mw.log(result .. ": " .. (description or 'nil') .. " " .. (previousDescription or 'nil'))
			currentTable = _fusionDisplay:tag("table"):attr("class", "wikitable")
			currentTable:tag("ul"):tag("li"):wikitext(formatCardDisplay(unlink(result)) .. (description and (" (" .. description .. ')') or ''))
			currentTable:tag("tr"):tag("th"):attr("scope", "col"):wikitext("Materials")
		end
    	
    	-- Appends materials to the table
    	currentTable = fillTableForFusionMaterial(currentTable, mats, columns, excepts)
    	
    	previousDescription = description
    	previousResult = result or previousResult
    end	
end

-- @name getCombinationsAndExceptionsForFusionResult
-- @description Gets the materials combinations and exceptions that result in this card
local function getCombinationsAndExceptionsForFusionResult()
    _combinationsQuery = processCombinationsQuery( string.format("[[Fusion result::%s]]", _pageName ) ) or {}
    _exceptionsQuery = runCombinationExceptionsQuery()
end

-- @name getCombinationsAndExceptionsForFusionMaterial
-- @description Gets the materials combinations and exceptions that this card is a material of
local function getCombinationsAndExceptionsForFusionMaterial()
    _combinationsQuery = processCombinationsQuery( string.format("<q>[[Fusion Material 1::%s]] OR [[Fusion Material 2::%s]]</q>|sort = Fusion result.Card number", _pageName, _pageName ) ) or {}
    _exceptionsQuery = runCombinationExceptionsQuery()
end

-- @name initParams
-- @description Sets values for all parameters accessible from the current context
local function initParams( frame )
	_frame = frame;
	_args  = UTIL.getArgs( frame, {
		trim         = true,
		removeBlanks = true,
		parentOnly   = true
	} );
    
    _fusionDisplay = HTML()
    _pageName = _args[1] or "Mavelus (FMR)"
end

-- @name fusionResult
-- @description Generates the material lists for a given Fusion result. To be called through #invoke.
local function fusionResult( frame )
	initParams(frame)
	
    getCombinationsAndExceptionsForFusionResult()
    
    createFusionResultTable()

	return tostring(_fusionDisplay)
end

-- @name fusionMaterial
-- @description Generates the list of other materials and Fusion result for a given material. To be called through #invoke.
local function fusionMaterial( frame )
	initParams(frame)
    
    getCombinationsAndExceptionsForFusionMaterial()
    
    createFusionMaterialTable()

	return tostring(_fusionDisplay)
end

----------
-- Return:
----------
-- @exports 
return {
	['fusionResult'] = fusionResult,
	['fusionMaterial'] = fusionMaterial
};
-- </pre>