ATTENTION! The process of updating WiKi to version Eco 10.x has begun. Those wishing to participate can find out more Information on our ECO Contribution Wiki Discord.

Module:UtilsAnimalTables

From Eco - English Wiki

Various utility functions to use when building tables of animals from Module:AnimalData. Primarily intended for use in Module:Table Animals and Module:Table Fish.

Usage[edit source]

Add the following line of code at the top of your file.

local AnimalUtils = require("Module:UtilsAnimalTables")

-- You may then call functions from this module in your script. For example:
local rangeText = AnimalUtils.toCountRange(2, 5, true)

local p = {}
local Utils = require("Module:Utils")
local Unit = require("Module:Unit")
local HTMLUtils = require("Module:UtilsHTML")

--- Format the input integers with <code>toRangeString</code> only if <code>1 < min</code> and optionally adds parentheses around the range.
-- @param #number min Minimum value (left side)
-- @param #number min Maximum value (right side)
-- @param #bool parentheses Add parentheses around the range?
-- @return #string "<code>min</code>—<code>max</code>"
-- @return #string " (<code>min</code>—<code>max</code>)"
-- @return #string "" (empty string) if <code>min == 1</code>
-- @author User:Demian
-- @see toRangeString
function p.toCountRange(min, max, parentheses)
	local range = Utils.toRangeString(min, max, min, "%d")

	if "1" == range then
		return ""
	else
		if parentheses then
			return mw.ustring.format(" (%s)", range)
		else
			return range
		end
	end
end

--- Format <code>height</code> into an integer, if it is greater than <code>0.0</code>.
-- @param #number height Value to format
-- @return #string "<code>height</code>" as an integer as a string.
-- @return #string "—" if <code>height == 0.0 or height == nil</code>.
-- @return #string "∞" if <code>height < 0.0</code>.
-- @author User:Demian
function p.formatClimbHeight(height)
	local heightNum = tonumber(Utils.valueOrDefault(height, 0.0))

	if 0.0 < heightNum then
		return mw.ustring.format("%d", heightNum)
	elseif 0.0 == heightNum then
		-- nil or 0.0
		return "—"
	else
		-- TODO: Support i18n.
		return HTMLUtils.tagAbbr("&infin;", "Unlimited")
	end
end

--- Create HTML code for a small image (recipe size) of the specified file with a blue border.
--
-- Heavily simplified from <code>Utils.build_icon</code>.
-- @param #string imageFileName Name of the image file to display
-- @param #string link Wikitext for a link to some page
-- @param #string backgroundColor One of: <code>"Green"</code>, <code>"Blue"</code>
-- @return #string HTML code wrapped around wikitext to display an image with a link.
-- @author User:Demian
-- @see formatWikilink
-- @see Util.build_icon
function p.buildResourceItemIcon(imageFileName, link, backgroundColor)
	local iconBg = mw.ustring.format("icon%s", backgroundColor)
	local file = mw.ustring.format("[[File:%s|frameless|class=iconRecipe %s|link=%s]]", Utils.checkImage(imageFileName, false), iconBg, link)
	return mw.ustring.format("<div class=iconContainerSmall><div class=\"iconStack\">%s</div><div class=\"iconBorder borderBlue\" style=\"position:absolute;\"></div></div>", file)
end

--- Sort items in the string list of foods, if any.
-- @param #string foodCSV String to split
-- @param #string character Character to split string with
-- @return #string <code>foodCSV</code> with items in alphabetical order.
-- @return #string "—" if <code>foodCSV</code> is <code>nil</code>.
-- @return #string <code>foodCSV</code> unchanged if it contains no commas.
-- @author User:Demian
function p.formatFoodSources(foodCSV)
	if nil == foodCSV then
		return "—"
	elseif mw.ustring.find(foodCSV, ",") then
		return Utils.sortListString(foodCSV, ",", ", ")
	else
		-- Skip all sorting if there is only one item in foods.
		return foodCSV
	end
end

