commit e3357f8bec90353f7c9d9a13ae90eebccd06b43f Author: Simon Brodtmann Date: Thu Jan 29 17:28:27 2026 +0100 Initial release diff --git a/README.md b/README.md new file mode 100644 index 0000000..50f23ef --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +[![foundrygg.com](https://img.shields.io/badge/foundrygg-4a1402?style=for-the-badge&logo=vercel&logoColor=white)](https://foundrygg.com)[![Discord](https://img.shields.io/badge/Discord-%235865F2.svg?style=for-the-badge&logo=discord&logoColor=white)](https://discord.gg/ufvFUJtVwk)[![Forgejo](https://img.shields.io/badge/source%20code-%23f4f4f5?style=for-the-badge&logo=forgejo&logoColor=%23c2410c)](https://git.cacklingfiend.info/cacklingfiend/factorio-light)[![Downloads](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fmods.factorio.com%2Fapi%2Fmods%2Ffactorio-light&query=%24.downloads_count&suffix=%20Downloads&style=for-the-badge&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI%2FPgo8IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDIwMDEwOTA0Ly9FTiIKICJodHRwOi8vd3d3LnczLm9yZy9UUi8yMDAxL1JFQy1TVkctMjAwMTA5MDQvRFREL3N2ZzEwLmR0ZCI%2BCjxzdmcgdmVyc2lvbj0iMS4wIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiB3aWR0aD0iMTIxNnB0IiBoZWlnaHQ9IjEyODBwdCIgdmlld0JveD0iMCAwIDEyMTYgMTI4MCIKIHByZXNlcnZlQXNwZWN0UmF0aW89InhNaWRZTWlkIG1lZXQiPgo8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwLDEyODApIHNjYWxlKDAuMSwtMC4xKSIKZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSJub25lIj4KPHBhdGggZD0iTTM0NzAgOTYwMCBsMCAtMzIwMCAtMTczMSAwIGMtOTUyIDAgLTE3MjkgLTQgLTE3MjcgLTggNCAtMTIgNjA2MgotNjM5MCA2MDY4IC02MzkwIDYgMCA2MDY0IDYzNzggNjA2OCA2MzkwIDIgNCAtNzc1IDggLTE3MjcgOCBsLTE3MzEgMCAwIDMyMDAKMCAzMjAwIC0yNjEwIDAgLTI2MTAgMCAwIC0zMjAweiIvPgo8L2c%2BCjwvc3ZnPgo%3D&label=&labelColor=%23e39827&color=%23e39827)](https://mods.factorio.com/mod/factorio-light/metrics?range=last_two_months&type=mod_downloads)[![Ko-fi](https://img.shields.io/badge/Ko--fi-F16061?style=for-the-badge&logo=ko-fi&logoColor=white)](https://ko-fi.com/cacklingfiend) + +_________________ +## Factorio light + +Did you ever think Factorio is too complex? Do you want to introduce your child to Factorio but think he/she will be overwhelmed? + +Well, that is over now! Factorio light is a reduced version of the vanilla experience. + +The following has been removed: + +* Production science pack +* Utility science pack +* Uranium and anything related +* Processing units +* mk2 equipment +* Beacons +* Circuitry +* Artillery +* Combat bots +* Infinite research +* Behemoth biters \ No newline at end of file diff --git a/factorio-light/data.lua b/factorio-light/data.lua new file mode 100644 index 0000000..d2da505 --- /dev/null +++ b/factorio-light/data.lua @@ -0,0 +1,121 @@ +require "__cf-lib__/util/table" + +local util = require "util" +local Technology = require("__cf-lib__/data/technology") +local Recipe = require("__cf-lib__/data/recipe") + +-- Clear space science +util.removeTechWithPrerequisite("space-science-pack") + +-- Clear production/utility science +util.removeEverythingRecursivelyByTechnology("uranium-mining", { hideEntities = { "heat-pipe", "nuclear-reactor", "spidertron" } }) +util.removeEverythingRecursivelyByTechnology("circuit-network", { skip = { "iron-stick" }, hideEntity = true }) +util.removeEverythingRecursivelyByTechnology("follower-robot-count-1") +util.removeEverythingRecursivelyByTechnology("military-4", { hideEntities = { "power-armor-mk2", "artillery-turret", "artillery-wagon", "spidertron" } }) +util.removeEverythingRecursivelyByTechnology("defender") +util.removeEverythingByTechnology("coal-liquefaction") +util.removeEverythingByTechnology("effect-transmission", { hideEntity = true }) +util.removeEverythingByTechnology("discharge-defense-equipment") +util.removeEverythingByTechnology("energy-shield-mk2-equipment") +util.removeEverythingByTechnology("personal-roboport-mk2-equipment") +util.removeEverythingByTechnology("battery-mk2-equipment") +util.removeEverythingByItem("discharge-defense-remote") + +-- Move rocket +Technology:new("rocket-silo") + :removePrerequisite("productivity-module-3") + :removePrerequisite("speed-module-3") + :removePrerequisite("utility-science-pack") + :removeIngredient("production-science-pack") + :removeIngredient("utility-science-pack") + +-- Move production/utility techs to space science +util.replacePrerequisite("production-science-pack", "space-science-pack") +util.replacePrerequisite("utility-science-pack", "space-science-pack") +util.replaceTechIngredient("production-science-pack", "space-science-pack") +util.replaceTechIngredient("utility-science-pack", "space-science-pack") +util.removeEverythingByTechnology("production-science-pack") +util.removeEverythingByTechnology("utility-science-pack") +data.raw["lab"]["lab"].inputs = table.filter( + data.raw["lab"]["lab"].inputs, + function(value) + return not table.contains({ "production-science-pack", "utility-science-pack" }, value) + end +) + +-- Remove processing unit +local recipes = { "speed-module-2", "speed-module-3", "efficiency-module-2", "efficiency-module-3", "productivity-module-2", "productivity-module-3" } +for _, recipe in pairs(recipes) do + Recipe:new(recipe):replaceIngredient("advanced-circuit", "electronic-circuit") +end +util.replaceRecipeIngredient("processing-unit", "advanced-circuit", 2) +util.replacePrerequisite("processing-unit", "chemical-science-pack") +util.removeEverythingByTechnology("processing-unit", { skip = { "advanced-circuit" } }) + +-- Remove tips and tricks +data.raw["tips-and-tricks-item"]["spidertron-control"] = nil +data.raw["tips-and-tricks-item"]["copy-paste-spidertron"] = nil +data.raw["tips-and-tricks-item"]["circuit-network"] = nil +data.raw["tips-and-tricks-item"]["connect-switch"] = nil + +-- Remove menu simulations +data.raw["utility-constants"]["default"].main_menu_simulations.nauvis_biter_base_laser_defense = nil + +-- Remove behemoth enemies +data.raw["unit"]["behemoth-biter"] = nil +data.raw["unit"]["behemoth-spitter"] = nil +data.raw["turret"]["behemoth-worm-turret"] = nil +data.raw["corpse"]["behemoth-biter-corpse"] = nil +data.raw["corpse"]["behemoth-spitter-corpse"] = nil +data.raw["corpse"]["behemoth-worm-corpse"] = nil +data.raw["corpse"]["behemoth-worm-corpse-burrowed"] = nil +data.raw["unit-spawner"]["biter-spawner"].result_units = (function() + local res = {} + res[1] = { "small-biter", { { 0.0, 0.3 }, { 0.6, 0.0 } } } + if not data.is_demo then + res[2] = { "medium-biter", { { 0.4, 0.0 }, { 0.6, 0.3 }, { 0.8, 0.1 } } } + res[3] = { "big-biter", { { 0.8, 0.0 }, { 1.0, 0.4 } } } + end + return res +end)() +data.raw["unit-spawner"]["spitter-spawner"].result_units = { + { "small-biter", { { 0.0, 0.3 }, { 0.55, 0 } } }, + { "small-spitter", { { 0.2, 0.0 }, { 0.4, 0.3 }, { 0.6, 0.0 } } }, + { "medium-spitter", { { 0.4, 0.0 }, { 0.6, 0.3 }, { 0.8, 0.1 } } }, + { "big-spitter", { { 0.8, 0.0 }, { 1.0, 0.4 } } } +} +data.raw["group-attack-achievement"]["it-stinks-and-they-dont-like-it"].entities = table.filter( + data.raw["group-attack-achievement"]["it-stinks-and-they-dont-like-it"].entities, + function(value) + return not table.contains({ "behemoth-biter", "behemoth-spitter" }, value) + end +) + +-- Remove uranium +data.raw["resource"]["uranium-ore"] = nil +data.raw["autoplace-control"]["uranium-ore"] = nil +data.raw["planet"]["nauvis"].map_gen_settings.autoplace_controls["uranium-ore"] = nil +data.raw["planet"]["nauvis"].map_gen_settings.autoplace_settings["entity"].settings["uranium-ore"] = nil +data.raw["map-gen-presets"]["default"]["rich-resources"].basic_settings.autoplace_controls["uranium-ore"] = nil +data.raw["map-gen-presets"]["default"]["rail-world"].basic_settings.autoplace_controls["uranium-ore"] = nil +data.raw["map-gen-presets"]["default"]["ribbon-world"].basic_settings.autoplace_controls["uranium-ore"] = nil +util.removeEverythingByItem("uranium-235") +util.removeEverythingByItem("uranium-238") +util.removeEverythingByItem("depleted-uranium-fuel-cell") + +-- Buff rocket silo +data.raw["rocket-silo"]["rocket-silo"].rocket_parts_required = 25 + +-- Buff LDS +Recipe:new("low-density-structure") + :replaceIngredient("copper-plate", 2) + :replaceIngredient("steel-plate", 1) + +-- Update character +data.raw["character"]["character"].animations[3].armors = table.filter(data.raw["character"]["character"].animations[3].armors, function(value) + return value ~= "power-armor-mk2" +end) +data.raw["character-corpse"]["character-corpse"].armor_picture_mapping["power-armor-mk2"] = nil + +-- Increase moisture +data.raw.planet["nauvis"].map_gen_settings.autoplace_controls["starting_area_moisture"] = { size = 2, richness = 6 } \ No newline at end of file diff --git a/factorio-light/info.json b/factorio-light/info.json new file mode 100644 index 0000000..fb82ba0 --- /dev/null +++ b/factorio-light/info.json @@ -0,0 +1,16 @@ +{ + "name": "factorio-light", + "version": "1.0.0", + "title": "Factorio light", + "description": "A light-weight version of Factorio for a chill round or your kids.", + "author": "cackling fiend", + "homepage": "https://discord.gg/ufvFUJtVwk", + "factorio_version": "2.0", + "dependencies": [ + "base", + "cf-lib >= 1.4.0", + "? elevated-rails", + "! space-age", + "! quality" + ] +} \ No newline at end of file diff --git a/factorio-light/thumbnail.png b/factorio-light/thumbnail.png new file mode 100644 index 0000000..3b5023f Binary files /dev/null and b/factorio-light/thumbnail.png differ diff --git a/factorio-light/util.lua b/factorio-light/util.lua new file mode 100644 index 0000000..12d663e --- /dev/null +++ b/factorio-light/util.lua @@ -0,0 +1,213 @@ +local util = {} + +util.removeTechWithPrerequisite = function(name) + for techName, technology in pairs(data.raw.technology) do + if technology.prerequisites and table.contains(technology.prerequisites, name) then + data.raw.technology[techName] = nil + end + end +end + +util.removeAchievementByItem = function(name) + for type in pairs(defines.prototypes['achievement']) do + if data.raw[type] then + for achievementName, achievement in pairs(data.raw[type]) do + if achievement.ammo_type == name or achievement.consumed_condition == name + or achievement.produced_condition == name or achievement.science_pack == name then + data.raw[type][achievementName] = nil + end + end + end + end +end + +util.removeAchievementByEntity = function(name) + for protoType in pairs(defines.prototypes['achievement']) do + if data.raw[protoType] then + local match = function(stringOrTable) + if stringOrTable == nil then + return false + end + local tableValue = type(stringOrTable) == "string" and { stringOrTable } or stringOrTable + return table.contains(tableValue, name) + end + + for achievementName, achievement in pairs(data.raw[protoType]) do + if achievement.to_build == name or match(achievement.to_kill) or match(achievement.damage_dealer) + or achievement.entity == name or match(achievement.excluded) or match(achievement.included) then + data.raw[protoType][achievementName] = nil + end + end + end + end +end + +util.removeOrHide = function(type, name, options) + local hide = options and options.hideEntity or false + if options and options.skip then + if table.contains(options.skip, name) then + return false + end + end + if options and options.hideEntities then + if table.contains(options.hideEntities, name) then + hide = true + end + end + if hide then + data.raw[type][name].hidden = true + data.raw[type][name].hidden_in_factoriopedia = true + else + data.raw[type][name] = nil + end + return true +end + +util.removeEntity = function(name, options) + for type in pairs(table.merge(defines.prototypes['entity'], defines.prototypes['equipment'])) do + if data.raw[type] then + local entity = data.raw[type][name] + if entity then + util.removeOrHide(type, name, options) + util.removeAchievementByEntity(name) + return + end + end + end +end + +util.removeEverythingByItem = function(name, options) + for type in pairs(defines.prototypes['item']) do + if data.raw[type] then + local item = data.raw[type][name] + if item then + if item.place_result then + util.removeEntity(item.place_result, options) + end + if item.place_as_equipment_result then + util.removeEntity(item.place_as_equipment_result, options) + end + util.removeOrHide(type, name, options) + + for customInputName, customInput in pairs(data.raw["custom-input"]) do + if customInput.item_to_spawn == name then + data.raw["custom-input"][customInputName] = nil + end + end + + util.removeAchievementByItem(name) + return + end + end + end +end + +util.removeEverythingByRecipe = function(name, options) + local recipe = data.raw.recipe[name] + local removed = util.removeOrHide("recipe", name, options) + if not removed then + return + end + if recipe.main_product then + util.removeEverythingByItem(recipe.main_product, options) + elseif recipe.results and #recipe.results == 1 and recipe.results[1].type == "item" then + util.removeEverythingByItem(recipe.results[1].name, options) + end + data.raw.recipe[name] = nil +end + +util.removeEverythingByTechnology = function(name, options) + local technology = data.raw.technology[name] + if technology and technology.effects then + for _, effect in pairs(technology.effects) do + if effect.type == "unlock-recipe" then + util.removeEverythingByRecipe(effect.recipe, options) + end + end + end + data.raw.technology[name] = nil + + for shortcutName, shortcut in pairs(data.raw.shortcut) do + if shortcut.technology_to_unlock then + data.raw.shortcut[shortcutName] = nil + end + end +end + +util.removeEverythingRecursivelyByTechnology = function(name, options) + util.removeEverythingByTechnology(name, options) + + for techName, technology in pairs(data.raw.technology) do + if technology.prerequisites and table.contains(technology.prerequisites, name) then + util.removeEverythingRecursivelyByTechnology(techName, options) + end + end +end + +util.replacePrerequisite = function(source, target) + for _, technology in pairs(data.raw.technology) do + if technology.prerequisites and table.contains(technology.prerequisites, source) then + table.removeValue(technology.prerequisites, source) + if not table.contains(technology.prerequisites, target) then + table.insert(technology.prerequisites, target) + end + end + end +end + +util.replaceTechIngredient = function(old, new) + for _, technology in pairs(data.raw.technology) do + if technology.unit and technology.unit.ingredients then + local matchOld = table.filter(technology.unit.ingredients, function(v) + return v[1] == old + end) + local matchNew = table.filter(technology.unit.ingredients, function(v) + return v[1] == new + end) + if #matchNew > 0 then + technology.unit.ingredients = table.filter(technology.unit.ingredients, function(v) + return v[1] ~= old + end) + elseif #matchOld > 0 then + matchOld[1][1] = new + end + end + end +end + +util.replaceRecipeIngredient = function(old, new, multiplier) + multiplier = multiplier or 1 + local function replace(tbl) + if not tbl then return end + for _, entry in pairs(tbl) do + if entry.name == old then + entry.name = new + if entry.amount then + entry.amount = entry.amount * multiplier + end + if entry.amount_min then + entry.amount_min = entry.amount_min * multiplier + end + if entry.amount_max then + entry.amount_max = entry.amount_max * multiplier + end + if entry.ignored_by_stats then + entry.ignored_by_stats = entry.ignored_by_stats * multiplier + end + if entry.ignored_by_productivity then + entry.ignored_by_productivity = entry.ignored_by_productivity * multiplier + end + end + end + end + + for _, recipe in pairs(data.raw["recipe"]) do + replace(recipe.ingredients) + replace(recipe.results) + if recipe.main_product == old then + recipe.main_product = new + end + end +end + +return util \ No newline at end of file diff --git a/sources/thumbnail.afphoto b/sources/thumbnail.afphoto new file mode 100644 index 0000000..86c0f50 Binary files /dev/null and b/sources/thumbnail.afphoto differ