diff --git a/cf-lib/changelog.txt b/cf-lib/changelog.txt index c6224f2..db76d84 100644 --- a/cf-lib/changelog.txt +++ b/cf-lib/changelog.txt @@ -1,4 +1,84 @@ --------------------------------------------------------------------------------------------------- +Version: 1.3.0 +Date: 24.12.2025 + Changes: + - :apply() returns self + - Add Recipe:addIndicatorIcon() +--------------------------------------------------------------------------------------------------- +Version: 1.2.0 +Date: 14.12.2025 + Changes: + - Add class Entity +--------------------------------------------------------------------------------------------------- +Version: 1.1.0 +Date: 12.12.2025 + Changes: + - Add Item:defaultImportLocation + - Add Recipe:addCategory + Bug Fixes: + - Settings.force also sets default_value +--------------------------------------------------------------------------------------------------- +Version: 1.0.0 +Date: 23.11.2025 + Changes: + - Add table.filterKey + - Add class Settings + - Add Recipe:removeResult + - Add Recipe:addResult +--------------------------------------------------------------------------------------------------- +Version: 0.0.14 +Date: 10.06.2025 + Changes: + - Add :merge +--------------------------------------------------------------------------------------------------- +Version: 0.0.13 +Date: 23.05.2025 + Changes: + - Add Item class +--------------------------------------------------------------------------------------------------- +Version: 0.0.12 +Date: 06.04.2025 + Changes: + - Add table.count +--------------------------------------------------------------------------------------------------- +Version: 0.0.11 +Date: 28.03.2025 + Changes: + - Add Technology:setIngredients +--------------------------------------------------------------------------------------------------- +Version: 0.0.10 +Date: 16.03.2025 + Changes: + - Recipe: addIngredient amount defaults to 1 + - Functions are chainable +--------------------------------------------------------------------------------------------------- +Version: 0.0.9 +Date: 28.02.2025 + Bug Fixes: + - Rename `table.concat` to `table.concatTables` to resolve conflict +--------------------------------------------------------------------------------------------------- +Version: 0.0.8 +Date: 28.02.2025 + Changes: + - Add table.trim +--------------------------------------------------------------------------------------------------- +Version: 0.0.7 +Date: 26.02.2025 + Changes: + - Add table.concat + Bug Fixes: + - Bugfix in Recipe +--------------------------------------------------------------------------------------------------- +Version: 0.0.6 +Date: 15.02.2025 + Bug Fixes: + - Bugfix in table.removeValue +--------------------------------------------------------------------------------------------------- +Version: 0.0.5 +Date: 15.02.2025 + Changes: + - Add Recipe class +--------------------------------------------------------------------------------------------------- Version: 0.0.4 Date: 13.02.2025 Changes: diff --git a/cf-lib/data/Entity.lua b/cf-lib/data/Entity.lua new file mode 100644 index 0000000..4a21072 --- /dev/null +++ b/cf-lib/data/Entity.lua @@ -0,0 +1,29 @@ +--- Utility class for entity prototype definitions +--- @class Entity +local Entity = {} + +--- Generates a selection box definition using width and height of the entity in tiles. +--- @param width number The width of the entity in tiles +--- @param height number The height of the entity in tiles +--- @return table A table containing the selection box definition +Entity.selectionBox = function(width, height) + return { + { -width / 2, -height / 2 }, + { width / 2, height / 2 } + } +end + +--- Generates a collision box definition using width and height of the entity in tiles. +--- @param width number The width of the entity in tiles +--- @param height number The height of the entity in tiles +--- @param margin number The margin to apply to the collision box compared to the selection box (default: 0.3) +--- @return table A table containing the collision box definition +Entity.collisionBox = function(width, height, margin) + margin = margin or 0.1 + return { + { -width / 2 + margin, -height / 2 + margin }, + { width / 2 - margin, height / 2 - margin } + } +end + +return Entity \ No newline at end of file diff --git a/cf-lib/data/Item.lua b/cf-lib/data/Item.lua new file mode 100644 index 0000000..a7d753c --- /dev/null +++ b/cf-lib/data/Item.lua @@ -0,0 +1,68 @@ +--- Utility class for a single item +--- @class Item +local Item = {} + +--- Pass an item name or an item table to get a Item object +--- @param value string|table The name of the item or the item table +function Item:new(value) + local name + local item + + if type(value) == "string" then + name = value + item = data.raw.item[name] + if not item then + log("Item not found: " .. name) + return nil + end + elseif type(value) == "table" then + name = value.name + item = value + end + + local obj = { + prototype = item + } + + setmetatable(obj, self) + self.__index = self + return obj +end + +--- Applies the item to the game +function Item:apply() + data:extend({ self.prototype }) + return self +end + +--- Assigns data to the item +--- Shorthand for table.assign(item.prototype, data) +--- @param data table The data to assign +function Item:assign(data) + table.assign(self.prototype, data) + return self +end + +--- Deeply merges data with the item +--- Shorthand for table.merge(item.prototype, data) +--- @param data table The data to merge +function Item:merge(data) + table.merge(self.prototype, data) + return self +end + +--- Sets the weight of the item calculated from given count per rocket +--- @param count number The amount of items that fit into a rocket +function Item:itemsPerRocket(count) + self.prototype.weight = (1000 / count) * kg + return self +end + +--- Sets the default import location for space platforms +--- @param planetName string The name of the planet +function Item:defaultImportLocation(planetName) + self.prototype.default_import_location = planetName + return self +end + +return Item \ No newline at end of file diff --git a/cf-lib/data/Recipe.lua b/cf-lib/data/Recipe.lua new file mode 100644 index 0000000..32a9d67 --- /dev/null +++ b/cf-lib/data/Recipe.lua @@ -0,0 +1,251 @@ +--- Utility class for a single recipe +--- @class Recipe +local Recipe = {} + +--- Pass a recipe name or a recipe table to get a Recipe object +--- @param value string|table The name of the recipe or the recipe table +function Recipe:new(value) + local name + local recipe + + if type(value) == "string" then + name = value + recipe = data.raw.recipe[name] + if not recipe then + log("Recipe not found: " .. name) + return nil + end + elseif type(value) == "table" then + name = value.name + recipe = value + end + + local obj = { + prototype = recipe + } + + setmetatable(obj, self) + self.__index = self + return obj +end + +--- Applies the recipe to the game +function Recipe:apply() + data:extend({ self.prototype }) + return self +end + +--- Assigns data to the recipe +--- Shorthand for table.assign(recipe.prototype, data) +--- @param data table The data to assign +function Recipe:assign(data) + table.assign(self.prototype, data) + return self +end + +--- Deeply merges data with the recipe +--- Shorthand for table.merge(recipe.prototype, data) +--- @param data table The data to merge +function Recipe:merge(data) + table.merge(self.prototype, data) + return self +end + +--- Adds an ingredient to the recipe +--- @param ingredientName string The name of the ingredient +--- @param amount number The amount of the ingredient (default: 1) +function Recipe:addIngredient(ingredientName, amount) + amount = amount or 1 + local ingredientType = data.raw.item[ingredientName] and "item" + or data.raw.module[ingredientName] and "item" + or data.raw.fluid[ingredientName] and "fluid" + or nil + if not ingredientType then + log("Unknown ingredient: " .. ingredientName) + return + end + self.prototype.ingredients = self.prototype.ingredients or {} + for _, ingredient in pairs(self.prototype.ingredients) do + if ingredient.name == ingredientName then + ingredient.amount = amount + ingredient.amount_min = nil + ingredient.amount_max = nil + ingredient.probability = nil + return self + end + end + table.insert(self.prototype.ingredients, { name = ingredientName, amount = amount, type = ingredientType }) + return self +end + +--- Replaces an existing ingredient by name with a new ingredient or adjusts the amount +--- @param old string The name of the existing ingredient +--- @param new? string The name of the new ingredient +--- @param amount? number The amount of the new ingredient +function Recipe:replaceIngredient(old, new, amount) + if type(new) == "number" then + amount = new + new = old + end + if self.prototype.ingredients then + for _, result in pairs(self.prototype.ingredients) do + if result.name == old then + result.name = new + result.amount = amount or result.amount + return self + end + end + end + return self +end + +--- Removes an existing ingredient by name +--- @param ingredientName string The name of the ingredient +function Recipe:removeIngredient(ingredientName) + if self.prototype.ingredients then + for i, ingredient in pairs(self.prototype.ingredients) do + if ingredient.name == ingredientName then + table.remove(self.prototype.ingredients, i) + return self + end + end + end + return self +end + +--- Adds a result to the recipe +--- @param resultName string The name of the result +--- @param amount number|table The amount of the result (default: 1) or a table with custom properties +function Recipe:addResult(resultName, amount) + local props = amount + if type(props) == "string" then + props = { amount = props } + end + if not props.amount then + props.amount = 1 + end + local resultType = data.raw.item[resultName] and "item" + or data.raw.module[resultName] and "item" + or data.raw.fluid[resultName] and "fluid" + or nil + if not resultType then + log("Unknown result: " .. resultName) + return + end + self.prototype.results = self.prototype.results or {} + for _, result in pairs(self.prototype.results) do + if result.name == resultName then + result.amount = nil + result.amount_min = nil + result.amount_max = nil + result.probability = nil + for k, v in pairs(props) do + result[k] = v + end + return self + end + end + props.name = resultName + props.type = resultType + table.insert(self.prototype.results, props) + return self +end + +--- Replaces an existing result by name with a new result +--- @param old string The name of the existing result +--- @param new? string The name of the new result +--- @param amount? number The amount of the new result (keeps the old value if not set) +function Recipe:replaceResult(old, new, amount) + if type(new) == "number" then + amount = new + new = old + end + if self.prototype.results then + for _, result in pairs(self.prototype.results) do + if result.name == old then + result.name = new + result.amount = amount or result.amount + end + end + if table.main_product == old then + table.main_product = new + end + end + return self +end + +--- Removes an existing result by name +--- @param resultName string The name of the result +function Recipe:removeResult(resultName) + if self.prototype.results then + for i, result in pairs(self.prototype.results) do + if result.name == resultName then + table.remove(self.prototype.results, i) + return self + end + end + end + return self +end + +--- Adds an additional crafting category +--- @param categoryName string The name of the crafting category +function Recipe:addCategory(categoryName) + local categories = self.prototype.additional_categories or {} + self.prototype.additional_categories = categories + if not table.contains(categories, categoryName) then + table.insert(categories, categoryName) + end + return self +end + +--- Adds the recipe to a technology +--- @param technology string|table The name of the technology or the technology table +function Recipe:unlockedByTechnology(technology) + if not technology then + return + end + technology = type(technology) == "table" and technology or data.raw.technology[technology] + if (not technology.effects) then + technology.effects = {} + end + for _, effect in pairs(technology.effects) do + if effect.type == "unlock-recipe" and effect.recipe == self.prototype.name then + return self + end + end + table.insert(technology.effects, { type = "unlock-recipe", recipe = self.prototype.name }) + return self +end + +--- Clones the recipe +--- @param name string The name of the new recipe +function Recipe:clone(name) + local clone = table.deepcopy(self.prototype) + clone.name = name + return Recipe:new(clone) +end + +--- Adds a small icon in the corner of the regular item +--- It currently assumes an icon_size of 64 +--- @param icon string The filename of the icon +--- @param position string The position of the icon. One of ["top-left", "top-right", "bottom-left", "bottom-right"]. Defaults to "bottom-right". +function Recipe:addIndicatorIcon(icon, position) + local icons = self.prototype.icons or {} + local mainIcon = icons[1] or self.prototype.icon or nil + if not mainIcon then + local result = self.prototype.results[1] + local resultItem = data.raw[result.type][result.name] + icons = resultItem.icons or {{ icon = resultItem.icon }} + end + local shift = { 8, 8 } + if position == "top-left" then shift = { -8, -8 } end + if position == "top-right" then shift = { 8, -8 } end + if position == "bottom-left" then shift = { -8, 8 } end + table.insert(icons, { icon = icon, scale = 0.25, shift = shift }) + self.prototype.icons = icons + log(serpent.block(self.prototype.icons)) + return self +end + +return Recipe diff --git a/cf-lib/data/Technology.lua b/cf-lib/data/Technology.lua index 88b947f..3dd3b48 100644 --- a/cf-lib/data/Technology.lua +++ b/cf-lib/data/Technology.lua @@ -40,6 +40,7 @@ end --- Applies the technology to the game function Technology:apply() data:extend({ self.prototype }) + return self end --- Assigns data to the technology @@ -47,32 +48,46 @@ end --- @param data table The data to assign function Technology:assign(data) table.assign(self.prototype, data) + return self +end + +--- Deeply merges data with the technology +--- Shorthand for table.merge(technology.prototype, data) +--- @param data table The data to merge +function Technology:merge(data) + table.merge(self.prototype, data) + return self end --- Sets the prerequisite for the technology --- @param prerequisites table The names of the prerequisites function Technology:setPrerequisites(prerequisites) self.prototype.prerequisites = prerequisites + return self end --- Adds a prerequisite to the technology --- @param prerequisite string|table The name of the prerequisite or the prerequisite Technology instance function Technology:addPrerequisite(prerequisite) local _prerequisite = prerequisiteName(prerequisite) + self.prototype.prerequisites = self.prototype.prerequisites or {} if (not table.contains(self.prototype.prerequisites, _prerequisite)) then table.insert(self.prototype.prerequisites, _prerequisite) end + return self end --- Adds multiple prerequisites to the technology --- @param prerequisites table The names of the prerequisite or the prerequisite Technology instances function Technology:addPrerequisites(prerequisites) + self.prototype.prerequisites = self.prototype.prerequisites or {} for _, prerequisite in pairs(prerequisites) do local _prerequisite = prerequisiteName(prerequisite) if (not table.contains(self.prototype.prerequisites, _prerequisite)) then table.insert(self.prototype.prerequisites, _prerequisite) end end + return self end --- Replaces a prerequisite in a technology @@ -86,6 +101,7 @@ function Technology:replacePrerequisite(old, new) self.prototype.prerequisites[i] = _new end end + return self end --- Removes a prerequisite from the technology @@ -95,9 +111,10 @@ function Technology:removePrerequisite(prerequisite) for i, techPrerequisite in pairs(self.prototype.prerequisites) do if techPrerequisite == _prerequisite then table.remove(self.prototype.prerequisites, i) - return + return self end end + return self end --- Adds an ingredient to the technology @@ -107,6 +124,7 @@ function Technology:addIngredient(ingredientName, amount) self.prototype.unit = self.prototype.unit or { ingredients = {} } self.prototype.unit.ingredients = self.prototype.unit.ingredients or {} table.insert(self.prototype.unit.ingredients, { ingredientName, amount or 1 }) + return self end --- Adds a list of ingredients to the technology with the default amount of 1 @@ -115,6 +133,7 @@ function Technology:addIngredients(ingredientNames) for _, ingredientName in pairs(ingredientNames) do self:addIngredient(ingredientName) end + return self end --- Removes an existing ingredient by name @@ -127,6 +146,16 @@ function Technology:removeIngredient(ingredientName) end end end + return self +end + +--- Sets the ingredients for the technology +--- @param ingredientNames table The names of the ingredients +function Technology:setIngredients(ingredients) + self.prototype.unit = self.prototype.unit or {} + self.prototype.unit.ingredients = {} + self:addIngredients(ingredients) + return self end --- Adds a recipe unlock to the technology @@ -134,16 +163,18 @@ end function Technology:addRecipe(recipeName) for _, effect in pairs(self.prototype.effects) do if effect.type == "unlock-recipe" and effect.recipe == recipeName then - return + return self end end table.insert(self.prototype.effects, { type = "unlock-recipe", recipe = recipeName }) + return self end --- Adds an effect to the technology --- @param effect table The effect to add function Technology:addEffect(effect) table.insert(self.prototype.effects, effect) + return self end --- Removes a recipe unlock from the technology @@ -152,9 +183,10 @@ function Technology:removeRecipe(recipeName) for i, effect in pairs(self.prototype.effects) do if effect.type == "unlock-recipe" and effect.recipe == recipeName then table.remove(self.prototype.effects, i) - return + return self end end + return self end --- Clones the technology diff --git a/cf-lib/info.json b/cf-lib/info.json index 088a838..4191509 100644 --- a/cf-lib/info.json +++ b/cf-lib/info.json @@ -1,10 +1,10 @@ { "name": "cf-lib", - "version": "0.0.4", + "version": "1.3.0", "title": "cackling fiends library", "description": "Because I'd like to have my own library :-)", "author": "cackling fiend", - "homepage": "", + "homepage": "https://discord.gg/ufvFUJtVwk", "factorio_version": "2.0", "dependencies": [ "base" diff --git a/cf-lib/settings/Settings.lua b/cf-lib/settings/Settings.lua new file mode 100644 index 0000000..0f1ce40 --- /dev/null +++ b/cf-lib/settings/Settings.lua @@ -0,0 +1,39 @@ +--- Utility class for manipulating settings +--- @class Settings +local Settings = {} + +local settingTypes = { "bool-setting", "int-setting", "double-setting", "string-setting", "color-setting" } + +local function getSetting(name) + for _, settingType in pairs(settingTypes) do + local setting = data.raw[settingType][name] + if setting then return setting end + end + error("getSetting: Setting with name '" .. name .. "' not found.") +end + +Settings.getSetting = getSetting + +function Settings.force(name, value) + local setting = getSetting(name) + setting.hidden = true + if setting.type == "bool-setting" or setting.type == "color-setting" then + setting.forced_value = value + else + setting.allowed_values = { value } + setting.default_value = value + end +end + +function Settings.default(name, value) + local setting = getSetting(name) + setting.default_value = value +end + +function Settings.forceDefault(name) + local setting = getSetting(name) + local value = setting.default_value + Settings.force(name, value) +end + +return Settings \ No newline at end of file diff --git a/cf-lib/util/table.lua b/cf-lib/util/table.lua index 1cdfc11..57c7fa2 100644 --- a/cf-lib/util/table.lua +++ b/cf-lib/util/table.lua @@ -46,7 +46,7 @@ end table.removeValue = function(target, value) for i, v in ipairs(target) do if v == value then - target.remove(target, i) + table.remove(target, i) return target end end @@ -68,4 +68,51 @@ function table.filter(target, predicate) end end return result +end + +--- Filters a table by removing given key. +--- @param target table The table to filter +--- @param key string The key to remove +function table.filterKey(target, key) + return table.filter( + target, + function(_, currentKey) + return currentKey ~= key + end + ) +end + +--- Concatenates two tables into a new table. +--- @param table1 table The first table +--- @param table2 table The second table +--- @return table The concatenated table +function table.concatTables(table1, table2) + local result = {} + for _, v in pairs(table1) do + table.insert(result, v) + end + for _, v in pairs(table2) do + table.insert(result, v) + end + return result +end + +--- Trims nil values from a table. +--- @param target table The table to trim +--- @return table The trimmed table +function table.trim(target) + return table.filter(target, function(v) + return v ~= nil + end) +end + +--- Counts the entries of given table. +--- @param target table Table to count +--- @return number Amount of entries +function table.count(target) + local count = 0 + for _ in pairs(target) do + count = count + 1 + end + return count end \ No newline at end of file