diff --git a/changelog.txt b/changelog.txt index 02f3166..dbef263 100644 --- a/changelog.txt +++ b/changelog.txt @@ -10,8 +10,11 @@ Date: 2026-06-29 - Add an "Asteroid spawning rate" input to the Game Settings cheats (Space Age only): a global multiplier for how often asteroids spawn around space platforms. Default is 1 (vanilla); set it to 0 to stop asteroids spawning entirely. Applies game-wide, not per surface. - "Create Surface → Blank surface" now creates a genuinely flat, empty test surface (no resources, water, cliffs, trees, decoratives, or enemies; always day) with a floor dropdown: Lab tiles, Concrete, or Refined concrete. - Add an "Ignore surface conditions" on/off Surface Cheat that lets planet-gated buildings be placed and planet-gated recipes crafted on the selected surface(s). + - Add a Super Quality Module: a creative module that guarantees the produced item is upgraded by at least one quality level (the engine's fixed cascade can still push it to +2/+3). It is granted by the normal Creative tools' recipe sweep and stocked by the Creative Provider Chest, alongside the other super modules. + - The Super Beacon now broadcasts the quality effect, so a single beacon fitted with the Super Quality Module can distribute the guaranteed quality upgrade to every nearby crafting machine. Changes: - Move the Surface Cheats menu from the Cheats tab into the Surface tab, grouping it with the surface-creation tools. + - The Super Beacon's distribution effectivity is raised from 0.5 to 1.5 and now scales with quality (+0.2 per level), mirroring the vanilla beacon so a legendary beacon reaches 2.5. Bugfixes: - Fix a crash that broke every fluid feature (Super Boiler, Super Cooler, Configurable Super Boiler, Fluid Void, and the Matter Source/Duplicator on fluid entities) once they held fluid. - Fix a non-recoverable crash ("'inventory index': real number expected got nil") in on_tick when a Matter Source with no filter set faces an assembling machine, rocket silo, or furnace: Factorio 2.0 folded the assembling-machine and furnace input/output inventories into crafter_input/crafter_output, so the mod's references to the removed assembling_machine_input/_output and furnace_result defines resolved to nil. The Matter Void's output-slot path was hit by the same removed defines. diff --git a/defines.lua b/defines.lua index ac46eb8..9a6f3ea 100644 --- a/defines.lua +++ b/defines.lua @@ -220,6 +220,7 @@ creative_mode_defines.names.items = { super_slow_module = creative_mode_defines.name_prefix .. "super-slow-module", super_consumption_module = creative_mode_defines.name_prefix .. "super-consumption-module", super_pollution_module = creative_mode_defines.name_prefix .. "super-pollution-module", + super_quality_module = creative_mode_defines.name_prefix .. "super-quality-module", belt_immunity_equipment = "belt-immunity-equipment", super_fusion_reactor_equipment = creative_mode_defines.name_prefix .. "super-fusion-reactor-equipment", super_personal_roboport_equipment = creative_mode_defines.name_prefix .. "super-personal-roboport-equipment", @@ -307,6 +308,7 @@ creative_mode_defines.names.recipes = { super_slow_module = creative_mode_defines.name_prefix .. "super-slow-module", super_consumption_module = creative_mode_defines.name_prefix .. "super-consumption-module", super_pollution_module = creative_mode_defines.name_prefix .. "super-pollution-module", + super_quality_module = creative_mode_defines.name_prefix .. "super-quality-module", belt_immunity_equipment = creative_mode_defines.name_prefix .. "belt-immunity-equipment", super_fusion_reactor_equipment = creative_mode_defines.name_prefix .. "super-fusion-reactor-equipment", super_personal_roboport_equipment = creative_mode_defines.name_prefix .. "super-personal-roboport-equipment", @@ -1726,6 +1728,10 @@ table.insert( creative_mode_defines.values.creative_provider_chest_additional_content_names, creative_mode_defines.names.items.super_pollution_module ) +table.insert( + creative_mode_defines.values.creative_provider_chest_additional_content_names, + creative_mode_defines.names.items.super_quality_module +) table.insert( creative_mode_defines.values.creative_provider_chest_additional_content_names, diff --git a/graphics/icons/super-quality-module.png b/graphics/icons/super-quality-module.png new file mode 100644 index 0000000..0097a5d Binary files /dev/null and b/graphics/icons/super-quality-module.png differ diff --git a/locale/en/base.cfg b/locale/en/base.cfg index 5469ccb..9c89c23 100644 --- a/locale/en/base.cfg +++ b/locale/en/base.cfg @@ -129,6 +129,7 @@ creative-mod_super-clean-module=Super clean module creative-mod_super-slow-module=Super slow module creative-mod_super-consumption-module=Super consumption module creative-mod_super-pollution-module=Super pollution module +creative-mod_super-quality-module=Super quality module [item-description] creative-mod_energy-absorption=Absorbing energy from the network. (You shouldn't hold this item. Return it ASAP!) @@ -145,6 +146,7 @@ creative-mod_super-clean-module=Massively decreases machine pollution. Minimum p creative-mod_super-slow-module=Massively decreases machine speed. Minimum speed is 20%. creative-mod_super-consumption-module=Massively increases machine energy consumption. creative-mod_super-pollution-module=Massively increases machine pollution. +creative-mod_super-quality-module=Guarantees the produced item is upgraded by at least one quality level. creative-mod_super-fusion-reactor-equipment=Generates unlimited power for your equipment. creative-mod_super-personal-roboport-equipment=Supports large numbers of robots. Large construction radius. Can recharge robots instantly without causing any energy. diff --git a/prototypes/entity.lua b/prototypes/entity.lua index 63167a2..7b16d0f 100644 --- a/prototypes/entity.lua +++ b/prototypes/entity.lua @@ -918,6 +918,10 @@ local function inserter( end -- Generates data for lab. +-- Deliberate no-op: the vanilla lab declares no allowed_effects, and the +-- LabPrototype engine default is "all effects except quality". We leave that +-- default untouched on purpose, so the creative labs continue to reject quality +-- modules — a quality effect on a lab is inert (labs roll no item quality). local function lab(entity_name, item_name, icon_name, on_animation_filename, off_animation_filename) local lab = table.deepcopy(data.raw["lab"]["lab"]) lab.name = entity_name @@ -2852,7 +2856,9 @@ data:extend({ dying_explosion = "medium-explosion", collision_box = { { -1.2, -1.2 }, { 1.2, 1.2 } }, selection_box = { { -1.5, -1.5 }, { 1.5, 1.5 } }, - allowed_effects = { "consumption", "speed", "pollution", "productivity" }, + -- "quality" added so the super-beacon can broadcast the super quality module across nearby + -- crafting machines (the "flood a base for quality testing" use case). + allowed_effects = { "consumption", "speed", "pollution", "productivity", "quality" }, base_picture = { filename = creative_mode_defines.mod_directory .. "/graphics/entity/super-beacon-base.png", width = 116, @@ -2890,7 +2896,10 @@ data:extend({ filename = "__base__/sound/car-metal-impact.ogg", volume = 0.65, }, - distribution_effectivity = 0.5, + -- Mirror the vanilla beacon's distribution-effectivity model: 1.5 baseline plus 0.2 per quality + -- level, so a legendary beacon reaches 2.5 just like vanilla. + distribution_effectivity = 1.5, + distribution_effectivity_bonus_per_quality_level = 0.2, module_slots = 7, module_info_icon_shift = { 0, 0.5 }, module_info_multi_row_initial_height_modifier = -0.3, diff --git a/prototypes/equipment.lua b/prototypes/equipment.lua index 9dbd5ec..e58cf27 100644 --- a/prototypes/equipment.lua +++ b/prototypes/equipment.lua @@ -60,6 +60,10 @@ data:extend({ stationing_offset = { 0, -0.6 }, charging_station_shift = { 0, 0.5 }, charging_station_count = 1000, + -- Deliberate no-op: charging_station_count_affected_by_quality is omitted + -- (defaults to false), so the station count stays a flat 1000. Intentional — + -- this equipment is only ever obtainable at normal quality, so a quality + -- scalar would never apply. charging_distance = 1.6, charging_threshold_distance = 5, categories = { "armor" }, diff --git a/prototypes/item.lua b/prototypes/item.lua index 37c3dce..5a7ab1d 100644 --- a/prototypes/item.lua +++ b/prototypes/item.lua @@ -863,6 +863,22 @@ data:extend({ stack_size = 50, effect = { pollution = 2.5 }, }, + { + -- Super quality module + type = "module", + name = creative_mode_defines.names.recipes.super_quality_module, + icon_size = 32, + icon = creative_mode_defines.mod_directory .. "/graphics/icons/super-quality-module.png", + hidden = hidden, + subgroup = creative_mode_defines.names.item_subgroups.modules, + category = "quality", + tier = 50, + order = "i", + stack_size = 50, + -- 1.0 = 100%: guaranteed at-least-+1 level; the engine's fixed ~10% cascade + -- can still push to +2/+3. Values >1.0 are identical (cascade is fixed). + effect = { quality = 1.0 }, + }, ----------------------------------------------------------------------------- { diff --git a/prototypes/recipe.lua b/prototypes/recipe.lua index afed19b..9883e34 100644 --- a/prototypes/recipe.lua +++ b/prototypes/recipe.lua @@ -523,6 +523,18 @@ data:extend({ results = { { type = "item", name = creative_mode_defines.names.items.super_pollution_module, amount = 1 } }, enabled = false, }, + { + -- Super quality module + -- Deliberate no-op: like every recipe here, ingredients = {} so the engine + -- resolves can_set_quality to false (this module item is craftable at normal + -- quality only). Intentional — cheat items are already maxed, so quality + -- variants of the module itself would have no functional payoff. + type = "recipe", + name = creative_mode_defines.names.recipes.super_quality_module, + ingredients = {}, + results = { { type = "item", name = creative_mode_defines.names.items.super_quality_module, amount = 1 } }, + enabled = false, + }, ----------------------------------------------------------------------------- { diff --git a/verify.py b/verify.py index 8e1eae4..a8ed253 100644 --- a/verify.py +++ b/verify.py @@ -480,6 +480,39 @@ def cmd_behavior(args: argparse.Namespace) -> int: "true", "super_boiler_placed", ), + # super_quality_module_effect_applied: place a quality-capable crafting machine + # (assembling-machine-2 allows the "quality" effect by vanilla default), read its + # baseline resolved quality effect, insert the creative super-quality-module into its + # module inventory, and read the effect again. LuaEntity.effects is a flat table keyed + # by effect type (e.g. { quality = 0.5 } before, { quality = 1.5 } after for AM-2). The + # module's effect = { quality = 1.0 } must raise the resolved quality by >= 1.0 + # (guaranteed at-least-+1 upgrade). Proves the module's effect is legal in a module slot + # AND carries the intended magnitude: the engine accepted both the category and the + # effect key, and the delta is the module's full contribution. + _assert_rcon( + sb, + '/c local s = game.surfaces["cm_verify"] ' + 'local m = s.create_entity{name="assembling-machine-2", position={14, 12}, force="player"} ' + "local before = (m.effects and m.effects.quality) or 0 " + 'm.get_module_inventory().insert{name="creative-mod_super-quality-module", count=1} ' + "local after = (m.effects and m.effects.quality) or 0 " + "rcon.print(tostring(after - before >= 1.0))", + "true", + "super_quality_module_effect_applied", + ), + # super_quality_module_beacon_insertable: validates the Phase-2 super_beacon + # allowed_effects edit end-to-end. Place a super-beacon and insert the + # super-quality-module into its module inventory; a successful insert (count == 1) + # proves the "quality" allowed-effects gate now admits the module for distribution. + # Before the edit the engine would reject the insert (insert returns 0). + _assert_rcon( + sb, + '/c local s = game.surfaces["cm_verify"] ' + 'local b = s.create_entity{name="creative-mod_super-beacon", position={18, 12}, force="player"} ' + 'rcon.print(tostring(b.get_module_inventory().insert{name="creative-mod_super-quality-module", count=1} == 1))', + "true", + "super_quality_module_beacon_insertable", + ), ] # Let the server tick so the per-tick refill runs on the just-placed thruster. time.sleep(1.0) @@ -553,6 +586,8 @@ def cmd_behavior(args: argparse.Namespace) -> int: "asteroid_spawning_rate_restore", "item_source_to_crafter_placed", "super_boiler_placed", + "super_quality_module_effect_applied", + "super_quality_module_beacon_insertable", "creative_thruster_refuels", "item_source_feeds_crafter", "super_boiler_heats_fluid",