Module:ProfessionList: Difference between revisions

From Brighter Shores Wiki
Jump to navigation Jump to search
Content added Content deleted
No edit summary
m (use a whitelist of "activity" and "recipe" for types of entry that use RecipeTreeSearch)
 
(32 intermediate revisions by 3 users not shown)
Line 1: Line 1:
local currency = require('Module:Currency')
local currency = require('Module:Currency')
local search = require('Module:RecipeTreeSearch')
local search = require('Module:RecipeTreeSearch')
local lang = mw.language.getContentLanguage()


local p = {}
local p = {}
Line 37: Line 38:
-- use within :node()
-- use within :node()
function p.unknown_value_edit_cell(page,N)
function p.unknown_value_edit_cell(page,N)
if type(page) == 'string' then
local url = tostring(mw.uri.fullUrl(page,'action=edit'))
page = {page}
end
local textstring = ''
for _,p in ipairs(page) do
local url = tostring(mw.uri.fullUrl(p,'action=edit'))
local link = '['..url.." '''?'''&nbsp;(edit)]<br>"
textstring = textstring .. link
end
return mw.html.create('td')
return mw.html.create('td')
:addClass('table-bg-gray')
:addClass('table-bg-gray')
:css{ ['text-align'] = 'center' }
:css{ ['text-align'] = 'center' }
:attr{ colspan = tostring(N) }
:attr{ colspan = tostring(N) }
:wikitext('['..url.." '''?''' (edit)]")
:wikitext(textstring)
:done()
:done()
end
end
Line 49: Line 58:
-- the queryString is just a selection of pages, from any combination of SMW properties and categories
-- the queryString is just a selection of pages, from any combination of SMW properties and categories
-- for pages with multiple recipes or activities, will make on entry into the results table for each
-- for pages with multiple recipes or activities, will make on entry into the results table for each
-- screenFunction takes a decoded recipe/activity JSON and returns true if it is to be kept
-- screenFunctions take a decoded recipe/activity JSON and returns true if it is to be kept
function p.generate_recipe_table(queryString,screenFunction)
function p.generate_recipe_table(queryString, screenFunctions)
-- first formulate the query to get the list of pages
-- first formulate the query to get the list of pages
local query = {
local query = {
Line 57: Line 65:
'? #- = pageName',
'? #- = pageName',
'?Image #- = pageImage',
'?Image #- = pageImage',
'?Default subobject.Image #- = subobjectImage',
'?Activity JSON = activityJSON',
'?Activity JSON = activityJSON',
'?Recipe JSON = recipeJSON',
'?Recipe JSON = recipeJSON',
'limit = 500'
'?Bounty JSON = bountyJSON',
'?Investigation JSON = investigationJSON',
'?Venture JSON = ventureJSON',
'limit = 1000'
}
}
local pages = mw.smw.ask(query)
local pages = mw.smw.ask(query)

-- if no pages were found, skip the rest of this function
-- if no pages were found, skip the rest of this function
if pages == nil then
if pages == nil then
return nil
return nil
end
end

-- collate structure
-- collate structure
local recipes = {}
local recipes = {}

-- iterate through pages found
-- iterate through pages found
for _, page in ipairs(pages) do
for _, page in ipairs(pages) do
-- first iterate through activities
-- iterate through the types of JSON
local JSONtypes = {'activity','recipe','bounty','investigation','venture'}
if page.activityJSON ~= nil then
for _,JSONtype in ipairs(JSONtypes) do
if type(page.activityJSON)=='string' then
page.activityJSON = { page.activityJSON }
end
for _, JSON in ipairs(page.activityJSON) do
local item = mw.text.jsonDecode(JSON)
item.pageName = page.pageName
item.pageImage = page.pageImage
item.type = 'activity'
table.insert(recipes,item)
end
end
-- same iteration through recipes
-- iterate through the JSONs of that type on the page
if page.recipeJSON ~= nil then
if page[JSONtype..'JSON'] ~= nil then
if type(page.recipeJSON)=='string' then
if type(page[JSONtype..'JSON'])=='string' then
page.recipeJSON = { page.recipeJSON }
page[JSONtype..'JSON'] = { page[JSONtype..'JSON'] }
end
end
for _, JSON in ipairs(page.recipeJSON) do
for _, JSON in ipairs(page[JSONtype..'JSON']) do
local item = mw.text.jsonDecode(JSON)
local item = mw.text.jsonDecode(JSON)
item.pageName = page.pageName
item.pageName = page.pageName
item.pageImage = page.pageImage
item.pageImage = page.pageImage or page.subobjectImage or ''
item.type = 'recipe'
item.type = JSONtype
table.insert(recipes,item)
table.insert(recipes,item)
end
end
end
end
end
end
end
Line 108: Line 110:
return nil
return nil
end
end

