Repository: randovania/randovania
Pull request #8088: Factorio improvements
Author: login: henriquegemignani
Labels: name: enhancement
name: game: Factorio
Created: 2025-03-12T00:05:02Z | Merged: 2025-03-12T19:57:04Z

DRAFT: make new release of patcher and depend on it

- Fix database importer
- Import database files
- Tweak Rocket Part recipe balancing
- Randomize Satellite recipe
- Make Satellite part of the win condition
- Add Differences tab

![image](https://github.com/user-attachments/assets/83cf2a91-2f6b-4f20-9281-d3e90e672250)



Diff:
diff --git a/pyproject.toml b/pyproject.toml
index 1eb85daac6..5a5b6795e9 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -79,7 +79,7 @@ exporters = [
     "tsc-utils>=0.2.5",
 
     # Factorio
-    "factorio-randovania-mod>=0.7.2",
+    "factorio-randovania-mod>=0.8.0",
 
     # Metroid Dread
     "open-dread-rando>=2.16.0",
diff --git a/randovania/games/factorio/assets/recipes-raw.json b/randovania/games/factorio/assets/recipes-raw.json
index ead90b805b..a3fad21053 100644
--- a/randovania/games/factorio/assets/recipes-raw.json
+++ b/randovania/games/factorio/assets/recipes-raw.json
@@ -19,7 +19,8 @@
         "subgroup": "parameters",
         "category": "parameters",
         "order": "a",
-        "parameter": true
+        "parameter": true,
+        "allow_productivity": true
     },
     "parameter-1": {
         "type": "recipe",
@@ -32,7 +33,8 @@
         "subgroup": "parameters",
         "category": "parameters",
         "order": "a",
-        "parameter": true
+        "parameter": true,
+        "allow_productivity": true
     },
     "parameter-2": {
         "type": "recipe",
@@ -45,7 +47,8 @@
         "subgroup": "parameters",
         "category": "parameters",
         "order": "a",
-        "parameter": true
+        "parameter": true,
+        "allow_productivity": true
     },
     "parameter-3": {
         "type": "recipe",
@@ -58,7 +61,8 @@
         "subgroup": "parameters",
         "category": "parameters",
         "order": "a",
-        "parameter": true
+        "parameter": true,
+        "allow_productivity": true
     },
     "parameter-4": {
         "type": "recipe",
@@ -71,7 +75,8 @@
         "subgroup": "parameters",
         "category": "parameters",
         "order": "a",
-        "parameter": true
+        "parameter": true,
+        "allow_productivity": true
     },
     "parameter-5": {
         "type": "recipe",
@@ -84,7 +89,8 @@
         "subgroup": "parameters",
         "category": "parameters",
         "order": "a",
-        "parameter": true
+        "parameter": true,
+        "allow_productivity": true
     },
     "parameter-6": {
         "type": "recipe",
@@ -97,7 +103,8 @@
         "subgroup": "parameters",
         "category": "parameters",
         "order": "a",
-        "parameter": true
+        "parameter": true,
+        "allow_productivity": true
     },
     "parameter-7": {
         "type": "recipe",
@@ -110,7 +117,8 @@
         "subgroup": "parameters",
         "category": "parameters",
         "order": "a",
-        "parameter": true
+        "parameter": true,
+        "allow_productivity": true
     },
     "parameter-8": {
         "type": "recipe",
@@ -123,7 +131,8 @@
         "subgroup": "parameters",
         "category": "parameters",
         "order": "a",
-        "parameter": true
+        "parameter": true,
+        "allow_productivity": true
     },
     "parameter-9": {
         "type": "recipe",
@@ -136,7 +145,8 @@
         "subgroup": "parameters",
         "category": "parameters",
         "order": "a",
-        "parameter": true
+        "parameter": true,
+        "allow_productivity": true
     },
     "speed-module": {
         "type": "recipe",
diff --git a/randovania/games/factorio/assets/techs-raw.json b/randovania/games/factorio/assets/techs-raw.json
index 9ac0f9b5ba..7086a715cd 100644
--- a/randovania/games/factorio/assets/techs-raw.json
+++ b/randovania/games/factorio/assets/techs-raw.json
@@ -1110,7 +1110,7 @@
     "electric-energy-distribution-1": {
         "type": "technology",
         "name": "electric-energy-distribution-1",
-        "icon": "__base__/graphics/technology/electric-energy-distribution-1.png",
+        "icon": "__randovania-assets__/graphics/technology/medium-electric-pole.png",
         "icon_size": 256,
         "effects": [
             {
@@ -5031,7 +5031,7 @@
                 "type": "unlock-recipe"
             }
         ],
-        "icon": "__base__/graphics/technology/electric-energy-distribution-1.png",
+        "icon": "__randovania-assets__/graphics/technology/big-electric-pole.png",
         "icon_size": 256,
         "type": "technology",
         "order": "d-b",
diff --git a/randovania/games/factorio/exporter/patch_data_factory.py b/randovania/games/factorio/exporter/patch_data_factory.py
index 2e6b296318..307a7412df 100644
--- a/randovania/games/factorio/exporter/patch_data_factory.py
+++ b/randovania/games/factorio/exporter/patch_data_factory.py
@@ -70,9 +70,16 @@ def create_game_specific_data(self) -> dict:
 
             technologies.append(new_tech)
 
-        return {
+        result: cfg.Configuration = {
             "configuration_identifier": self.description.shareable_hash,
             "layout_uuid": str(self.players_config.get_own_uuid()),
+            "optional_modifications": {
+                "can_send_fish_to_space": self.configuration.can_send_fish_to_space,
+                "stronger_solar": self.configuration.stronger_solar,
+                "productivity_everywhere": self.configuration.productivity_everywhere,
+                "single_item_freebie": self.configuration.single_item_freebie,
+                "strict_multiplayer_freebie": self.configuration.strict_multiplayer_freebie,
+            },
             "technologies": technologies,
             "recipes": self._create_recipe_patches(),
             "starting_tech": [
@@ -83,6 +90,8 @@ def create_game_specific_data(self) -> dict:
             ],
         }
 
+        return result
+
     def _create_recipe_patches(self) -> list[cfg.ConfigurationRecipesItem]:
         result = []
 
diff --git a/randovania/games/factorio/generator/base_patches_factory.py b/randovania/games/factorio/generator/base_patches_factory.py
index f258a38297..2d867e4daf 100644
--- a/randovania/games/factorio/generator/base_patches_factory.py
+++ b/randovania/games/factorio/generator/base_patches_factory.py
@@ -38,6 +38,11 @@ def create_game_specific(
         techs_raw = data_parser.load_techs_raw()
         item_cost = cost_calculator(recipes_raw, techs_raw)
 
+        # Choose a random recipe for rocket parts as if it's 5 times cheaper.
+        # We'll later multiply what we get by 5. This means the recipe will always involve items in bulk,
+        # making it harder to build a rocket with just freebies.
+        item_cost["rocket-part"].material /= 5
+
         # Get all tech that are present in the preset
         collection = ResourceCollection.with_database(game.resource_database)
         for pickup in pool_creator.calculate_pool_results(configuration, game).all_pickups():
@@ -50,8 +55,11 @@ def create_game_specific(
 
         # Get all recipe results
         all_items_set = set(BASIC_RESOURCES)
-        for recipe in available_recipes:
-            all_items_set.update(data_parser.get_results(recipes_raw[recipe]).keys())
+        for recipe_name in available_recipes:
+            recipe = recipes_raw[recipe_name]
+            recipe_category = recipe.get("category", "crafting")
+            if recipe_category != "rocket-building":
+                all_items_set.update(data_parser.get_results(recipe).keys())
 
         all_items = sorted(all_items_set)
         simple_items = [
@@ -68,14 +76,16 @@ def create_game_specific(
             "production-science-pack": (all_items, 2),
             "utility-science-pack": (all_items, 2),
             "rocket-part": (all_items, 0),
+            "satellite": (all_items, 3),
         }
         custom_recipes = {}
 
         for target_item, (items, max_fluid) in to_change.items():
             ingredients = recipes.make_random_recipe(rng, items, target_item, item_cost, max_fluid=max_fluid)
+            multiplier = 5 if target_item == "rocket-part" else 1
             custom_recipes[target_item] = {
                 "ingredients": {
-                    item_name: amount * 10 if item_cost[item_name].is_fluid else amount
+                    item_name: (amount * 10 if item_cost[item_name].is_fluid else amount) * multiplier
                     for item_name, amount in ingredients
                 }
             }
diff --git a/randovania/games/factorio/gui/preset_settings/factorio_patches_tab.py b/randovania/games/factorio/gui/preset_settings/factorio_patches_tab.py
index a5c94d9db1..9d3e3f1cee 100644
--- a/randovania/games/factorio/gui/preset_settings/factorio_patches_tab.py
+++ b/randovania/games/factorio/gui/preset_settings/factorio_patches_tab.py
@@ -13,13 +13,18 @@
     from randovania.layout.preset import Preset
 
 
-class PresetFactorioPatches(PresetTab, Ui_PresetFactorioPatches):
+class PresetFactorioPatches(PresetTab[FactorioConfiguration], Ui_PresetFactorioPatches):
     def __init__(self, editor: PresetEditor, game_description: GameDescription, window_manager: WindowManager):
         super().__init__(editor, game_description, window_manager)
         self.setupUi(self)
 
         # Signals
         self.full_tech_tree_check.stateChanged.connect(self._persist_option_then_notify("full_tech_tree"))
+        self.allow_productivity_check.stateChanged.connect(self._persist_option_then_notify("productivity_everywhere"))
+        self.stronger_solar_check.stateChanged.connect(self._persist_option_then_notify("stronger_solar"))
+        self.strict_multiplayer_freebie_check.stateChanged.connect(
+            self._persist_option_then_notify("strict_multiplayer_freebie")
+        )
 
     @classmethod
     def tab_title(cls) -> str:
@@ -29,7 +34,9 @@ def tab_title(cls) -> str:
     def header_name(cls) -> str | None:
         return None
 
-    def on_preset_changed(self, preset: Preset) -> None:
+    def on_preset_changed(self, preset: Preset[FactorioConfiguration]) -> None:
         config = preset.configuration
-        assert isinstance(config, FactorioConfiguration)
         self.full_tech_tree_check.setChecked(config.full_tech_tree)
+        self.allow_productivity_check.setChecked(config.productivity_everywhere)
+        self.stronger_solar_check.setChecked(config.stronger_solar)
+        self.strict_multiplayer_freebie_check.setChecked(config.strict_multiplayer_freebie)
diff --git a/randovania/games/factorio/gui/ui_files/games_tab_factorio_widget.ui b/randovania/games/factorio/gui/ui_files/games_tab_factorio_widget.ui
index 732438ae22..91b2161d73 100644
--- a/randovania/games/factorio/gui/ui_files/games_tab_factorio_widget.ui
+++ b/randovania/games/factorio/gui/ui_files/games_tab_factorio_widget.ui
@@ -33,7 +33,12 @@
       <item>
        <widget class="QLabel" name="intro_label">
         <property name="text">
-         <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Build a factory, research new technologies and automate the whole process in order to launch a rocket.&lt;/p&gt;&lt;p&gt;What each technology you unlock gives is shuffled, but which science packs you'll need and how many is still the same. However, the recipes for each pack plus the rocket is random!&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+         <string>Build a factory, research new technologies and automate the whole process in order to launch a rocket.
+
+What each technology you unlock gives is shuffled, but which science packs you'll need and how many is still the same. However, the recipes for each pack plus the rocket and satellite are random!</string>
+        </property>
+        <property name="textFormat">
+         <enum>Qt::TextFormat::MarkdownText</enum>
         </property>
         <property name="wordWrap">
          <bool>true</bool>
@@ -52,7 +57,7 @@
     <item>
      <spacer name="intro_spacer">
       <property name="orientation">
-       <enum>Qt::Vertical</enum>
+       <enum>Qt::Orientation::Vertical</enum>
       </property>
       <property name="sizeHint" stdset="0">
        <size>
@@ -107,7 +112,101 @@
            <string># updated from code</string>
           </property>
           <property name="alignment">
-           <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+           <set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
+          </property>
+          <property name="wordWrap">
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QWidget" name="differences_tab">
+   <attribute name="title">
+    <string>Differences</string>
+   </attribute>
+   <layout class="QVBoxLayout" name="differences_layout">
+    <property name="leftMargin">
+     <number>0</number>
+    </property>
+    <property name="topMargin">
+     <number>0</number>
+    </property>
+    <property name="rightMargin">
+     <number>0</number>
+    </property>
+    <property name="bottomMargin">
+     <number>0</number>
+    </property>
+    <item>
+     <widget class="QScrollArea" name="differences_scroll_area">
+      <property name="widgetResizable">
+       <bool>true</bool>
+      </property>
+      <widget class="QWidget" name="differences_scroll_area_contents">
+       <property name="geometry">
+        <rect>
+         <x>0</x>
+         <y>0</y>
+         <width>485</width>
+         <height>929</height>
+        </rect>
+       </property>
+       <layout class="QVBoxLayout" name="differences_scroll_layout">
+        <item>
+         <widget class="QLabel" name="differences_label">
+          <property name="text">
+           <string>### Freebies
+Whenever a technology is researched, any eligible items from the unlocked recipes are distributed to online players as bonus.
+
+Using buildings acquired by freebies is expected by logic, such as solar panels for power.
+
+#### Eligible Items
+- Placeable entities, such as buildings and robots.
+- Armors, equipment and guns.
+
+### New Items and Recipes
+- Burner Assembling Machine and Burner Lab have been added, always available.
+- Longer Handed Inserters have been added, with a unique technology to unlock them.
+- Alternative recipes for handcrafting Construction and Logistic Robots were added.
+
+### Starting Items and Recipes
+- Transport Belts are now unlocked by Logistics 1, along with Underground Belts and Splitters.
+- Pipes and Pipe to Ground are now unlocked by default, instead of by Steam Power.
+- Copper Cable, Small Electric Poles, Iron Stick and Labs are once again unlocked by default.
+
+### Inserter Changes
+- Inserters now have their own technology, progressive with Fast Inserters and Bulk Inserters.
+- Long Handed Inserters are unlocked by their own technology, separately from other Inserters.
+- Inserter Capactity Bonus is split into Regular and Bulk Inserter Capacity Bonus.
+
+### Fluid Changes
+- Fluid Handling now only unlocks barreling recipes.
+- New technology Fluid Storage has been added that unlocks Fluid Tanks and Pumps.
+- Fluid Wagons no longer have Storage Tanks as an ingredient and the technology also unlocks Pumps.
+
+### Oil Changes
+- Solid Fuel recipes are now unlocked only by the new technology Solid Fuel.
+- Heavy and Light Oil Cracking are now unlocked by the Oil Cracking technology.
+- Coal Liquefaction now uses Steam as an ingredient instead of Heavy Oil and also unlocks Refineries.
+
+### Changed and New Technologies
+- Electronics now unlocks only Electronic Circuits.
+- Medium Electric Pole and Big Electric Pole are now unlocked by separate technologies.
+- Research Productivity has been added.
+- Research Speed and Mining Productivity now give high bonuses per level.
+- Automated rail transportation has been merged with Railway.
+- Nuclear Fuel Reprocessing has been merged with Nuclear Power.</string>
+          </property>
+          <property name="textFormat">
+           <enum>Qt::TextFormat::MarkdownText</enum>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
           </property>
           <property name="wordWrap">
            <bool>true</bool>
diff --git a/randovania/games/factorio/gui/ui_files/preset_factorio_patches.ui b/randovania/games/factorio/gui/ui_files/preset_factorio_patches.ui
index 9ea11a98eb..837919495c 100644
--- a/randovania/games/factorio/gui/ui_files/preset_factorio_patches.ui
+++ b/randovania/games/factorio/gui/ui_files/preset_factorio_patches.ui
@@ -34,54 +34,140 @@
      <number>4</number>
     </property>
     <item>
-     <widget class="QCheckBox" name="full_tech_tree_check">
-      <property name="enabled">
-       <bool>true</bool>
-      </property>
-      <property name="text">
-       <string>Full Tech Tree</string>
+     <widget class="QGroupBox" name="tech_tree_group">
+      <property name="title">
+       <string>Tech Tree</string>
       </property>
+      <layout class="QVBoxLayout" name="tech_tree_layout">
+       <item>
+        <widget class="QCheckBox" name="full_tech_tree_check">
+         <property name="enabled">
+          <bool>true</bool>
+         </property>
+         <property name="text">
+          <string>Full Tech Tree</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QLabel" name="full_tech_tree_label">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="maximumSize">
+          <size>
+           <width>16777215</width>
+           <height>60</height>
+          </size>
+         </property>
+         <property name="text">
+          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Include checks for every single tier of the damage and speed upgrades. When unchecked only the first two tiers are used.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
+         </property>
+         <property name="wordWrap">
+          <bool>true</bool>
+         </property>
+         <property name="openExternalLinks">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+      </layout>
      </widget>
     </item>
     <item>
-     <widget class="QLabel" name="full_tech_tree_label">
-      <property name="sizePolicy">
-       <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
-        <horstretch>0</horstretch>
-        <verstretch>0</verstretch>
-       </sizepolicy>
-      </property>
-      <property name="maximumSize">
-       <size>
-        <width>16777215</width>
-        <height>60</height>
-       </size>
-      </property>
-      <property name="text">
-       <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Include checks for every single tier of the damage and speed upgrades. When unchecked only the first two tiers are used.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
-      </property>
-      <property name="alignment">
-       <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
-      </property>
-      <property name="wordWrap">
-       <bool>true</bool>
-      </property>
-      <property name="openExternalLinks">
-       <bool>true</bool>
+     <widget class="QGroupBox" name="difficulty_group">
+      <property name="title">
+       <string>Difficulty</string>
       </property>
+      <layout class="QVBoxLayout" name="difficulty_layout">
+       <item>
+        <widget class="QCheckBox" name="allow_productivity_check">
+         <property name="text">
+          <string>Allow Productivity Modules everywhere</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QLabel" name="allow_productivity_label">
+         <property name="text">
+          <string>When enabled, all recipes allow the use of Productivity Modules not just intermediates.
+
+Recommended, due the non-intermediates being valid science ingredients.</string>
+         </property>
+         <property name="textFormat">
+          <enum>Qt::TextFormat::MarkdownText</enum>
+         </property>
+         <property name="wordWrap">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QCheckBox" name="stronger_solar_check">
+         <property name="text">
+          <string>Stronger Solar Panels and Accumulators</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QLabel" name="stronger_solar_label">
+         <property name="text">
+          <string>Increases how much power a Solar Panel gives and how much energy an Accumulator holds to 4 times as much.
+
+Recommended, as Solar can be your only source of Electricity and scaling it in early game is very expensive.</string>
+         </property>
+         <property name="textFormat">
+          <enum>Qt::TextFormat::MarkdownText</enum>
+         </property>
+         <property name="wordWrap">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+      </layout>
      </widget>
     </item>
     <item>
-     <widget class="Line" name="full_tech_tree_line">
-      <property name="orientation">
-       <enum>Qt::Horizontal</enum>
+     <widget class="QGroupBox" name="freebies_group">
+      <property name="title">
+       <string>Freebies</string>
       </property>
+      <layout class="QVBoxLayout" name="freebies_layout">
+       <item>
+        <widget class="QCheckBox" name="strict_multiplayer_freebie_check">
+         <property name="text">
+          <string>Strict Freebies in Multiplayer</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QLabel" name="strict_multiplayer_freebie_label">
+         <property name="text">
+          <string>When enabled, guarantee that the total amount of items distributed is not more than in singleplayer, even if it  means some players get nothing.
+
+When disabled, every player is guaranteed at least one, even if you were offline. This includes vehicles and armors.</string>
+         </property>
+         <property name="textFormat">
+          <enum>Qt::TextFormat::MarkdownText</enum>
+         </property>
+         <property name="wordWrap">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+      </layout>
      </widget>
     </item>
     <item>
      <spacer name="vertical_spacer">
       <property name="orientation">
-       <enum>Qt::Vertical</enum>
+       <enum>Qt::Orientation::Vertical</enum>
       </property>
       <property name="sizeHint" stdset="0">
        <size>
diff --git a/tools/factorio/util.py b/randovania/games/factorio/importer_util.py
similarity index 96%
rename from tools/factorio/util.py
rename to randovania/games/factorio/importer_util.py
index 25d191fd76..0b53421e75 100644
--- a/tools/factorio/util.py
+++ b/randovania/games/factorio/importer_util.py
@@ -39,7 +39,7 @@ def get_localized_name(n: str) -> str:
 def read_locales(factorio_path: Path) -> None:
     with factorio_path.joinpath("data/base/locale/en/base.cfg").open() as fp:
         locale.read_file(itertools.chain(["[global]"], fp), source="base.cfg")
-    locale.read([factorio_path.joinpath("mods/randovania-layout/locale/en/strings.cfg")])
+    locale.read([factorio_path.joinpath("mods/randovania/locale/en/strings.cfg")])
 
 
 def template_req(name: str) -> dict:
diff --git a/randovania/games/factorio/layout/factorio_configuration.py b/randovania/games/factorio/layout/factorio_configuration.py
index a464089522..350d561afc 100644
--- a/randovania/games/factorio/layout/factorio_configuration.py
+++ b/randovania/games/factorio/layout/factorio_configuration.py
@@ -9,6 +9,11 @@
 @dataclasses.dataclass(frozen=True)
 class FactorioConfiguration(BaseConfiguration):
     full_tech_tree: bool
+    can_send_fish_to_space: bool
+    stronger_solar: bool
+    productivity_everywhere: bool
+    single_item_freebie: bool
+    strict_multiplayer_freebie: bool
 
     @classmethod
     def game_enum(cls) -> RandovaniaGame:
diff --git a/randovania/games/factorio/layout/factorio_preset_describer.py b/randovania/games/factorio/layout/factorio_preset_describer.py
index 0251924911..964f3361fd 100644
--- a/randovania/games/factorio/layout/factorio_preset_describer.py
+++ b/randovania/games/factorio/layout/factorio_preset_describer.py
@@ -3,7 +3,7 @@
 from typing import TYPE_CHECKING
 
 from randovania.games.factorio.layout import FactorioConfiguration
-from randovania.layout.preset_describer import GamePresetDescriber
+from randovania.layout.preset_describer import GamePresetDescriber, fill_template_strings_from_tree
 
 if TYPE_CHECKING:
     from randovania.layout.base.base_configuration import BaseConfiguration
@@ -19,4 +19,15 @@ def format_params(self, configuration: BaseConfiguration) -> dict[str, list[str]
         if configuration.full_tech_tree:
             template_strings["Item Pool"].insert(0, "Uses Full Tech Tree")
 
+        extra_message_tree = {
+            "Gameplay": [{"Strict Multiplayer Freebies": configuration.strict_multiplayer_freebie}],
+            "Game Changes": [
+                {
+                    "Regular Solar": not configuration.stronger_solar,
+                    "Regular Productivity Modules": not configuration.productivity_everywhere,
+                },
+            ],
+        }
+        fill_template_strings_from_tree(template_strings, extra_message_tree)
+
         return template_strings
diff --git a/randovania/games/factorio/logic_database/header.json b/randovania/games/factorio/logic_database/header.json
index aac5f022ee..54890d6ae9 100644
--- a/randovania/games/factorio/logic_database/header.json
+++ b/randovania/games/factorio/logic_database/header.json
@@ -1211,7 +1211,7 @@
                 }
             },
             "bulk-inserter-capacity-bonus": {
-                "long_name": "bulk inserter capacity bonus",
+                "long_name": "Bulk inserter capacity bonus",
                 "max_capacity": 1,
                 "extra": {
                     "recipes_unlocked": []
@@ -10423,8 +10423,20 @@
     },
     "minimal_logic": null,
     "victory_condition": {
-        "type": "template",
-        "data": "craft-rocket-part"
+        "type": "and",
+        "data": {
+            "comment": null,
+            "items": [
+                {
+                    "type": "template",
+                    "data": "craft-rocket-part"
+                },
+                {
+                    "type": "template",
+                    "data": "craft-satellite"
+                }
+            ]
+        }
     },
     "dock_weakness_database": {
         "types": {
@@ -10463,4 +10475,4 @@
     "regions": [
         "Tech.json"
     ]
-}
\ No newline at end of file
+}
diff --git a/randovania/games/factorio/pickup_database/pickup-database.json b/randovania/games/factorio/pickup_database/pickup-database.json
index 0ee0e3a981..fcbe2218a9 100644
--- a/randovania/games/factorio/pickup_database/pickup-database.json
+++ b/randovania/games/factorio/pickup_database/pickup-database.json
@@ -952,7 +952,7 @@
             "hint_features": [
                 "power"
             ],
-            "model_name": "__base__/graphics/technology/electric-energy-distribution-1.png",
+            "model_name": "__randovania-assets__/graphics/technology/big-electric-pole.png",
             "offworld_models": {},
             "preferred_location_category": "major",
             "progression": [
@@ -965,7 +965,7 @@
             "hint_features": [
                 "power"
             ],
-            "model_name": "__base__/graphics/technology/electric-energy-distribution-1.png",
+            "model_name": "__randovania-assets__/graphics/technology/medium-electric-pole.png",
             "offworld_models": {},
             "preferred_location_category": "major",
             "progression": [
diff --git a/randovania/games/factorio/presets/starter_preset.rdvpreset b/randovania/games/factorio/presets/starter_preset.rdvpreset
index a1f8d2a81a..3ee9317f3b 100644
--- a/randovania/games/factorio/presets/starter_preset.rdvpreset
+++ b/randovania/games/factorio/presets/starter_preset.rdvpreset
@@ -346,6 +346,11 @@
         "single_set_for_pickups_that_solve": true,
         "staggered_multi_pickup_placement": true,
         "check_if_beatable_after_base_patches": false,
-        "full_tech_tree": false
+        "full_tech_tree": false,
+        "can_send_fish_to_space": false,
+        "stronger_solar": true,
+        "productivity_everywhere": true,
+        "single_item_freebie": false,
+        "strict_multiplayer_freebie": false
     }
-}
\ No newline at end of file
+}
diff --git a/requirements.txt b/requirements.txt
index f16bdf0201..02c2cf6b63 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -87,7 +87,7 @@ eventlet==0.39.1
     # via randovania (setup.py)
 execnet==2.1.1
     # via pytest-xdist
-factorio-randovania-mod==0.7.2
+factorio-randovania-mod==0.8.0
     # via randovania (setup.py)
 flask==3.1.0
     # via
diff --git a/test/test_files/log_files/factorio/starter_preset.rdvgame b/test/test_files/log_files/factorio/starter_preset.rdvgame
index e98c121c9c..64f511678c 100644
--- a/test/test_files/log_files/factorio/starter_preset.rdvgame
+++ b/test/test_files/log_files/factorio/starter_preset.rdvgame
@@ -356,7 +356,12 @@
                     "single_set_for_pickups_that_solve": true,
                     "staggered_multi_pickup_placement": true,
                     "check_if_beatable_after_base_patches": false,
-                    "full_tech_tree": false
+                    "full_tech_tree": false,
+                    "can_send_fish_to_space": false,
+                    "stronger_solar": true,
+                    "productivity_everywhere": true,
+                    "single_item_freebie": false,
+                    "strict_multiplayer_freebie": false
                 }
             }
         ]
@@ -635,5 +640,5 @@
         "Battery at Tech/Tier 5/Pickup (Military 3)",
         "Fluid Storage at Tech/Tier 7/Pickup (Rocket fuel)"
     ],
-    "checksum": "0d544873c2ae5d569eb2049fea08e68cbec5ef9150a0bf37713110f61b9fb7ec161661414c2ef5c3a5e4af0fc0c993e934073dd6f9483609dfa5a0ed12b5520f"
-}
\ No newline at end of file
+    "checksum": "a45262b901bae9e683e5362688ca18cf25ea5d229e3377967558eb1f40eb2f8fe206d56045009d139b03ed88d62f8acef87fce9ad1b5cca4dd4e7edb0795c771"
+}
diff --git a/test/test_files/patcher_data/factorio/factorio/starter_preset/world_1.json b/test/test_files/patcher_data/factorio/factorio/starter_preset/world_1.json
index 84c30973a3..19aaf3326a 100644
--- a/test/test_files/patcher_data/factorio/factorio/starter_preset/world_1.json
+++ b/test/test_files/patcher_data/factorio/factorio/starter_preset/world_1.json
@@ -1,6 +1,13 @@
 {
     "configuration_identifier": "XXXXXXXX",
     "layout_uuid": "00000000-0000-1111-0000-000000000000",
+    "optional_modifications": {
+        "can_send_fish_to_space": false,
+        "stronger_solar": true,
+        "productivity_everywhere": true,
+        "single_item_freebie": false,
+        "strict_multiplayer_freebie": false
+    },
     "technologies": [
         {
             "tech_name": "randovania-steam-power",
@@ -189,7 +196,7 @@
             "tech_name": "randovania-gun-turret",
             "locale_name": "Medium Electric Pole",
             "description": "",
-            "icon": "__base__/graphics/technology/electric-energy-distribution-1.png",
+            "icon": "__randovania-assets__/graphics/technology/medium-electric-pole.png",
             "icon_size": 256,
             "cost_reference": "gun-turret",
             "prerequisites": [
@@ -632,7 +639,7 @@
             "tech_name": "randovania-stronger-explosives-c",
             "locale_name": "Big Electric Pole",
             "description": "",
-            "icon": "__base__/graphics/technology/electric-energy-distribution-1.png",
+            "icon": "__randovania-assets__/graphics/technology/big-electric-pole.png",
             "icon_size": 256,
             "cost_reference": "stronger-explosives-2",
             "prerequisites": [
@@ -2459,4 +2466,4 @@
     "_randovania_meta": {
         "layout_was_user_modified": false
     }
-}
\ No newline at end of file
+}
diff --git a/tools/factorio/create_nodes.py b/tools/factorio/create_nodes.py
index facf6be69a..c5e2301c94 100644
--- a/tools/factorio/create_nodes.py
+++ b/tools/factorio/create_nodes.py
@@ -7,8 +7,8 @@
 
 import networkx
 
-from tools.factorio import util
-from tools.factorio.util import and_req, tech_req, template_req
+from randovania.games.factorio import importer_util
+from randovania.games.factorio.importer_util import tech_req, template_req
 
 _upgrade_techs_templates = {
     "refined-flammables-{}": 6,
@@ -29,31 +29,55 @@
 ]
 
 _k_tier_requirements = [
-    [],  # 1
+    # 1 and 2
     [],
-    [template_req("craft-transport-belt")],
+    [],
+    # 3
+    [
+        template_req("craft-transport-belt"),
+    ],
+    # 4: being able to transition away from burner tech
     [
         template_req("craft-assembling-machine-1"),
         template_req("craft-inserter"),
         template_req("craft-electric-mining-drill"),
         template_req("has-electricity"),
-    ],  # TODO: electric lab
+        # TODO: electric lab, in case we shuffle that
+    ],
+    # 5, around Oil Processing
     [
+        # TODO: fast smelting
         template_req("craft-fast-transport-belt"),
         template_req("craft-fast-inserter"),
         tech_req("railway"),
-    ],  # TODO: fast smelting
-    [tech_req("construction-robotics"), template_req("craft-assembling-machine-2")],
-    [template_req("craft-bulk-inserter")],
-    [tech_req("research-speed-6"), template_req("craft-assembling-machine-3")],
-    [tech_req("logistic-system")],
+    ],
+    # 6, initial utility/production science
+    [
+        tech_req("construction-robotics"),
+        template_req("craft-assembling-machine-2"),
+    ],
+    # 7, late game tech
+    [
+        template_req("craft-bulk-inserter"),
+    ],
+    # 8, Rocket Silo and similar
+    [
+        tech_req("research-speed-6"),
+        template_req("craft-assembling-machine-3"),
+    ],
+    # 9, Spidertron
+    [
+        tech_req("logistic-system"),
+    ],
+    # 10, Atomic Bomb
     [tech_req("productivity-module-3")],
 ]
 
 trivial_req = {"type": "and", "data": {"comment": None, "items": []}}
-areas = {
+areas: dict = {
     "Tech Tree": {
         "default_node": None,
+        "hint_features": [],
         "extra": {},
         "nodes": {
             "Spawn Point": {
@@ -71,7 +95,7 @@
 }
 
 
-def make_node_base(extra=None):
+def make_node_base(extra: dict | None = None) -> dict:
     return {
         "heal": False,
         "coordinates": None,
@@ -82,7 +106,7 @@ def make_node_base(extra=None):
     }
 
 
-def make_dock(target_area: str, target_node: str, connections=None):
+def make_dock(target_area: str, target_node: str, connections: dict | None = None) -> dict:
     return {
         "node_type": "dock",
         **make_node_base(),
@@ -102,9 +126,12 @@ def make_dock(target_area: str, target_node: str, connections=None):
     areas["Tech Tree"]["nodes"][f"Connection to Tier {tier + 1}"] = make_dock(
         f"Tier {tier + 1}", "Connection to Tech Tree", {"Spawn Point": trivial_req}
     )
-    areas["Tech Tree"]["nodes"]["Spawn Point"]["connections"][f"Connection to Tier {tier + 1}"] = and_req(req)
+    areas["Tech Tree"]["nodes"]["Spawn Point"]["connections"][f"Connection to Tier {tier + 1}"] = importer_util.and_req(
+        req
+    )
     areas[f"Tier {tier + 1}"] = {
         "default_node": None,
+        "hint_features": [],
         "extra": {},
         "nodes": {"Connection to Tech Tree": make_dock("Tech Tree", f"Connection to Tier {tier + 1}")},
     }
@@ -112,12 +139,13 @@ def make_dock(target_area: str, target_node: str, connections=None):
 current_pickup_index = -1
 
 
-def make_pickup(index: int, *, is_major: bool = False, connections=None) -> dict[str, typing.Any]:
+def make_pickup(index: int, *, is_major: bool = False, connections: dict | None = None) -> dict[str, typing.Any]:
     return {
         "node_type": "pickup",
         **make_node_base(),
         "pickup_index": index,
         "location_category": "major" if is_major else "minor",
+        "hint_features": [],
         "connections": connections or {},
     }
 
@@ -143,7 +171,7 @@ def gen_id() -> int:
     return gen_id
 
 
-def main():
+def main() -> None:
     parser = argparse.ArgumentParser()
     parser.add_argument("--factorio", type=Path, help="Path to the Factorio root folder.", required=True)
     args = parser.parse_args()
@@ -152,7 +180,7 @@ def main():
     rdv_factorio_path = Path(__file__).parents[2].joinpath("randovania/games/factorio")
     region_path = rdv_factorio_path.joinpath("logic_database/Tech.json")
 
-    util.read_locales(factorio_path)
+    importer_util.read_locales(factorio_path)
 
     raw_dump_path = factorio_path.joinpath("script-output/data-raw-dump.json")
 
@@ -161,18 +189,18 @@ def main():
 
     techs_raw = {key: value for key, value in raw_dump["technology"].items() if not key.startswith("randovania-")}
 
-    existing_ids = util.load_existing_pickup_ids(region_path)
+    existing_ids = importer_util.load_existing_pickup_ids(region_path)
     gen_id = make_gen_id(existing_ids)
 
     graph = networkx.DiGraph()
-    pack_for_tech = {}
-    pickup_nodes = {}
+    pack_for_tech: dict[str, tuple[str, ...] | None] = {}
+    pickup_nodes: dict[str, dict] = {}
 
     for tech_name, tech in techs_raw.items():
         if tech.get("randovania_custom_tech"):
             continue
 
-        packs = None
+        packs: tuple[str, ...] | None = None
         if "unit" in tech:
             packs = tuple(sorted(it[0] for it in tech["unit"]["ingredients"]))
             if "space-science-pack" in packs:
@@ -195,17 +223,18 @@ def main():
             )
 
         pickup_nodes[tech_name] = {
-            "node_name": f"Pickup ({util.get_localized_name(tech_name)})",
+            "node_name": f"Pickup ({importer_util.get_localized_name(tech_name)})",
             "tech_name": f"randovania-{new_tech}",
             "complexity": complexity,
         }
 
-    def requirement_for_tech(n: str):
+    def requirement_for_tech(n: str) -> dict:
         # code = "".join(it[0] for it in pack_for_tech[n]).upper()
         # print(f"{n:40} {cost_complexity:10d} -- {code:>6} x{tech['unit']['count']:<6} @ {tech['unit']['time']}s")
 
-        if pack_for_tech[n] is not None:
-            items = [{"type": "template", "data": f"craft-{pack.lower()}"} for pack in pack_for_tech[n]]
+        packs = pack_for_tech[n]
+        if packs is not None:
+            items = [{"type": "template", "data": f"craft-{pack.lower()}"} for pack in packs]
         else:
             trigger = techs_raw[n]["research_trigger"]
             match trigger["type"]:
diff --git a/tools/factorio/create_resources.py b/tools/factorio/create_resources.py
index e77a314e81..76dba0518d 100644
--- a/tools/factorio/create_resources.py
+++ b/tools/factorio/create_resources.py
@@ -4,10 +4,10 @@
 import typing
 from pathlib import Path
 
+from randovania.games.factorio import importer_util
 from randovania.games.factorio.data_importer import data_parser
+from randovania.games.factorio.importer_util import and_req, or_req, tech_req, template_req
 from randovania.lib import json_lib
-from tools.factorio import util
-from tools.factorio.util import and_req, or_req, tech_req, template_req
 
 _k_items_for_crafting_category = {
     "crafting": [],
@@ -120,7 +120,7 @@ def create_resources(header: dict, techs_for_recipe: dict) -> None:
 
     for tech_name, recipes_unlocked in techs_for_recipe.items():
         header["resource_database"]["items"][tech_name] = {
-            "long_name": util.get_localized_name(tech_name),
+            "long_name": importer_util.get_localized_name(tech_name),
             "max_capacity": 1,
             "extra": {"recipes_unlocked": recipes_unlocked},
         }
@@ -185,7 +185,7 @@ def update_templates(header: dict, recipes_raw: dict, techs_for_recipe: dict[str
 
     # Add the templates for crafting all recipes
     for item_name, recipes in data_parser.get_recipes_for(recipes_raw).items():
-        localized_name = util.get_localized_name(item_name)
+        localized_name = importer_util.get_localized_name(item_name)
 
         techs = set()
         for recipe in recipes:
@@ -207,7 +207,7 @@ def update_templates(header: dict, recipes_raw: dict, techs_for_recipe: dict[str
 
     # Mining all resources
     for resource_name, requirement in _k_miner_for_resource.items():
-        localized_name = util.get_localized_name(resource_name)
+        localized_name = importer_util.get_localized_name(resource_name)
         header["resource_database"]["requirement_template"][f"craft-{resource_name}"] = {
             "display_name": f"Mine {localized_name}",
             "requirement": requirement,
@@ -272,16 +272,15 @@ def create_pickups(techs_raw: dict, existing_pickup_ids: dict[str, int], tech_cs
                 icon = tech["icon"]
 
             result[pickup_name] = {
-                "pickup_category": data["category"],
+                "gui_category": data["category"],
                 "hint_features": [data["category"]],
                 "model_name": icon,
                 "offworld_models": {},
-                "progression": [tech_name],
                 "preferred_location_category": "major" if data["category"] != "enhancement" else "minor",
+                "description": "",
+                "progression": [tech_name],
                 "expected_case_for_describer": "shuffled",
             }
-            if tech_name in existing_pickup_ids:
-                result[pickup_name]["original_locations"] = [existing_pickup_ids[tech_name]]
 
             if pickup_name in _custom_shuffled_count:
                 if _custom_shuffled_count[pickup_name] == 0:
@@ -289,15 +288,21 @@ def create_pickups(techs_raw: dict, existing_pickup_ids: dict[str, int], tech_cs
                 else:
                     result[pickup_name]["custom_count_for_shuffled_case"] = _custom_shuffled_count[pickup_name]
 
+            if tech_name in existing_pickup_ids:
+                result[pickup_name]["original_locations"] = [existing_pickup_ids[tech_name]]
+
     for pickup in result.values():
         if len(pickup["progression"]) > 1:
             pickup["description"] = "Provides in order: " + " → ".join(
-                util.get_localized_name(tech_name) for tech_name in pickup["progression"]
+                importer_util.get_localized_name(tech_name) for tech_name in pickup["progression"]
             )
+        else:
+            pickup.pop("description")
 
     result["Uranium Mining"]["expected_case_for_describer"] = "starting_item"
     result["Rocket Silo"]["expected_case_for_describer"] = "vanilla"
     result["Rocket Silo"]["hide_from_gui"] = True
+    result["Rocket Silo"]["original_locations"] = result["Rocket Silo"].pop("original_locations")  # move to the end
 
     return result
 
@@ -326,10 +331,10 @@ def main():
     header_path = rdv_factorio_path.joinpath("logic_database/header.json")
 
     raw_dump_path = factorio_path.joinpath("script-output/data-raw-dump.json")
-    util.read_locales(factorio_path)
+    importer_util.read_locales(factorio_path)
     tech_csv = read_tech_csv(csv_path)
 
-    existing_pickup_ids = util.load_existing_pickup_ids(rdv_factorio_path.joinpath("logic_database/Tech.json"))
+    existing_pickup_ids = importer_util.load_existing_pickup_ids(rdv_factorio_path.joinpath("logic_database/Tech.json"))
 
     with raw_dump_path.open() as f:
         raw_dump: dict[str, dict[str, typing.Any]] = json.load(f)


Reviews:
user: login: duncathan
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T16:31:24Z
user: login: henriquegemignani
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-12T16:31:43Z
user: login: duncathan
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T16:45:00Z
user: login: henriquegemignani
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-12T17:01:36Z
user: login: duncathan
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T17:42:27Z
user: login: henriquegemignani
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-12T18:33:58Z
user: login: henriquegemignani
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-12T18:35:59Z
user: login: duncathan
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T19:35:09Z

Review comments:
diff_hunk: @@ -68,14 +76,16 @@ def create_game_specific(
             "production-science-pack": (all_items, 2),
             "utility-science-pack": (all_items, 2),
             "rocket-part": (all_items, 0),
+            "satellite": (all_items, 3),
path: randovania/games/factorio/generator/base_patches_factory.py
user: login: duncathan
body: oil refinery satellite recipe :eyes: 
created_at: 2025-03-12T16:31:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -68,14 +76,16 @@ def create_game_specific(
             "production-science-pack": (all_items, 2),
             "utility-science-pack": (all_items, 2),
             "rocket-part": (all_items, 0),
+            "satellite": (all_items, 3),
path: randovania/games/factorio/generator/base_patches_factory.py
user: login: henriquegemignani
body: one can dream
created_at: 2025-03-12T16:31:43Z
author_association: MEMBER
diff_hunk: @@ -33,7 +33,12 @@
       <item>
        <widget class="QLabel" name="intro_label">
         <property name="text">
-         <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Build a factory, research new technologies and automate the whole process in order to launch a rocket.&lt;/p&gt;&lt;p&gt;What each technology you unlock gives is shuffled, but which science packs you'll need and how many is still the same. However, the recipes for each pack plus the rocket is random!&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+         <string>Build a factory, research new technologies and automate the whole process in order to launch a rocket.
+
+What each technology you unlock gives is shuffled, but which science packs you'll need and how many is still the same. However, the recipes for each pack plus the rocket and satellite is random!</string>
path: randovania/games/factorio/gui/ui_files/games_tab_factorio_widget.ui
user: login: duncathan
body: are random*
created_at: 2025-03-12T16:32:48Z
author_association: CONTRIBUTOR
diff_hunk: @@ -107,7 +112,100 @@
            <string># updated from code</string>
           </property>
           <property name="alignment">
-           <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+           <set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
+          </property>
+          <property name="wordWrap">
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QWidget" name="differences_tab">
+   <attribute name="title">
+    <string>Differences</string>
+   </attribute>
+   <layout class="QVBoxLayout" name="differences_layout">
+    <property name="leftMargin">
+     <number>0</number>
+    </property>
+    <property name="topMargin">
+     <number>0</number>
+    </property>
+    <property name="rightMargin">
+     <number>0</number>
+    </property>
+    <property name="bottomMargin">
+     <number>0</number>
+    </property>
+    <item>
+     <widget class="QScrollArea" name="differences_scroll_area">
+      <property name="widgetResizable">
+       <bool>true</bool>
+      </property>
+      <widget class="QWidget" name="differences_scroll_area_contents">
+       <property name="geometry">
+        <rect>
+         <x>0</x>
+         <y>0</y>
+         <width>489</width>
+         <height>615</height>
+        </rect>
+       </property>
+       <layout class="QVBoxLayout" name="differences_scroll_layout">
+        <item>
+         <widget class="QLabel" name="differences_label">
+          <property name="text">
+           <string>### Freebies
+Whenever a technology is researched, any eligible item from the unlocked recipes are distributed to online players as bonus.
path: randovania/games/factorio/gui/ui_files/games_tab_factorio_widget.ui
user: login: duncathan
body: items*
created_at: 2025-03-12T16:33:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -107,7 +112,100 @@
            <string># updated from code</string>
           </property>
           <property name="alignment">
-           <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+           <set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
+          </property>
+          <property name="wordWrap">
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QWidget" name="differences_tab">
+   <attribute name="title">
+    <string>Differences</string>
+   </attribute>
+   <layout class="QVBoxLayout" name="differences_layout">
+    <property name="leftMargin">
+     <number>0</number>
+    </property>
+    <property name="topMargin">
+     <number>0</number>
+    </property>
+    <property name="rightMargin">
+     <number>0</number>
+    </property>
+    <property name="bottomMargin">
+     <number>0</number>
+    </property>
+    <item>
+     <widget class="QScrollArea" name="differences_scroll_area">
+      <property name="widgetResizable">
+       <bool>true</bool>
+      </property>
+      <widget class="QWidget" name="differences_scroll_area_contents">
+       <property name="geometry">
+        <rect>
+         <x>0</x>
+         <y>0</y>
+         <width>489</width>
+         <height>615</height>
+        </rect>
+       </property>
+       <layout class="QVBoxLayout" name="differences_scroll_layout">
+        <item>
+         <widget class="QLabel" name="differences_label">
+          <property name="text">
+           <string>### Freebies
+Whenever a technology is researched, any eligible item from the unlocked recipes are distributed to online players as bonus.
+
+Using buildings adquired by freebies is expected by logic, such as solar panels for power.
path: randovania/games/factorio/gui/ui_files/games_tab_factorio_widget.ui
user: login: duncathan
body: acquired*
created_at: 2025-03-12T16:33:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -107,7 +112,100 @@
            <string># updated from code</string>
           </property>
           <property name="alignment">
-           <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+           <set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
+          </property>
+          <property name="wordWrap">
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QWidget" name="differences_tab">
+   <attribute name="title">
+    <string>Differences</string>
+   </attribute>
+   <layout class="QVBoxLayout" name="differences_layout">
+    <property name="leftMargin">
+     <number>0</number>
+    </property>
+    <property name="topMargin">
+     <number>0</number>
+    </property>
+    <property name="rightMargin">
+     <number>0</number>
+    </property>
+    <property name="bottomMargin">
+     <number>0</number>
+    </property>
+    <item>
+     <widget class="QScrollArea" name="differences_scroll_area">
+      <property name="widgetResizable">
+       <bool>true</bool>
+      </property>
+      <widget class="QWidget" name="differences_scroll_area_contents">
+       <property name="geometry">
+        <rect>
+         <x>0</x>
+         <y>0</y>
+         <width>489</width>
+         <height>615</height>
+        </rect>
+       </property>
+       <layout class="QVBoxLayout" name="differences_scroll_layout">
+        <item>
+         <widget class="QLabel" name="differences_label">
+          <property name="text">
+           <string>### Freebies
+Whenever a technology is researched, any eligible item from the unlocked recipes are distributed to online players as bonus.
+
+Using buildings adquired by freebies is expected by logic, such as solar panels for power.
path: randovania/games/factorio/gui/ui_files/games_tab_factorio_widget.ui
user: login: duncathan
body: (it's ok, mercurysteam makes this same mistake lmao)
created_at: 2025-03-12T16:33:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -107,7 +112,100 @@
            <string># updated from code</string>
           </property>
           <property name="alignment">
-           <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+           <set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
+          </property>
+          <property name="wordWrap">
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QWidget" name="differences_tab">
+   <attribute name="title">
+    <string>Differences</string>
+   </attribute>
+   <layout class="QVBoxLayout" name="differences_layout">
+    <property name="leftMargin">
+     <number>0</number>
+    </property>
+    <property name="topMargin">
+     <number>0</number>
+    </property>
+    <property name="rightMargin">
+     <number>0</number>
+    </property>
+    <property name="bottomMargin">
+     <number>0</number>
+    </property>
+    <item>
+     <widget class="QScrollArea" name="differences_scroll_area">
+      <property name="widgetResizable">
+       <bool>true</bool>
+      </property>
+      <widget class="QWidget" name="differences_scroll_area_contents">
+       <property name="geometry">
+        <rect>
+         <x>0</x>
+         <y>0</y>
+         <width>489</width>
+         <height>615</height>
+        </rect>
+       </property>
+       <layout class="QVBoxLayout" name="differences_scroll_layout">
+        <item>
+         <widget class="QLabel" name="differences_label">
+          <property name="text">
+           <string>### Freebies
+Whenever a technology is researched, any eligible item from the unlocked recipes are distributed to online players as bonus.
+
+Using buildings adquired by freebies is expected by logic, such as solar panels for power.
+
+#### Eligible Items
+- Placeable entities, such as buildings and robots.
+- Armors, equipment and guns.
+
+### New Items and Recipes
+- Burner Assembling Machine and Burner Lab have been added, always available.
+- Longer Handed Inserter have been added with it's own technology.
path: randovania/games/factorio/gui/ui_files/games_tab_factorio_widget.ui
user: login: duncathan
body: Longer Handed Inserters have been added, with a unique technology to unlock them.
created_at: 2025-03-12T16:34:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -107,7 +112,100 @@
            <string># updated from code</string>
           </property>
           <property name="alignment">
-           <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+           <set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
+          </property>
+          <property name="wordWrap">
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QWidget" name="differences_tab">
+   <attribute name="title">
+    <string>Differences</string>
+   </attribute>
+   <layout class="QVBoxLayout" name="differences_layout">
+    <property name="leftMargin">
+     <number>0</number>
+    </property>
+    <property name="topMargin">
+     <number>0</number>
+    </property>
+    <property name="rightMargin">
+     <number>0</number>
+    </property>
+    <property name="bottomMargin">
+     <number>0</number>
+    </property>
+    <item>
+     <widget class="QScrollArea" name="differences_scroll_area">
+      <property name="widgetResizable">
+       <bool>true</bool>
+      </property>
+      <widget class="QWidget" name="differences_scroll_area_contents">
+       <property name="geometry">
+        <rect>
+         <x>0</x>
+         <y>0</y>
+         <width>489</width>
+         <height>615</height>
+        </rect>
+       </property>
+       <layout class="QVBoxLayout" name="differences_scroll_layout">
+        <item>
+         <widget class="QLabel" name="differences_label">
+          <property name="text">
+           <string>### Freebies
+Whenever a technology is researched, any eligible item from the unlocked recipes are distributed to online players as bonus.
+
+Using buildings adquired by freebies is expected by logic, such as solar panels for power.
+
+#### Eligible Items
+- Placeable entities, such as buildings and robots.
+- Armors, equipment and guns.
+
+### New Items and Recipes
+- Burner Assembling Machine and Burner Lab have been added, always available.
+- Longer Handed Inserter have been added with it's own technology.
+- Alternative recipes for handcrafting Construction and Logistic Robots were added.
+
+### Starting Items and Recipes
+- Transport Belts are now unlocked by Logistics 1, along with Underground Belts and Splitters.
+- Pipes and Pipe to Ground are now unlocked by default, instead of Steam Power.
path: randovania/games/factorio/gui/ui_files/games_tab_factorio_widget.ui
user: login: duncathan
body: instead of by Steam Power*
created_at: 2025-03-12T16:34:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -107,7 +112,100 @@
            <string># updated from code</string>
           </property>
           <property name="alignment">
-           <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+           <set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
+          </property>
+          <property name="wordWrap">
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QWidget" name="differences_tab">
+   <attribute name="title">
+    <string>Differences</string>
+   </attribute>
+   <layout class="QVBoxLayout" name="differences_layout">
+    <property name="leftMargin">
+     <number>0</number>
+    </property>
+    <property name="topMargin">
+     <number>0</number>
+    </property>
+    <property name="rightMargin">
+     <number>0</number>
+    </property>
+    <property name="bottomMargin">
+     <number>0</number>
+    </property>
+    <item>
+     <widget class="QScrollArea" name="differences_scroll_area">
+      <property name="widgetResizable">
+       <bool>true</bool>
+      </property>
+      <widget class="QWidget" name="differences_scroll_area_contents">
+       <property name="geometry">
+        <rect>
+         <x>0</x>
+         <y>0</y>
+         <width>489</width>
+         <height>615</height>
+        </rect>
+       </property>
+       <layout class="QVBoxLayout" name="differences_scroll_layout">
+        <item>
+         <widget class="QLabel" name="differences_label">
+          <property name="text">
+           <string>### Freebies
+Whenever a technology is researched, any eligible item from the unlocked recipes are distributed to online players as bonus.
+
+Using buildings adquired by freebies is expected by logic, such as solar panels for power.
+
+#### Eligible Items
+- Placeable entities, such as buildings and robots.
+- Armors, equipment and guns.
+
+### New Items and Recipes
+- Burner Assembling Machine and Burner Lab have been added, always available.
+- Longer Handed Inserter have been added with it's own technology.
+- Alternative recipes for handcrafting Construction and Logistic Robots were added.
+
+### Starting Items and Recipes
+- Transport Belts are now unlocked by Logistics 1, along with Underground Belts and Splitters.
+- Pipes and Pipe to Ground are now unlocked by default, instead of Steam Power.
+- Copper Cable, Small Electric Poles, Iron Stick and Labs are once again unlocked by default.
+
+### Inserter Changes
+- Inserter have it's own technology.
path: randovania/games/factorio/gui/ui_files/games_tab_factorio_widget.ui
user: login: duncathan
body: Inserters now have their own technology, progressive with Fast Inserters and Bulk Inserters.
created_at: 2025-03-12T16:35:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -107,7 +112,100 @@
            <string># updated from code</string>
           </property>
           <property name="alignment">
-           <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+           <set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
+          </property>
+          <property name="wordWrap">
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QWidget" name="differences_tab">
+   <attribute name="title">
+    <string>Differences</string>
+   </attribute>
+   <layout class="QVBoxLayout" name="differences_layout">
+    <property name="leftMargin">
+     <number>0</number>
+    </property>
+    <property name="topMargin">
+     <number>0</number>
+    </property>
+    <property name="rightMargin">
+     <number>0</number>
+    </property>
+    <property name="bottomMargin">
+     <number>0</number>
+    </property>
+    <item>
+     <widget class="QScrollArea" name="differences_scroll_area">
+      <property name="widgetResizable">
+       <bool>true</bool>
+      </property>
+      <widget class="QWidget" name="differences_scroll_area_contents">
+       <property name="geometry">
+        <rect>
+         <x>0</x>
+         <y>0</y>
+         <width>489</width>
+         <height>615</height>
+        </rect>
+       </property>
+       <layout class="QVBoxLayout" name="differences_scroll_layout">
+        <item>
+         <widget class="QLabel" name="differences_label">
+          <property name="text">
+           <string>### Freebies
+Whenever a technology is researched, any eligible item from the unlocked recipes are distributed to online players as bonus.
+
+Using buildings adquired by freebies is expected by logic, such as solar panels for power.
+
+#### Eligible Items
+- Placeable entities, such as buildings and robots.
+- Armors, equipment and guns.
+
+### New Items and Recipes
+- Burner Assembling Machine and Burner Lab have been added, always available.
+- Longer Handed Inserter have been added with it's own technology.
+- Alternative recipes for handcrafting Construction and Logistic Robots were added.
+
+### Starting Items and Recipes
+- Transport Belts are now unlocked by Logistics 1, along with Underground Belts and Splitters.
+- Pipes and Pipe to Ground are now unlocked by default, instead of Steam Power.
+- Copper Cable, Small Electric Poles, Iron Stick and Labs are once again unlocked by default.
+
+### Inserter Changes
+- Inserter have it's own technology.
+- Long Handed Inserter is unlocked by it's own technology.
path: randovania/games/factorio/gui/ui_files/games_tab_factorio_widget.ui
user: login: duncathan
body: Long Handed Inserters are unlocked by their own technology, separately from other Inserters.
created_at: 2025-03-12T16:36:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -107,7 +112,100 @@
            <string># updated from code</string>
           </property>
           <property name="alignment">
-           <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+           <set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
+          </property>
+          <property name="wordWrap">
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QWidget" name="differences_tab">
+   <attribute name="title">
+    <string>Differences</string>
+   </attribute>
+   <layout class="QVBoxLayout" name="differences_layout">
+    <property name="leftMargin">
+     <number>0</number>
+    </property>
+    <property name="topMargin">
+     <number>0</number>
+    </property>
+    <property name="rightMargin">
+     <number>0</number>
+    </property>
+    <property name="bottomMargin">
+     <number>0</number>
+    </property>
+    <item>
+     <widget class="QScrollArea" name="differences_scroll_area">
+      <property name="widgetResizable">
+       <bool>true</bool>
+      </property>
+      <widget class="QWidget" name="differences_scroll_area_contents">
+       <property name="geometry">
+        <rect>
+         <x>0</x>
+         <y>0</y>
+         <width>489</width>
+         <height>615</height>
+        </rect>
+       </property>
+       <layout class="QVBoxLayout" name="differences_scroll_layout">
+        <item>
+         <widget class="QLabel" name="differences_label">
+          <property name="text">
+           <string>### Freebies
+Whenever a technology is researched, any eligible item from the unlocked recipes are distributed to online players as bonus.
+
+Using buildings adquired by freebies is expected by logic, such as solar panels for power.
+
+#### Eligible Items
+- Placeable entities, such as buildings and robots.
+- Armors, equipment and guns.
+
+### New Items and Recipes
+- Burner Assembling Machine and Burner Lab have been added, always available.
+- Longer Handed Inserter have been added with it's own technology.
+- Alternative recipes for handcrafting Construction and Logistic Robots were added.
+
+### Starting Items and Recipes
+- Transport Belts are now unlocked by Logistics 1, along with Underground Belts and Splitters.
+- Pipes and Pipe to Ground are now unlocked by default, instead of Steam Power.
+- Copper Cable, Small Electric Poles, Iron Stick and Labs are once again unlocked by default.
+
+### Inserter Changes
+- Inserter have it's own technology.
+- Long Handed Inserter is unlocked by it's own technology.
+- Inserter Capactity Bonus is split into Regular and Bulk Inserter Capacity Bonus.
+
+### Fluid Changes
+- Fluid Handling now only unlocks barreling recipes.
+- New technology Fluid Storage has been added that unlocks Fluid Tanks and Pumps.
+- Fluid Wagons no longer have Storage Tanks as an ingredient and the technology also unlocks Pumps.
+
+### Oil Changes
+- Solid Fuel recipes are now unlocked only by the new technology Solid Fuel.
+- Heavy and Light Oil Cracking is now unlocked by the Oil Cracking technology.
path: randovania/games/factorio/gui/ui_files/games_tab_factorio_widget.ui
user: login: duncathan
body: are now unlocked*
created_at: 2025-03-12T16:37:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -107,7 +112,100 @@
            <string># updated from code</string>
           </property>
           <property name="alignment">
-           <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+           <set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
+          </property>
+          <property name="wordWrap">
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QWidget" name="differences_tab">
+   <attribute name="title">
+    <string>Differences</string>
+   </attribute>
+   <layout class="QVBoxLayout" name="differences_layout">
+    <property name="leftMargin">
+     <number>0</number>
+    </property>
+    <property name="topMargin">
+     <number>0</number>
+    </property>
+    <property name="rightMargin">
+     <number>0</number>
+    </property>
+    <property name="bottomMargin">
+     <number>0</number>
+    </property>
+    <item>
+     <widget class="QScrollArea" name="differences_scroll_area">
+      <property name="widgetResizable">
+       <bool>true</bool>
+      </property>
+      <widget class="QWidget" name="differences_scroll_area_contents">
+       <property name="geometry">
+        <rect>
+         <x>0</x>
+         <y>0</y>
+         <width>489</width>
+         <height>615</height>
+        </rect>
+       </property>
+       <layout class="QVBoxLayout" name="differences_scroll_layout">
+        <item>
+         <widget class="QLabel" name="differences_label">
+          <property name="text">
+           <string>### Freebies
+Whenever a technology is researched, any eligible item from the unlocked recipes are distributed to online players as bonus.
+
+Using buildings adquired by freebies is expected by logic, such as solar panels for power.
+
+#### Eligible Items
+- Placeable entities, such as buildings and robots.
+- Armors, equipment and guns.
+
+### New Items and Recipes
+- Burner Assembling Machine and Burner Lab have been added, always available.
+- Longer Handed Inserter have been added with it's own technology.
+- Alternative recipes for handcrafting Construction and Logistic Robots were added.
+
+### Starting Items and Recipes
+- Transport Belts are now unlocked by Logistics 1, along with Underground Belts and Splitters.
+- Pipes and Pipe to Ground are now unlocked by default, instead of Steam Power.
+- Copper Cable, Small Electric Poles, Iron Stick and Labs are once again unlocked by default.
+
+### Inserter Changes
+- Inserter have it's own technology.
+- Long Handed Inserter is unlocked by it's own technology.
+- Inserter Capactity Bonus is split into Regular and Bulk Inserter Capacity Bonus.
+
+### Fluid Changes
+- Fluid Handling now only unlocks barreling recipes.
+- New technology Fluid Storage has been added that unlocks Fluid Tanks and Pumps.
+- Fluid Wagons no longer have Storage Tanks as an ingredient and the technology also unlocks Pumps.
+
+### Oil Changes
+- Solid Fuel recipes are now unlocked only by the new technology Solid Fuel.
+- Heavy and Light Oil Cracking is now unlocked by the Oil Cracking technology.
+- Coal Liquefaction now uses Steam as an ingredient instead of Heavy Oil and also unlocks Refineries.
+
+### Changed and New Technologies
+- Electronics now unlocks only Electronic Circuits.
+- Medium Electric Pole and Big Electric Pole are now unlocked by separate technologies.
+- Research Productivity has been added.
path: randovania/games/factorio/gui/ui_files/games_tab_factorio_widget.ui
user: login: duncathan
body: wasn't research speed buffed, too? that might warrant a mention
created_at: 2025-03-12T16:38:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -102,9 +126,12 @@ def make_dock(target_area: str, target_node: str, connections=None):
     areas["Tech Tree"]["nodes"][f"Connection to Tier {tier + 1}"] = make_dock(
         f"Tier {tier + 1}", "Connection to Tech Tree", {"Spawn Point": trivial_req}
     )
-    areas["Tech Tree"]["nodes"]["Spawn Point"]["connections"][f"Connection to Tier {tier + 1}"] = and_req(req)
+    areas["Tech Tree"]["nodes"]["Spawn Point"]["connections"][f"Connection to Tier {tier + 1}"] = importer_util.and_req(
path: tools/factorio/create_nodes.py
user: login: duncathan
body: mypy
created_at: 2025-03-12T16:44:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -107,7 +112,100 @@
            <string># updated from code</string>
           </property>
           <property name="alignment">
-           <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+           <set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
+          </property>
+          <property name="wordWrap">
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QWidget" name="differences_tab">
+   <attribute name="title">
+    <string>Differences</string>
+   </attribute>
+   <layout class="QVBoxLayout" name="differences_layout">
+    <property name="leftMargin">
+     <number>0</number>
+    </property>
+    <property name="topMargin">
+     <number>0</number>
+    </property>
+    <property name="rightMargin">
+     <number>0</number>
+    </property>
+    <property name="bottomMargin">
+     <number>0</number>
+    </property>
+    <item>
+     <widget class="QScrollArea" name="differences_scroll_area">
+      <property name="widgetResizable">
+       <bool>true</bool>
+      </property>
+      <widget class="QWidget" name="differences_scroll_area_contents">
+       <property name="geometry">
+        <rect>
+         <x>0</x>
+         <y>0</y>
+         <width>489</width>
+         <height>615</height>
+        </rect>
+       </property>
+       <layout class="QVBoxLayout" name="differences_scroll_layout">
+        <item>
+         <widget class="QLabel" name="differences_label">
+          <property name="text">
+           <string>### Freebies
+Whenever a technology is researched, any eligible item from the unlocked recipes are distributed to online players as bonus.
+
+Using buildings adquired by freebies is expected by logic, such as solar panels for power.
+
+#### Eligible Items
+- Placeable entities, such as buildings and robots.
+- Armors, equipment and guns.
+
+### New Items and Recipes
+- Burner Assembling Machine and Burner Lab have been added, always available.
+- Longer Handed Inserter have been added with it's own technology.
+- Alternative recipes for handcrafting Construction and Logistic Robots were added.
+
+### Starting Items and Recipes
+- Transport Belts are now unlocked by Logistics 1, along with Underground Belts and Splitters.
+- Pipes and Pipe to Ground are now unlocked by default, instead of Steam Power.
+- Copper Cable, Small Electric Poles, Iron Stick and Labs are once again unlocked by default.
+
+### Inserter Changes
+- Inserter have it's own technology.
+- Long Handed Inserter is unlocked by it's own technology.
+- Inserter Capactity Bonus is split into Regular and Bulk Inserter Capacity Bonus.
+
+### Fluid Changes
+- Fluid Handling now only unlocks barreling recipes.
+- New technology Fluid Storage has been added that unlocks Fluid Tanks and Pumps.
+- Fluid Wagons no longer have Storage Tanks as an ingredient and the technology also unlocks Pumps.
+
+### Oil Changes
+- Solid Fuel recipes are now unlocked only by the new technology Solid Fuel.
+- Heavy and Light Oil Cracking is now unlocked by the Oil Cracking technology.
+- Coal Liquefaction now uses Steam as an ingredient instead of Heavy Oil and also unlocks Refineries.
+
+### Changed and New Technologies
+- Electronics now unlocks only Electronic Circuits.
+- Medium Electric Pole and Big Electric Pole are now unlocked by separate technologies.
+- Research Productivity has been added.
path: randovania/games/factorio/gui/ui_files/games_tab_factorio_widget.ui
user: login: henriquegemignani
body: It's weird. It's not buffed, but it uses the speed of the last level you'd normally research. In vanilla each tier you research gives more speed in absolute than the previous one.
created_at: 2025-03-12T17:01:36Z
author_association: MEMBER
diff_hunk: @@ -107,7 +112,100 @@
            <string># updated from code</string>
           </property>
           <property name="alignment">
-           <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+           <set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
+          </property>
+          <property name="wordWrap">
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QWidget" name="differences_tab">
+   <attribute name="title">
+    <string>Differences</string>
+   </attribute>
+   <layout class="QVBoxLayout" name="differences_layout">
+    <property name="leftMargin">
+     <number>0</number>
+    </property>
+    <property name="topMargin">
+     <number>0</number>
+    </property>
+    <property name="rightMargin">
+     <number>0</number>
+    </property>
+    <property name="bottomMargin">
+     <number>0</number>
+    </property>
+    <item>
+     <widget class="QScrollArea" name="differences_scroll_area">
+      <property name="widgetResizable">
+       <bool>true</bool>
+      </property>
+      <widget class="QWidget" name="differences_scroll_area_contents">
+       <property name="geometry">
+        <rect>
+         <x>0</x>
+         <y>0</y>
+         <width>489</width>
+         <height>615</height>
+        </rect>
+       </property>
+       <layout class="QVBoxLayout" name="differences_scroll_layout">
+        <item>
+         <widget class="QLabel" name="differences_label">
+          <property name="text">
+           <string>### Freebies
+Whenever a technology is researched, any eligible item from the unlocked recipes are distributed to online players as bonus.
+
+Using buildings adquired by freebies is expected by logic, such as solar panels for power.
+
+#### Eligible Items
+- Placeable entities, such as buildings and robots.
+- Armors, equipment and guns.
+
+### New Items and Recipes
+- Burner Assembling Machine and Burner Lab have been added, always available.
+- Longer Handed Inserter have been added with it's own technology.
+- Alternative recipes for handcrafting Construction and Logistic Robots were added.
+
+### Starting Items and Recipes
+- Transport Belts are now unlocked by Logistics 1, along with Underground Belts and Splitters.
+- Pipes and Pipe to Ground are now unlocked by default, instead of Steam Power.
+- Copper Cable, Small Electric Poles, Iron Stick and Labs are once again unlocked by default.
+
+### Inserter Changes
+- Inserter have it's own technology.
+- Long Handed Inserter is unlocked by it's own technology.
+- Inserter Capactity Bonus is split into Regular and Bulk Inserter Capacity Bonus.
+
+### Fluid Changes
+- Fluid Handling now only unlocks barreling recipes.
+- New technology Fluid Storage has been added that unlocks Fluid Tanks and Pumps.
+- Fluid Wagons no longer have Storage Tanks as an ingredient and the technology also unlocks Pumps.
+
+### Oil Changes
+- Solid Fuel recipes are now unlocked only by the new technology Solid Fuel.
+- Heavy and Light Oil Cracking is now unlocked by the Oil Cracking technology.
+- Coal Liquefaction now uses Steam as an ingredient instead of Heavy Oil and also unlocks Refineries.
+
+### Changed and New Technologies
+- Electronics now unlocks only Electronic Circuits.
+- Medium Electric Pole and Big Electric Pole are now unlocked by separate technologies.
+- Research Productivity has been added.
path: randovania/games/factorio/gui/ui_files/games_tab_factorio_widget.ui
user: login: duncathan
body: that sounds like a buff to mii? at any given tier of the research you have more speed than you would in vanilla
created_at: 2025-03-12T17:42:27Z
author_association: CONTRIBUTOR
diff_hunk: @@ -107,7 +112,100 @@
            <string># updated from code</string>
           </property>
           <property name="alignment">
-           <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+           <set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
+          </property>
+          <property name="wordWrap">
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QWidget" name="differences_tab">
+   <attribute name="title">
+    <string>Differences</string>
+   </attribute>
+   <layout class="QVBoxLayout" name="differences_layout">
+    <property name="leftMargin">
+     <number>0</number>
+    </property>
+    <property name="topMargin">
+     <number>0</number>
+    </property>
+    <property name="rightMargin">
+     <number>0</number>
+    </property>
+    <property name="bottomMargin">
+     <number>0</number>
+    </property>
+    <item>
+     <widget class="QScrollArea" name="differences_scroll_area">
+      <property name="widgetResizable">
+       <bool>true</bool>
+      </property>
+      <widget class="QWidget" name="differences_scroll_area_contents">
+       <property name="geometry">
+        <rect>
+         <x>0</x>
+         <y>0</y>
+         <width>489</width>
+         <height>615</height>
+        </rect>
+       </property>
+       <layout class="QVBoxLayout" name="differences_scroll_layout">
+        <item>
+         <widget class="QLabel" name="differences_label">
+          <property name="text">
+           <string>### Freebies
+Whenever a technology is researched, any eligible item from the unlocked recipes are distributed to online players as bonus.
+
+Using buildings adquired by freebies is expected by logic, such as solar panels for power.
path: randovania/games/factorio/gui/ui_files/games_tab_factorio_widget.ui
user: login: henriquegemignani
body: [I am not surprised](https://dicionario.priberam.org/adquirir) I messed this up...
created_at: 2025-03-12T18:33:58Z
author_association: MEMBER
diff_hunk: @@ -107,7 +112,100 @@
            <string># updated from code</string>
           </property>
           <property name="alignment">
-           <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+           <set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
+          </property>
+          <property name="wordWrap">
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QWidget" name="differences_tab">
+   <attribute name="title">
+    <string>Differences</string>
+   </attribute>
+   <layout class="QVBoxLayout" name="differences_layout">
+    <property name="leftMargin">
+     <number>0</number>
+    </property>
+    <property name="topMargin">
+     <number>0</number>
+    </property>
+    <property name="rightMargin">
+     <number>0</number>
+    </property>
+    <property name="bottomMargin">
+     <number>0</number>
+    </property>
+    <item>
+     <widget class="QScrollArea" name="differences_scroll_area">
+      <property name="widgetResizable">
+       <bool>true</bool>
+      </property>
+      <widget class="QWidget" name="differences_scroll_area_contents">
+       <property name="geometry">
+        <rect>
+         <x>0</x>
+         <y>0</y>
+         <width>489</width>
+         <height>615</height>
+        </rect>
+       </property>
+       <layout class="QVBoxLayout" name="differences_scroll_layout">
+        <item>
+         <widget class="QLabel" name="differences_label">
+          <property name="text">
+           <string>### Freebies
+Whenever a technology is researched, any eligible item from the unlocked recipes are distributed to online players as bonus.
+
+Using buildings adquired by freebies is expected by logic, such as solar panels for power.
+
+#### Eligible Items
+- Placeable entities, such as buildings and robots.
+- Armors, equipment and guns.
+
+### New Items and Recipes
+- Burner Assembling Machine and Burner Lab have been added, always available.
+- Longer Handed Inserter have been added with it's own technology.
+- Alternative recipes for handcrafting Construction and Logistic Robots were added.
+
+### Starting Items and Recipes
+- Transport Belts are now unlocked by Logistics 1, along with Underground Belts and Splitters.
+- Pipes and Pipe to Ground are now unlocked by default, instead of Steam Power.
+- Copper Cable, Small Electric Poles, Iron Stick and Labs are once again unlocked by default.
+
+### Inserter Changes
+- Inserter have it's own technology.
path: randovania/games/factorio/gui/ui_files/games_tab_factorio_widget.ui
user: login: henriquegemignani
body: I actually wrote about being progressive at first, then decided to skip mentioning these. Not opposed, was just trying to reduce the number of words
created_at: 2025-03-12T18:35:59Z
author_association: MEMBER

PR comments:
user: login: codecov[bot]
created_at: 2025-03-12T00:15:34Z
body: ## [Codecov](https://app.codecov.io/gh/randovania/randovania/pull/8088?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=randovania) Report
Attention: Patch coverage is `96.15385%` with `1 line` in your changes missing coverage. Please review.
> Project coverage is 85.67%. Comparing base [(`c0ebc09`)](https://app.codecov.io/gh/randovania/randovania/commit/c0ebc09bbc592b2f5adde09e5eab7adadc44b54d?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=randovania) to head [(`8c070d1`)](https://app.codecov.io/gh/randovania/randovania/commit/8c070d12fe5be05da6fe039940993ab14926d77d?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=randovania).
> Report is 13 commits behind head on main.

| [Files with missing lines](https://app.codecov.io/gh/randovania/randovania/pull/8088?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=randovania) | Patch % | Lines |
|---|---|---|
| [randovania/games/factorio/importer\_util.py](https://app.codecov.io/gh/randovania/randovania/pull/8088?src=pr&el=tree&filepath=randovania%2Fgames%2Ffactorio%2Fimporter_util.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=randovania#diff-cmFuZG92YW5pYS9nYW1lcy9mYWN0b3Jpby9pbXBvcnRlcl91dGlsLnB5) | 0.00% | [1 Missing :warning: ](https://app.codecov.io/gh/randovania/randovania/pull/8088?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=randovania) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main    #8088      +/-   ##
==========================================
- Coverage   85.77%   85.67%   -0.10%     
==========================================
  Files         680      681       +1     
  Lines       43929    44000      +71     
  Branches     5465     5478      +13     
==========================================
+ Hits        37678    37696      +18     
- Misses       5041     5094      +53     
  Partials     1210     1210              
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/randovania/randovania/pull/8088?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=randovania).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=randovania).

<details><summary>🚀 New features to boost your workflow: </summary>

- ❄ [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
author_association: NONE
user: login: henriquegemignani
created_at: 2025-03-12T19:24:52Z
body: ![image](https://github.com/user-attachments/assets/c4125faa-5ea0-45d6-8520-5434ec1b1ae2)

author_association: MEMBER
user: login: henriquegemignani
created_at: 2025-03-12T19:25:18Z
body: PR finished, comments addressed
author_association: MEMBER