--- Create HTML code to display an item harvested from an animal with an image of the item on the left and the name (with link) on the right side.
--
-- Will additionally display the number of items harvested as a range, but only if <code>1 < resourceMin</code>.
-- @param #string itemLink Wikitext link to the resource item
-- @param #number resourceMin Minimum number of items that harvesting will yield
-- @param #number resourceMax Maximum number of items that harvesting will yield
-- @return #string HTML code for a table displaying the harvested item.
-- @author User:Demian
function p.formatResourceItem(itemLink, resourceMin, resourceMax)
	local itemName = mw.ustring.sub(itemLink, 3, -3)
	local actualPageName = Utils.getDirectPageName(itemName, "item")
	local countString = p.toCountRange(resourceMin, resourceMax, true)
	local linkHTML = mw.ustring.format("%s%s", Utils.formatWikilink(actualPageName, itemName, true), countString)

	-- Raw meat seems to be the only exception to the harvest items. It is the only "food" item.
	local iconHTML = p.buildResourceItemIcon(itemName, Utils.formatWikilink(actualPageName, actualPageName, false), "Raw Meat" == itemName and "Green" or "Blue")

	return HTMLUtils.twoCellTable(iconHTML, linkHTML)
end

--- Determine if the specified animal swims and has unlimited climb height.
-- @param #table animal Animal data from Module:AnimalData
-- @return #bool <code>true</code> <em>usually</em> means the animal is a fish living <em>exclusively</em> in the ocean.
-- @return #bool <code>false</code> definitively means the animal is not a fish.
-- @author User:Demian
function p.isFishAnimal(animal)
	return "Swimming" == animal.isSwimming and nil ~= animal.climbHeight and 0.0 > tonumber(animal.climbHeight)
end

--- Determine if the specified animal swims and does <strong>not</strong> have unlimited climb height.
-- @param #table animal Animal data from Module:AnimalData
-- @return #bool <code>true</code> <em>usually</em> means the animal is aquatic (can swim) and definitively means the animal is <strong>not</strong> a fish living <em>exclusively</em> in the ocean.
-- @return #bool <code>false</code> definitively means the animal is not aquatic, as it can not swim.
-- @author User:Demian
function p.isAquaticNonFishAnimal(animal)
	return "Swimming" == animal.isSwimming and nil ~= animal.climbHeight and 0.0 <= tonumber(animal.climbHeight)
end

--- Create a HTML table header for a table of animals used in Template:AnimalList.
-- @param #table tbl Table to insert header to (parameter is modified directly)
-- @author User:Demian
-- @see insertAnimalDataRow
function p.insertAnimalListHeader(tbl)
	-- The following values are left out as rather niche or unclear information.
	-- maturity: unclear.
	-- headDistance: distance between heads when sleeping in a pack.
	-- resourceBonus: unclear.
	local meterAbbr = Unit._unit(nil, "m", true)
	
	table.insert(tbl, "<tr>")
	table.insert(tbl, "<th rowspan=\"3\">Animal</th>")
	table.insert(tbl, "<th colspan=\"5\">Movement</th>")
	table.insert(tbl, "<th colspan=\"2\">Nutrition</th>")
	table.insert(tbl, "<th colspan=\"10\">Hunting</th>")
	table.insert(tbl, mw.ustring.format("<th rowspan=\"3\">Carbon<br>Released<br>(%s)</th>", Unit._unit(nil, "ppm", true)))
	table.insert(tbl, "</tr>")

	table.insert(tbl, "<tr>")
	-- Header: Movement
	table.insert(tbl, "<th rowspan=\"2\">Swims?</th>")
	table.insert(tbl, "<th rowspan=\"2\">Flies?</th>")
	table.insert(tbl, mw.ustring.format("<th rowspan=\"2\">Climb<br>Height (%s)</th>", meterAbbr))
	table.insert(tbl, mw.ustring.format("<th colspan=\"2\">Speed (%s)</th>", Unit._unit(nil, "m/s", true)))

	-- Header: Nutrition
	table.insert(tbl, "<th rowspan=\"2\">Eats</th>")
	table.insert(tbl, "<th rowspan=\"2\">Calories</th>")

	-- Header: Hunting
	table.insert(tbl, "<th rowspan=\"2\">Attacks?</th>")
	table.insert(tbl, "<th rowspan=\"2\">Flees?</th>")
	table.insert(tbl, "<th rowspan=\"2\">Fear<br>Factor</th>")
	table.insert(tbl, "<th rowspan=\"2\">Health</th>")
	table.insert(tbl, mw.ustring.format("<th rowspan=\"2\">Detect<br>Range (%s)</th>", meterAbbr))
	table.insert(tbl, "<th colspan=\"4\">Attack</th>")
	table.insert(tbl, "<th rowspan=\"2\">Harvest Item</th>")
	table.insert(tbl, "</tr>")

	table.insert(tbl, "<tr>")
	-- Header: Speed
	table.insert(tbl, "<th>Idle</th>")
	table.insert(tbl, "<th>Run</th>")

	-- Header: Attack
	table.insert(tbl, "<th>Chance (%)</th>")
	table.insert(tbl, mw.ustring.format("<th>Range (%s)</th>", meterAbbr))
	table.insert(tbl, "<th>Damage</th>")
	table.insert(tbl, "<th>Delay</th>")
	table.insert(tbl, "</tr>")