-- output structure
-- output structure
local output = {}
local output = {}

-- screen using screenFunction
-- screen using screenFunctions
if (type(screenFunctions) == 'function') then
for _, item in ipairs(recipes) do
for _, item in ipairs(recipes) do
if screenFunction(item) then
-- when a single function is given, check the item against it
table.insert(output,item)
if screenFunctions(item) then
table.insert(output,item)
end
end
end
elseif (type(screenFunctions) == 'table') then
for _, item in ipairs(recipes) do
-- when a table of functions is given, check against all of them
local allTrue = true
for _, func in pairs(screenFunctions) do
if (type(func) == 'function' and not func(item)) then
allTrue = false
break
end
end
if allTrue then
table.insert(output,item)
end
end
else
-- support not passing in screen func to return all results
output = recipes
end
end

-- if all of the results were screened, skip the rest of this function
-- if all of the results were screened, skip the rest of this function
if output == nil or output[1] == nil then
if output == nil or output[1] == nil then
Line 126: Line 148:
-- sort the output by the .level parameter
-- sort the output by the .level parameter
table.sort(output, function(item1, item2)
table.sort(output, function(item1, item2)
-- if one of the two has no leve
-- if one of the two has no level
if (item1.level == nil) ~= (item2.level == nil) then
if (item1.level == nil) ~= (item2.level == nil) then
-- true if lvl2 is nil but not lvl1, false if lvl1 is nil but not lvl2
-- true if lvl2 is nil but not lvl1, false if lvl1 is nil but not lvl2
Line 134: Line 156:
-- if both are empty, treat as equivalent
-- if both are empty, treat as equivalent
return false
return false
end
if item2.level == item1.level and item1.coop~=item2.coop then
return item2.coop
end
end
-- normal comparison
-- normal comparison
return item1.level < item2.level
return item1.level < item2.level
end)
end)

-- perform a number of useful calculations on the data that are widely useful
-- perform a number of useful calculations on the data that are widely useful
-- like cost of materials, profit of products, recipe tree search
-- like cost of materials, profit of products, recipe tree search
for _,item in ipairs(output) do
for _, item in ipairs(output) do
if item.type=='recipe' or item.type=='activity' then

item.product = item.output[1].name or ''
item.product = (item.output and item.output[1] and item.output[1].name) or ''

-- perform the full recipe tree search on the product
-- perform the full recipe tree search on the product
local fullRecipe = search.main(item.product)
local fullRecipe = search.main(item.product)

-- overwrite any parameters from the original recipe with their counterparts from the full recipe
-- also search for the recipe and compare to the provided recipe
-- when product is nil, no data is overwritten
-- in the case of co-op recipes the two will differ in xp, kp, and duration
local lastStepRecipe = search.getRecipe(item.product)
-- increase the full recice values by the difference
fullRecipe.xp = fullRecipe.xp and lastStepRecipe.xp and item.xp and fullRecipe.xp + (item.xp - lastStepRecipe.xp)
fullRecipe.kp = fullRecipe.kp and lastStepRecipe.kp and item.kp and fullRecipe.kp + (item.kp - lastStepRecipe.kp)
fullRecipe.duration = fullRecipe.duration and lastStepRecipe.duration and item.duration and fullRecipe.duration + (item.duration - lastStepRecipe.duration)

-- otherwise overwrite any parameters from the original recipe with their counterparts from the full recipe
-- parameters which dont exist in the fullRecipe wont be overwritten
-- need to pre-nil the xp, kp, and duration so that if they are nil in the full recipe, they stay nil
item.xp = nil
item.kp = nil
item.duration = nil
for key,value in pairs(fullRecipe) do
for key,value in pairs(fullRecipe) do
item[key] = value
item[key] = value
end
end

-- include the price of selling the product
-- include the price of selling the product
item.sellPrice = search.getShopSellPrice(item.product)
local sell = search.getShopSellPrice(item.product)
local qty = item.output[1].quantity
item.sellPrice = sell and qty and sell * qty
item.profit = item.buyPrice and item.sellPrice and item.sellPrice - item.buyPrice
item.profit = item.buyPrice and item.sellPrice and item.sellPrice - item.buyPrice
item.profitPerXP = item.profit and item.xp and item.profit / item.xp

end
end
end

return output
return output
end
end


function p.one_column_image_text(sort,materials)
function p.one_column_image_text(materials)
local materialCell = mw.html.create('td')

local matcount = 0
for _, mat in ipairs(materials) do
materialCell:wikitext(((tonumber(mat.quantity) and lang:formatNum(tonumber(mat.quantity))) or mat.quantity) .. ' &times; [[File:' .. mat.name .. '.png|18x18px|link=' .. mat.name .. ']] [[' .. mat.name .. ']]<br>')
matcount = matcount + 1
end
if matcount==0 then
local materialCell = mw.html.create('td')
materialCell:addClass('table-na'):wikitext('N/A')
end


return materialCell
for _, mat in ipairs(item.materials) do
materialCell:wikitext(mat.quantity .. ' &times; [[File:' .. mat.name .. '.png|18px|link=' .. mat.name .. ']] [[' .. mat.name .. ']]<br>')
end
return materialCell
end
end


function p.two_column_image_text(sort,image,text,link)
function p.two_column_image_text(sort,image,text,link)
return mw.html.create('td')
return mw.html.create('td')
:css{ ['border-right'] = '0', ['text-align'] = 'right', ['max-width'] = '100px' }
:attr{ ['data-sort-value'] = sort }
:attr{ ['data-sort-value'] = sort }
:css{ ['border-right'] = '0', ['padding-right'] = '0', ['text-align'] = 'center', ['width'] = '40px' }
:wikitext(' [[' .. image .. '|link=' .. link .. '|30x30px]]')
:wikitext('[[' .. image .. '|link=' .. link .. '|48x32px]]')
:done()
:done()
:tag('td')
:tag('td')
:css{ ['border-left'] = '0', ['padding-left'] = '.2em' }
:addClass('plinkt-link no-border')
:wikitext('[[' .. link .. '|' .. text ..']]')
:wikitext('[[' .. link .. '|' .. text ..']]')
:done()
:done()
end
end


function p.three_column_image_text(sort,quantity,image,text,link)
function p.three_column_image_text(sort,quantity,image,text,link)

return mw.html.create('td')
return mw.html.create('td')
:css{ ['border-right'] = '0', ['padding-right'] = '0', ['text-align'] = 'right' }
:attr{ ['data-sort-value'] = sort }
:attr{ ['data-sort-value'] = sort }
:css{ ['border-right'] = '0', ['padding-right'] = '.2em', ['text-align'] = 'right' }
:wikitext(quantity .. ' &times;')
:wikitext(((tonumber(quantity) and lang:formatNum(tonumber(quantity))) or quantity) .. ' &times;')
:done()
:done()
:tag('td')
:tag('td')
:css{ ['border-left'] = '0', ['padding-left'] = '0', ['border-right'] = '0', ['padding-right'] = '0', ['text-align'] = 'center', ['width'] = '40px' }
:addClass('plinkt-image no-border')
:wikitext('[[' .. image .. '|link=' .. link .. '|48x32px]]')
:css{ ['border-left'] = '0', ['padding-left'] = '0' }
:wikitext('[[' .. image .. '|link=' .. link .. '|30px]]')
:done()
:done()
:tag('td')
:tag('td')
:css{ ['border-left'] = '0', ['padding-left'] = '.2em' }
:addClass('plinkt-link no-border')
:wikitext('[[' .. link .. '|' .. text .. ']]')
:wikitext('[[' .. link .. '|' .. text .. ']]')
:done()
:done()