end

--- Parse and format given <code>data</data> into a single HTML table row that matches columns output by <code>insertAnimalListHeader</code>.
-- @param #table tbl Table to insert row into (parameter is modified directly)
-- @param #string animalName Name of the animal on this row
-- @param #table animal Animal data from Module:AnimalData
-- @author User:Demian
-- @see insertAnimalListHeader
function p.insertAnimalListDataRow(tbl, animalName, data)
	local maxAttackDelay;

	table.insert(tbl, "<tr>")
	table.insert(tbl, mw.ustring.format("<td>%s</td>", Utils.formatWikilink(Utils.getDirectPageName(animalName, "animal"), animalName, false)))

	-- Movement 5 columns
	table.insert(tbl, mw.ustring.format("<td style=\"text-align:center;\">%s</td>", Utils.formatNilToYesNo(data.isSwimming)))
	table.insert(tbl, mw.ustring.format("<td style=\"text-align:center;\">%s</td>", Utils.formatNilToYesNo(data.isFlying)))
	table.insert(tbl, mw.ustring.format("<td style=\"text-align:right;\">%s</td>", p.formatClimbHeight(data.climbHeight)))
	table.insert(tbl, mw.ustring.format("<td style=\"text-align:right;\">%.1f</td>", Utils.valueOrDash(tonumber(data.wanderingSpeed))))
	table.insert(tbl, mw.ustring.format("<td style=\"text-align:right;\">%.1f</td>", Utils.valueOrDash(tonumber(data.speed))))

	-- Nutrition 2 columns
	table.insert(tbl, mw.ustring.format("<td>%s</td>", p.formatFoodSources(data.foodSources)))
	table.insert(tbl, mw.ustring.format("<td style=\"text-align:right;\">%s</td>", Utils.valueOrDash(tonumber(data.calorieValue))))

	-- Hunting 10 columns
	maxAttackDelay = tonumber(data.maxAttackDelay)
	table.insert(tbl, mw.ustring.format("<td style=\"text-align:center;\">%s</td>", Utils.formatBoolToYesNo(0.0 ~= maxAttackDelay and 0.0 ~= tonumber(data.damage))))
	table.insert(tbl, mw.ustring.format("<td style=\"text-align:center;\">%s</td>", Utils.formatNilToYesNo(data.flees)))
	table.insert(tbl, mw.ustring.format("<td style=\"text-align:right;\">%.1f</td>", data.fearFactor))
	table.insert(tbl, mw.ustring.format("<td style=\"text-align:right;\">%.1f</td>", data.health))
	table.insert(tbl, mw.ustring.format("<td style=\"text-align:right;\">%.1f</td>", data.detectRange))
	table.insert(tbl, mw.ustring.format("<td style=\"text-align:right;\">%.1f</td>", data.chanceToAttack))
	table.insert(tbl, mw.ustring.format("<td style=\"text-align:right;\">%.1f</td>", data.attackRange))
	table.insert(tbl, mw.ustring.format("<td style=\"text-align:right;\">%.1f</td>", data.damage))

	if 0.0 == maxAttackDelay then
		table.insert(tbl, mw.ustring.format("<td style=\"text-align:right;\">—</td>"))
	else
		table.insert(tbl, mw.ustring.format("<td style=\"text-align:right;\">%s</td>", Utils.toRangeString(data.minAttackDelay, data.maxAttackDelay, data.minAttackDelay, "%0.1f")))
	end

	if nil == data.resourceItem then
		table.insert(tbl, mw.ustring.format("<td>—</td>"))
	else
		table.insert(tbl, mw.ustring.format("<td>%s</td>", p.formatResourceItem(data.resourceItem, data.resourceMin, data.resourceMax)))
	end

	-- Remaining 1 column
	table.insert(tbl, mw.ustring.format("<td style=\"text-align:right;\">%.4f</td>", data.carbonRelease))

	table.insert(tbl, "</tr>")
end

return p