end
end



Latest revision as of 22:24, 5 February 2025

Module documentation
This documentation is transcluded from Module:ProfessionList/doc. [edit] [history] [purge]
Module:ProfessionList requires Module:Currency.
Module:ProfessionList requires Module:RecipeTreeSearch.

This module provides functions that are commonly used among all of the modules to greate tables of profession activities, such as AlchemistList, DetectivePassiveList, BlacksmithSmeltingList, and so on. This module is a helper module to be used by other modules; it may not designed to be invoked directly. See Brighter Shores:Lua/Helper modules for a full list and more information. For a full list of modules using this helper click here

FunctionTypeUse
currency_cell(amount)amount: number, or nilReturns the HTML to create a table currency cell using module:currency._cell.

If instead it is supplied with nil then a 10x1 cell is created with the text "unknown".

Use within :node()
unknown_value_cell(size)size: integerReturns the HTML to create a sizex1 cell with the text "unknown".
Use within :node()
unknown_value_edit_cell(page,size)page: string (or table of strings), names of pages
size: integer
Returns the HTML to create a sizex1 cell with a link to edit the specified pages.
Use within :node()
generate_recipe_table(queryString)queryString: string, SMW query
screenFunction: function, table > boolean
Generates a table of recipes and activities from a Semantic MediaWiki query string and a screening function.

queryString is an SMW query, but it only needs to be the selection of pages. Parameters to be returned are chosen by the function.
screenFunction is a function which takes one recipe from the search and returns true if that recipe is to be kept, or false if it is to be discarded. Parameters available to the screening function are those present in the Recipe JSON or Activity JSON, as well as the name and image of the page that held the JSON.
Pages with multiple recipes or activities will make one entry into the results table for each JSON.

The results table entries are the decoded recipe/activity JSONs, parsed through Module:RecipeTreeSearch
one_column_image_text(materials)materials: table, each entry is a name/quantity pair for an item to displayProduces 1x1 cell with multiple lines of text in a standard format to display a list of materials for a recipe.

Includes the quantity of each material.
Requires that the image of each item is exactly the same as the item name itself.

For example: the reagents and bottle to produce a potion.
two_column_image_text(sort,image,text,link)sort: string, to sort the multicolumn cell by

image: string, image name to use, including File: and .png
text: string, text to display

link: string, page to link the image and text to
Produces 2 cells in a standard format to display an image and some text, which link to the same page.
For example: the image and name of a skill node, but linking to the item they make.
three_column_image_text(sort,quantity,image,text,link)sort: string, to sort the multicolumn cell by

quantity: number, will be displayed with "×" after it
image: string, image name to use, including File: and .png
text: string, text to display

link: string, page to link the image and text to
Produces 3 cells in a standard format to display a quantity, an image, and some text, which link to the same page.
For example: the quantity, image, and name of a recipe product.

local currency = require('Module:Currency')
local search = require('Module:RecipeTreeSearch')
local lang = mw.language.getContentLanguage()

local p = {}

-- this module provides functions that are commonly used among all of the modules to greate tables of profession activities
-- such as AlchemistList, DetectivePassiveList, BlacksmithSmeltingList, and so on

-- creates the html directly for a currency cell of a table
-- will always be 10 cells wide, for currency alignment
-- if not supplied with an amount, displays "unknown"
-- use within :node()
function p.currency_cell(amount)
	if not amount then
		return mw.html.create('td')
			:addClass('table-bg-gray')
			:css{ ['text-align'] = 'center' }
			:attr{ colspan = '10' }
			:wikitext("''unknown''")
		:done()
	end
	return currency._cell(amount, { html = 'yes' })
end

-- creates the html for an unknown value cell
-- use within :node()
function p.unknown_value_cell(N)
	return mw.html.create('td')
		:addClass('table-bg-gray')
		:css{ ['text-align'] = 'center' }
		:attr{ colspan = tostring(N) }
		:wikitext("''unknown''")
	:done()
end

-- creates the html for an unknown value cell, with a link to edit the page that has the unknown parameter
-- use within :node()
function p.unknown_value_edit_cell(page,N)
	if type(page) == 'string' then
		page = {page}
	end
	local textstring = ''
	for _,p in ipairs(page) do
		local url = tostring(mw.uri.fullUrl(p,'action=edit'))
		local link = '['..url.." '''?'''&nbsp;(edit)]<br>"
		textstring = textstring .. link
	end
	return mw.html.create('td')
		:addClass('table-bg-gray')
		:css{ ['text-align'] = 'center' }
		:attr{ colspan = tostring(N) }
		:wikitext(textstring)
	:done()
end

-- generates a table of recipes and activities from a Semantic MediaWiki query string
-- the queryString is just a selection of pages, from any combination of SMW properties and categories
-- for pages with multiple recipes or activities, will make on entry into the results table for each 
-- screenFunctions take a decoded recipe/activity JSON and returns true if it is to be kept
function p.generate_recipe_table(queryString, screenFunctions)
	-- first formulate the query to get the list of pages
	local query = {
		queryString,
		'? #- = pageName',
		'?Image #- = pageImage',
		'?Default subobject.Image #- = subobjectImage',
		'?Activity JSON = activityJSON',
		'?Recipe JSON = recipeJSON',
		'?Bounty JSON = bountyJSON',
		'?Investigation JSON = investigationJSON',
		'?Venture JSON = ventureJSON',
		'limit = 1000'
	}
	local pages = mw.smw.ask(query)

	-- if no pages were found, skip the rest of this function
	if pages == nil then
		return nil
	end

	-- collate structure
	local recipes = {}

	-- iterate through pages found
	for _, page in ipairs(pages) do
		
		-- iterate through the types of JSON
		local JSONtypes = {'activity','recipe','bounty','investigation','venture'}
		for _,JSONtype in ipairs(JSONtypes) do
		
			-- iterate through the JSONs of that type on the page
			if page[JSONtype..'JSON'] ~= nil then
				if type(page[JSONtype..'JSON'])=='string' then
					page[JSONtype..'JSON'] = { page[JSONtype..'JSON'] }
				end
				for _, JSON in ipairs(page[JSONtype..'JSON']) do
					local item = mw.text.jsonDecode(JSON)
					item.pageName = page.pageName
					item.pageImage = page.pageImage or page.subobjectImage or ''
					item.type = JSONtype
					table.insert(recipes,item)
				end
			end
		end
	end
	
	-- if none of the pages had recipes of activities, skip the rest of this function
	if recipes == nil or recipes[1] == nil then
		return nil
	end

	-- output structure
	local output = {}

	-- screen using screenFunctions
	if (type(screenFunctions) == 'function') then
		for _, item in ipairs(recipes) do
			-- when a single function is given, check the item against it
			if screenFunctions(item) then
				table.insert(output,item)
			end
		end
	elseif (type(screenFunctions) == 'table') then
		for _, item in ipairs(recipes) do
			-- when a table of functions is given, check against all of them
			local allTrue = true
			for _, func in pairs(screenFunctions) do
				if (type(func) == 'function' and not func(item)) then
					allTrue = false
					break
				end
			end
			if allTrue then
				table.insert(output,item)
			end
		end
	else
		-- support not passing in screen func to return all results
		output = recipes
	end

	-- if all of the results were screened, skip the rest of this function
	if output == nil or output[1] == nil then
		return nil
	end

	-- sort the output by the .level parameter
	table.sort(output, function(item1, item2)
		 -- if one of the two has no level
		if (item1.level == nil) ~= (item2.level == nil) then
			-- true if lvl2 is nil but not lvl1, false if lvl1 is nil but not lvl2
			return item2.level == nil 
		end
		if item1.level == nil then
			-- if both are empty, treat as equivalent
			return false
		end
		if item2.level == item1.level and item1.coop~=item2.coop then
			return item2.coop
		end
		 -- normal comparison
		return item1.level < item2.level
	end)

	-- perform a number of useful calculations on the data that are widely useful
	-- like cost of materials, profit of products, recipe tree search
	for _, item in ipairs(output) do
		if item.type=='recipe' or item.type=='activity' then

		item.product = (item.output and item.output[1] and item.output[1].name) or ''

		-- perform the full recipe tree search on the product
		local fullRecipe = search.main(item.product)

		-- also search for the recipe and compare to the provided recipe
		-- in the case of co-op recipes the two will differ in xp, kp, and duration
		local lastStepRecipe = search.getRecipe(item.product)
		-- increase the full recice values by the difference
		fullRecipe.xp = fullRecipe.xp and lastStepRecipe.xp and item.xp and fullRecipe.xp + (item.xp - lastStepRecipe.xp)
		fullRecipe.kp = fullRecipe.kp and lastStepRecipe.kp and item.kp and fullRecipe.kp + (item.kp - lastStepRecipe.kp)
		fullRecipe.duration = fullRecipe.duration and lastStepRecipe.duration and item.duration and fullRecipe.duration + (item.duration - lastStepRecipe.duration)

		-- otherwise overwrite any parameters from the original recipe with their counterparts from the full recipe
		-- parameters which dont exist in the fullRecipe wont be overwritten
		-- need to pre-nil the xp, kp, and duration so that if they are nil in the full recipe, they stay nil
		item.xp = nil
		item.kp = nil
		item.duration = nil
		for key,value in pairs(fullRecipe) do
			item[key] = value
		end

		-- include the price of selling the product
		local sell = search.getShopSellPrice(item.product)
		local qty = item.output[1].quantity
		item.sellPrice = sell and qty and sell * qty
		item.profit = item.buyPrice and item.sellPrice and item.sellPrice - item.buyPrice
		item.profitPerXP = item.profit and item.xp and item.profit / item.xp

		end
	end

	return output
end

function p.one_column_image_text(materials)
	local materialCell  = mw.html.create('td')

	local matcount = 0
	for _, mat in ipairs(materials) do
		materialCell:wikitext(((tonumber(mat.quantity) and lang:formatNum(tonumber(mat.quantity))) or mat.quantity) .. ' &times; [[File:' .. mat.name .. '.png|18x18px|link=' .. mat.name .. ']] [[' .. mat.name .. ']]<br>')
		matcount = matcount + 1
	end
	
	if matcount==0 then
		materialCell:addClass('table-na'):wikitext('N/A')
	end

	return materialCell 
end

function p.two_column_image_text(sort,image,text,link)
	return mw.html.create('td')
		:attr{ ['data-sort-value'] = sort }
		:css{ ['border-right'] = '0', ['padding-right'] = '0', ['text-align'] = 'center', ['width'] = '40px' }
		:wikitext('[[' .. image .. '|link=' .. link .. '|48x32px]]')
	:done()
	:tag('td')
		:css{ ['border-left'] = '0', ['padding-left'] = '.2em' }
		:wikitext('[[' .. link .. '|'  .. text ..']]')
	:done()
end

function p.three_column_image_text(sort,quantity,image,text,link)

	return mw.html.create('td')
		:attr{ ['data-sort-value'] = sort }
		:css{ ['border-right'] = '0', ['padding-right'] = '.2em', ['text-align'] = 'right' }
		:wikitext(((tonumber(quantity) and lang:formatNum(tonumber(quantity))) or quantity) .. ' &times;')
	:done()
	:tag('td')
		:css{ ['border-left'] = '0', ['padding-left'] = '0', ['border-right'] = '0', ['padding-right'] = '0', ['text-align'] = 'center', ['width'] = '40px' }
		:wikitext('[[' .. image .. '|link=' .. link .. '|48x32px]]')
	:done()
	:tag('td')
		:css{ ['border-left'] = '0', ['padding-left'] = '.2em' }
		:wikitext('[[' .. link .. '|' .. text .. ']]')
	:done()

end

return p