From babe2945bb95a97c30ecdf523dc377594c29de89 Mon Sep 17 00:00:00 2001 From: lex <5781244+lex@users.noreply.github.com> Date: Wed, 8 Jul 2026 16:12:47 +0300 Subject: [PATCH 01/31] Tetrominoes: use shared vanilla_ores_stone ore config Replaces the map-specific tetromino_ores.lua with a shared 4-ore config that derives its three vanilla entries from config/vanilla_ores.lua (one source of truth for the numbers) and appends the stone sector. Co-Authored-By: Claude Fable 5 --- .../danger_ores/config/tetromino_ores.lua | 55 ------------------- .../danger_ores/config/vanilla_ores_stone.lua | 33 +++++++++++ .../presets/danger_ore_tetrominoes.lua | 2 +- 3 files changed, 34 insertions(+), 56 deletions(-) delete mode 100644 map_gen/maps/danger_ores/config/tetromino_ores.lua create mode 100644 map_gen/maps/danger_ores/config/vanilla_ores_stone.lua diff --git a/map_gen/maps/danger_ores/config/tetromino_ores.lua b/map_gen/maps/danger_ores/config/tetromino_ores.lua deleted file mode 100644 index faa191593..000000000 --- a/map_gen/maps/danger_ores/config/tetromino_ores.lua +++ /dev/null @@ -1,55 +0,0 @@ -local b = require 'map_gen.shared.builders' -local start_value = b.euclidean_value(0, 0.35) -local value = b.exponential_value(0, 0.07, 1.45) - --- Order is significant: it maps to ore indices 1..4 produced by tetromino_layout. -return { - { - name = 'iron-ore', - tiles = { 'grass-1', 'grass-2', 'grass-3', 'grass-4' }, - start = start_value, - weight = 1, - ratios = { - { resource = b.resource(b.full_shape, 'iron-ore', value), weight = 75 }, - { resource = b.resource(b.full_shape, 'copper-ore', value), weight = 13 }, - { resource = b.resource(b.full_shape, 'stone', value), weight = 7 }, - { resource = b.resource(b.full_shape, 'coal', value), weight = 5 }, - }, - }, - { - name = 'copper-ore', - tiles = { 'red-desert-0', 'red-desert-1', 'red-desert-2', 'red-desert-3' }, - start = start_value, - weight = 1, - ratios = { - { resource = b.resource(b.full_shape, 'iron-ore', value), weight = 15 }, - { resource = b.resource(b.full_shape, 'copper-ore', value), weight = 70 }, - { resource = b.resource(b.full_shape, 'stone', value), weight = 10 }, - { resource = b.resource(b.full_shape, 'coal', value), weight = 5 }, - }, - }, - { - name = 'coal', - tiles = { 'dirt-1', 'dirt-2', 'dirt-3', 'dirt-4', 'dirt-5', 'dirt-6', 'dirt-7' }, - start = start_value, - weight = 1, - ratios = { - { resource = b.resource(b.full_shape, 'iron-ore', value), weight = 18 }, - { resource = b.resource(b.full_shape, 'copper-ore', value), weight = 9 }, - { resource = b.resource(b.full_shape, 'stone', value), weight = 8 }, - { resource = b.resource(b.full_shape, 'coal', value), weight = 65 }, - }, - }, - { - name = 'stone', - tiles = { 'sand-1', 'sand-2', 'sand-3' }, - start = start_value, - weight = 1, - ratios = { - { resource = b.resource(b.full_shape, 'iron-ore', value), weight = 25 }, - { resource = b.resource(b.full_shape, 'copper-ore', value), weight = 10 }, - { resource = b.resource(b.full_shape, 'stone', value), weight = 60 }, - { resource = b.resource(b.full_shape, 'coal', value), weight = 5 }, - }, - }, -} diff --git a/map_gen/maps/danger_ores/config/vanilla_ores_stone.lua b/map_gen/maps/danger_ores/config/vanilla_ores_stone.lua new file mode 100644 index 000000000..03bac9c86 --- /dev/null +++ b/map_gen/maps/danger_ores/config/vanilla_ores_stone.lua @@ -0,0 +1,33 @@ +-- Vanilla ores plus stone as a fourth main ore: the three vanilla entries come straight +-- from config/vanilla_ores.lua (one source of truth for the numbers), with a stone entry +-- using the same distance curves. Order is significant: it maps to ore indices 1..4. +-- Used by maps whose layouts need four main ores, e.g. the clean-border partition maps +-- (Tetrominoes, Voronoi), whose region colouring requires at least 4 colours. +local b = require 'map_gen.shared.builders' +local vanilla_ores = require 'map_gen.maps.danger_ores.config.vanilla_ores' + +local by_name = {} +for _, ore in ipairs(vanilla_ores) do + by_name[ore.name] = ore +end + +local start_value = b.euclidean_value(0, 0.35) +local value = b.exponential_value(0, 0.07, 1.45) + +return { + by_name['iron-ore'], + by_name['copper-ore'], + by_name['coal'], + { + name = 'stone', + tiles = { 'sand-1', 'sand-2', 'sand-3' }, + start = start_value, + weight = 1, + ratios = { + { resource = b.resource(b.full_shape, 'iron-ore', value), weight = 25 }, + { resource = b.resource(b.full_shape, 'copper-ore', value), weight = 10 }, + { resource = b.resource(b.full_shape, 'stone', value), weight = 60 }, + { resource = b.resource(b.full_shape, 'coal', value), weight = 5 }, + }, + }, +} diff --git a/map_gen/maps/danger_ores/presets/danger_ore_tetrominoes.lua b/map_gen/maps/danger_ores/presets/danger_ore_tetrominoes.lua index c10bf23c3..8ef8d9f55 100644 --- a/map_gen/maps/danger_ores/presets/danger_ore_tetrominoes.lua +++ b/map_gen/maps/danger_ores/presets/danger_ore_tetrominoes.lua @@ -13,7 +13,7 @@ ScenarioInfo.add_map_extra_info([[ DOC.scenario_name = 'danger-ore-tetrominoes' DOC.map_config.main_ores_builder = require 'map_gen.maps.danger_ores.modules.main_ores_tetrominoes' -DOC.map_config.main_ores = require 'map_gen.maps.danger_ores.config.tetromino_ores' +DOC.map_config.main_ores = require 'map_gen.maps.danger_ores.config.vanilla_ores_stone' DOC.map_config.main_ores_rotate = nil return Scenario.register(DOC) From d3feca14f260e50cbbfe3ed75062c1f7880917df Mon Sep 17 00:00:00 2001 From: lex <5781244+lex@users.noreply.github.com> Date: Wed, 8 Jul 2026 16:12:47 +0300 Subject: [PATCH 02/31] Voronoi: use shared vanilla_ores_stone ore config Drops the duplicate voronoi_ores.lua (identical to tetromino_ores.lua). Co-Authored-By: Claude Fable 5 --- .../maps/danger_ores/config/voronoi_ores.lua | 55 ------------------- .../presets/danger_ore_voronoi.lua | 2 +- 2 files changed, 1 insertion(+), 56 deletions(-) delete mode 100644 map_gen/maps/danger_ores/config/voronoi_ores.lua diff --git a/map_gen/maps/danger_ores/config/voronoi_ores.lua b/map_gen/maps/danger_ores/config/voronoi_ores.lua deleted file mode 100644 index 9544a2b8c..000000000 --- a/map_gen/maps/danger_ores/config/voronoi_ores.lua +++ /dev/null @@ -1,55 +0,0 @@ -local b = require 'map_gen.shared.builders' -local start_value = b.euclidean_value(0, 0.35) -local value = b.exponential_value(0, 0.07, 1.45) - --- Order is significant: it maps to ore indices 1..4 produced by voronoi_layout. -return { - { - name = 'iron-ore', - tiles = { 'grass-1', 'grass-2', 'grass-3', 'grass-4' }, - start = start_value, - weight = 1, - ratios = { - { resource = b.resource(b.full_shape, 'iron-ore', value), weight = 75 }, - { resource = b.resource(b.full_shape, 'copper-ore', value), weight = 13 }, - { resource = b.resource(b.full_shape, 'stone', value), weight = 7 }, - { resource = b.resource(b.full_shape, 'coal', value), weight = 5 }, - }, - }, - { - name = 'copper-ore', - tiles = { 'red-desert-0', 'red-desert-1', 'red-desert-2', 'red-desert-3' }, - start = start_value, - weight = 1, - ratios = { - { resource = b.resource(b.full_shape, 'iron-ore', value), weight = 15 }, - { resource = b.resource(b.full_shape, 'copper-ore', value), weight = 70 }, - { resource = b.resource(b.full_shape, 'stone', value), weight = 10 }, - { resource = b.resource(b.full_shape, 'coal', value), weight = 5 }, - }, - }, - { - name = 'coal', - tiles = { 'dirt-1', 'dirt-2', 'dirt-3', 'dirt-4', 'dirt-5', 'dirt-6', 'dirt-7' }, - start = start_value, - weight = 1, - ratios = { - { resource = b.resource(b.full_shape, 'iron-ore', value), weight = 18 }, - { resource = b.resource(b.full_shape, 'copper-ore', value), weight = 9 }, - { resource = b.resource(b.full_shape, 'stone', value), weight = 8 }, - { resource = b.resource(b.full_shape, 'coal', value), weight = 65 }, - }, - }, - { - name = 'stone', - tiles = { 'sand-1', 'sand-2', 'sand-3' }, - start = start_value, - weight = 1, - ratios = { - { resource = b.resource(b.full_shape, 'iron-ore', value), weight = 25 }, - { resource = b.resource(b.full_shape, 'copper-ore', value), weight = 10 }, - { resource = b.resource(b.full_shape, 'stone', value), weight = 60 }, - { resource = b.resource(b.full_shape, 'coal', value), weight = 5 }, - }, - }, -} diff --git a/map_gen/maps/danger_ores/presets/danger_ore_voronoi.lua b/map_gen/maps/danger_ores/presets/danger_ore_voronoi.lua index 0a290ab6f..82b27bd55 100644 --- a/map_gen/maps/danger_ores/presets/danger_ore_voronoi.lua +++ b/map_gen/maps/danger_ores/presets/danger_ore_voronoi.lua @@ -13,7 +13,7 @@ ScenarioInfo.add_map_extra_info([[ DOC.scenario_name = 'danger-ore-voronoi' DOC.map_config.main_ores_builder = require 'map_gen.maps.danger_ores.modules.main_ores_voronoi' -DOC.map_config.main_ores = require 'map_gen.maps.danger_ores.config.voronoi_ores' +DOC.map_config.main_ores = require 'map_gen.maps.danger_ores.config.vanilla_ores_stone' DOC.map_config.main_ores_rotate = nil return Scenario.register(DOC) From c7fc4240a62827d840d4dd427cf39866494011ac Mon Sep 17 00:00:00 2001 From: lex <5781244+lex@users.noreply.github.com> Date: Wed, 8 Jul 2026 16:12:47 +0300 Subject: [PATCH 03/31] Tetrominoes: enforce the 4-ore minimum at map creation The clean-border guarantee is a graph colouring and 4 colours is the mathematical minimum; generate() now asserts it with a clear message. The module accepts any 4+-ore config as input. Test covers rejection. Co-Authored-By: Claude Fable 5 --- map_gen/maps/danger_ores/modules/tetromino_layout.lua | 2 ++ map_gen/maps/danger_ores/modules/tetromino_layout.test.lua | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/map_gen/maps/danger_ores/modules/tetromino_layout.lua b/map_gen/maps/danger_ores/modules/tetromino_layout.lua index 6b694f8aa..48e8eb83e 100644 --- a/map_gen/maps/danger_ores/modules/tetromino_layout.lua +++ b/map_gen/maps/danger_ores/modules/tetromino_layout.lua @@ -287,6 +287,8 @@ end function M.generate(opts) local size = opts.size local num_ores = opts.num_ores + -- the clean-border guarantee is a graph colouring: 4 colours is the mathematical minimum + assert(num_ores >= 4, 'tetromino_layout: at least 4 ore types are required for guaranteed clean borders') local random = opts.random local max_attempts = opts.max_attempts or 8 diff --git a/map_gen/maps/danger_ores/modules/tetromino_layout.test.lua b/map_gen/maps/danger_ores/modules/tetromino_layout.test.lua index 081067e9e..2b7d1edb1 100644 --- a/map_gen/maps/danger_ores/modules/tetromino_layout.test.lua +++ b/map_gen/maps/danger_ores/modules/tetromino_layout.test.lua @@ -157,6 +157,12 @@ do end end +-- enforcement: fewer than 4 ores must be rejected (clean borders need 4 colours) +local ok_low = pcall(function() + return Layout.generate { size = 16, palette = palette, num_ores = 3, random = rand } +end) +check(not ok_low, 'generate rejects fewer than 4 ores') + if failures == 0 then print('\nALL PASSED') else From 032d702ec04ac491f461a3b76c5b2932a2a3622d Mon Sep 17 00:00:00 2001 From: lex <5781244+lex@users.noreply.github.com> Date: Wed, 8 Jul 2026 16:12:47 +0300 Subject: [PATCH 04/31] Voronoi: enforce the 4-ore minimum at map creation Same guarantee and enforcement as the Tetrominoes layout; any 4+-ore config plugs in. Test covers rejection. Co-Authored-By: Claude Fable 5 --- map_gen/maps/danger_ores/modules/voronoi_layout.lua | 2 ++ map_gen/maps/danger_ores/modules/voronoi_layout.test.lua | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/map_gen/maps/danger_ores/modules/voronoi_layout.lua b/map_gen/maps/danger_ores/modules/voronoi_layout.lua index c33da0201..d07d08e63 100644 --- a/map_gen/maps/danger_ores/modules/voronoi_layout.lua +++ b/map_gen/maps/danger_ores/modules/voronoi_layout.lua @@ -217,6 +217,8 @@ function M.build(opts) local spacing = opts.spacing local relaxation = opts.relaxation or 0 local num_ores = opts.num_ores + -- the clean-border guarantee is a graph colouring: 4 colours is the mathematical minimum + assert(num_ores >= 4, 'voronoi_layout: at least 4 ore types are required for guaranteed clean borders') local random = opts.random local max_attempts = opts.max_attempts or 8 local g = floor(size / spacing) diff --git a/map_gen/maps/danger_ores/modules/voronoi_layout.test.lua b/map_gen/maps/danger_ores/modules/voronoi_layout.test.lua index cb03167ec..58cc59468 100644 --- a/map_gen/maps/danger_ores/modules/voronoi_layout.test.lua +++ b/map_gen/maps/danger_ores/modules/voronoi_layout.test.lua @@ -136,6 +136,12 @@ do check(totals[4] == min_ore, 'stone (ore 4) is the least-used ore') end +-- enforcement: fewer than 4 ores must be rejected (clean borders need 4 colours) +local ok_low = pcall(function() + return Layout.generate { size = 16, spacing = 4, relaxation = 0, num_ores = 3, random = rand } +end) +check(not ok_low, 'generate rejects fewer than 4 ores') + if failures == 0 then print('\nALL PASSED') else From 2448123d097b1fe870637d61c5ca9c51e4032738 Mon Sep 17 00:00:00 2001 From: lex <5781244+lex@users.noreply.github.com> Date: Wed, 8 Jul 2026 16:12:47 +0300 Subject: [PATCH 05/31] Derive 3way_ores from x_cross_ores The two files were identical except for the commented-out stone sector; 3way now returns the first three x_cross entries directly. Co-Authored-By: Claude Fable 5 --- map_gen/maps/danger_ores/config/3way_ores.lua | 79 +------------------ 1 file changed, 4 insertions(+), 75 deletions(-) diff --git a/map_gen/maps/danger_ores/config/3way_ores.lua b/map_gen/maps/danger_ores/config/3way_ores.lua index 64789ca6c..26aaa574c 100644 --- a/map_gen/maps/danger_ores/config/3way_ores.lua +++ b/map_gen/maps/danger_ores/config/3way_ores.lua @@ -1,76 +1,5 @@ -local b = require 'map_gen.shared.builders' -local start_value = b.exponential_value(0, 0.15, 1.3) -local value = b.exponential_value(0, 0.15, 1.3) +-- The 3-way sector ores are exactly the first three entries (copper, coal, iron) of the +-- X-Cross config, which also carries the stone sector -- one source of truth for the numbers. +local x_cross_ores = require 'map_gen.maps.danger_ores.config.x_cross_ores' -return { - { - name = 'copper-ore', - ['tiles'] = { - [1] = 'red-desert-0', - [2] = 'red-desert-1', - [3] = 'red-desert-2', - [4] = 'red-desert-3' - }, - ['start'] = start_value, - ['weight'] = 1, - ['ratios'] = { - {resource = b.resource(b.full_shape, 'iron-ore', value), weight = 15}, - {resource = b.resource(b.full_shape, 'copper-ore', value), weight = 70}, - {resource = b.resource(b.full_shape, 'stone', value), weight = 10}, - {resource = b.resource(b.full_shape, 'coal', value), weight = 5} - } - }, - { - name = 'coal', - ['tiles'] = { - [1] = 'dirt-1', - [2] = 'dirt-2', - [3] = 'dirt-3', - [4] = 'dirt-4', - [5] = 'dirt-5', - [6] = 'dirt-6', - [7] = 'dirt-7' - }, - ['start'] = start_value, - ['weight'] = 1, - ['ratios'] = { - {resource = b.resource(b.full_shape, 'iron-ore', value), weight = 14}, - {resource = b.resource(b.full_shape, 'copper-ore', value), weight = 6}, - {resource = b.resource(b.full_shape, 'stone', value), weight = 10}, - {resource = b.resource(b.full_shape, 'coal', value), weight = 70} - } - }, - { - name = 'iron-ore', - ['tiles'] = { - [1] = 'grass-1', - [2] = 'grass-2', - [3] = 'grass-3', - [4] = 'grass-4' - }, - ['start'] = start_value, - ['weight'] = 1, - ['ratios'] = { - {resource = b.resource(b.full_shape, 'iron-ore', value), weight = 75}, - {resource = b.resource(b.full_shape, 'copper-ore', value), weight = 13}, - {resource = b.resource(b.full_shape, 'stone', value), weight = 7}, - {resource = b.resource(b.full_shape, 'coal', value), weight = 5} - } - }, - --[[ { - name = 'stone', - ['tiles'] = { - [1] = 'sand-1', - [2] = 'sand-2', - [3] = 'sand-3' - }, - ['start'] = start_value, - ['weight'] = 1, - ['ratios'] = { - {resource = b.resource(b.full_shape, 'iron-ore', value), weight = 25}, - {resource = b.resource(b.full_shape, 'copper-ore', value), weight = 10}, - {resource = b.resource(b.full_shape, 'stone', value), weight = 60}, - {resource = b.resource(b.full_shape, 'coal', value), weight = 5} - } - } ]] -} +return { x_cross_ores[1], x_cross_ores[2], x_cross_ores[3] } From 732f46bcfaf43d4d7e4b1bd41b53382d13072975 Mon Sep 17 00:00:00 2001 From: lex <5781244+lex@users.noreply.github.com> Date: Wed, 8 Jul 2026 16:12:47 +0300 Subject: [PATCH 06/31] Add ore config test suite Standalone lua tests: structural invariants for every main-ore config (strict shape for ratios-based configs, looser for the gradient/joker variants), identity checks for the derived configs, and golden ratio weights for the shared sources so old maps cannot drift by accident. Co-Authored-By: Claude Fable 5 --- .../danger_ores/config/ore_configs.test.lua | 125 ++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 map_gen/maps/danger_ores/config/ore_configs.test.lua diff --git a/map_gen/maps/danger_ores/config/ore_configs.test.lua b/map_gen/maps/danger_ores/config/ore_configs.test.lua new file mode 100644 index 000000000..a04959404 --- /dev/null +++ b/map_gen/maps/danger_ores/config/ore_configs.test.lua @@ -0,0 +1,125 @@ +-- Standalone unit tests for the danger-ores ore configs. +-- Run from the repo root: lua map_gen/maps/danger_ores/config/ore_configs.test.lua +package.path = './?.lua;' .. package.path + +-- stub the builders so configs load outside Factorio; value functions become readable tags +local fake_builders = { full_shape = 'FULL' } +function fake_builders.euclidean_value(base, mult) + return ('euclid(%s,%s)'):format(base, mult) +end +function fake_builders.exponential_value(base, mult, pow) + return ('exp(%s,%s,%s)'):format(base, mult, pow) +end +function fake_builders.resource(shape, name, value) + return { kind = 'resource', name = name, value = value } +end +setmetatable(fake_builders, { + __index = function(_, k) + return function() + return 'builders.' .. k + end + end, +}) +package.loaded['map_gen.shared.builders'] = fake_builders + +local failures = 0 +local function check(cond, msg) + if cond then + print('ok: ' .. msg) + else + failures = failures + 1 + print('FAIL: ' .. msg) + end +end + +local function load_config(name) + return require('map_gen.maps.danger_ores.config.' .. name) +end + +-- 1) structural invariants. STRICT configs use the standard ratios-based entry shape the +-- common ore_builder consumes; VARIANT configs (gradient's shape+weight-function entries, +-- joker's special starter area) only need to load and have named entries. +local STRICT_CONFIGS = { + 'vanilla_ores', '3way_ores', 'x_cross_ores', 'vanilla_ores_one_direction', + 'vanilla_ores_gridlocked', 'vanilla_ores_landfill', + 'coal', 'one_direction_ores_xmas', 'poor_mans_coal_fields_ores', 'scrap', + 'vanilla_ores_stone', 'joker_outer_area', +} +local VARIANT_CONFIGS = { 'vanilla_gradient_ores', 'joker_starter_area' } + +for _, config_name in ipairs(STRICT_CONFIGS) do + local ok, config = pcall(load_config, config_name) + if not ok then + check(false, config_name .. ' loads (' .. tostring(config) .. ')') + else + local valid = type(config) == 'table' and #config > 0 + for _, entry in ipairs(config) do + valid = valid and type(entry.name) == 'string' + and type(entry.tiles) == 'table' and #entry.tiles > 0 + and entry.start ~= nil and entry.weight ~= nil + and type(entry.ratios) == 'table' and #entry.ratios > 0 + for _, ratio in ipairs(entry.ratios or {}) do + valid = valid and ratio.resource ~= nil and ratio.weight ~= nil + end + end + check(valid, config_name .. ' is structurally valid (' .. #config .. ' ores)') + end +end + +for _, config_name in ipairs(VARIANT_CONFIGS) do + local ok, config = pcall(load_config, config_name) + if not ok then + check(false, config_name .. ' loads (' .. tostring(config) .. ')') + else + local valid = type(config) == 'table' and #config > 0 + for _, entry in ipairs(config) do + valid = valid and type(entry.name) == 'string' + and (entry.ratios ~= nil or entry.shape ~= nil) + end + check(valid, config_name .. ' loads with named entries (' .. #config .. ' entries)') + end +end + +-- 2) derived configs share their source's tables (one source of truth for the numbers) +local three_way = load_config('3way_ores') +local x_cross = load_config('x_cross_ores') +check(#three_way == 3, '3way_ores has 3 sectors') +check(rawequal(three_way[1], x_cross[1]) and rawequal(three_way[2], x_cross[2]) + and rawequal(three_way[3], x_cross[3]), '3way_ores entries ARE the x_cross_ores entries') + +local with_stone = load_config('vanilla_ores_stone') +local vanilla = load_config('vanilla_ores') +local vanilla_by_name = {} +for _, ore in ipairs(vanilla) do + vanilla_by_name[ore.name] = ore +end +check(#with_stone == 4, 'vanilla_ores_stone has 4 ores') +check(rawequal(with_stone[1], vanilla_by_name['iron-ore']) + and rawequal(with_stone[2], vanilla_by_name['copper-ore']) + and rawequal(with_stone[3], vanilla_by_name['coal']), + 'vanilla_ores_stone entries 1-3 ARE the vanilla_ores entries') +check(with_stone[4].name == 'stone', 'vanilla_ores_stone entry 4 is stone') + +-- 3) golden numbers for the source configs, so old maps cannot drift by accident +local function ratio_weights(entry) + local weights = {} + for _, ratio in ipairs(entry.ratios) do + weights[#weights + 1] = tostring(ratio.weight) + end + return table.concat(weights, '/') +end +check(ratio_weights(vanilla_by_name['iron-ore']) == '75/13/7/5' + and ratio_weights(vanilla_by_name['copper-ore']) == '15/70/10/5' + and ratio_weights(vanilla_by_name['coal']) == '18/9/8/65', + 'vanilla_ores ratio weights unchanged (75/13/7/5, 15/70/10/5, 18/9/8/65)') +check(ratio_weights(x_cross[1]) == '15/70/10/5' and ratio_weights(x_cross[2]) == '14/6/10/70' + and ratio_weights(x_cross[3]) == '75/13/7/5' and ratio_weights(x_cross[4]) == '25/10/60/5', + 'x_cross_ores ratio weights unchanged') +check(ratio_weights(with_stone[4]) == '25/10/60/5', 'stone entry ratio weights unchanged') + +if failures == 0 then + print('\nALL PASSED') +else + print('\n' .. failures .. ' FAILURE(S)') + os.exit(1) +end From ebf07f46015ef70ec39bf2059e2d1734d06093ce Mon Sep 17 00:00:00 2001 From: lex <5781244+lex@users.noreply.github.com> Date: Wed, 8 Jul 2026 16:32:44 +0300 Subject: [PATCH 07/31] Danger Ores: add a factory for the standard main-ore configs Most main-ore configs repeat the same per-ore floor tiles and the same dominant+minor resource mixes, differing only in richness curves, ore order, entry weights or tile skins. The factory holds those canonical numbers once; configs compose their entries from it. All runtime tables are built fresh per call so configs never share mutable state. Co-Authored-By: Claude Fable 5 --- .../danger_ores/config/main_ores_factory.lua | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 map_gen/maps/danger_ores/config/main_ores_factory.lua diff --git a/map_gen/maps/danger_ores/config/main_ores_factory.lua b/map_gen/maps/danger_ores/config/main_ores_factory.lua new file mode 100644 index 000000000..5d06ec816 --- /dev/null +++ b/map_gen/maps/danger_ores/config/main_ores_factory.lua @@ -0,0 +1,97 @@ +-- Factory for the standard danger-ores main-ore configs. Most of them use the same +-- per-ore floor tiles and the same dominant+minor resource mixes, differing only in +-- richness curves, ore order, entry weights or tile skins. This module holds those +-- canonical numbers once; each config composes its entries from here. +local b = require 'map_gen.shared.builders' + +local Public = {} + +-- Per-ore floor tiles. +local tile_sets = { + ['iron-ore'] = {'grass-1', 'grass-2', 'grass-3', 'grass-4'}, + ['copper-ore'] = {'red-desert-0', 'red-desert-1', 'red-desert-2', 'red-desert-3'}, + ['coal'] = {'dirt-1', 'dirt-2', 'dirt-3', 'dirt-4', 'dirt-5', 'dirt-6', 'dirt-7'}, + ['stone'] = {'sand-1', 'sand-2', 'sand-3'} +} + +-- Dominant + minor mixes as {resource name, ratio weight} pairs, in canonical order. +local mixes = { + ['iron-ore'] = {{'iron-ore', 75}, {'copper-ore', 13}, {'stone', 7}, {'coal', 5}}, + ['copper-ore'] = {{'iron-ore', 15}, {'copper-ore', 70}, {'stone', 10}, {'coal', 5}}, + ['coal'] = {{'iron-ore', 18}, {'copper-ore', 9}, {'stone', 8}, {'coal', 65}}, + ['stone'] = {{'iron-ore', 25}, {'copper-ore', 10}, {'stone', 60}, {'coal', 5}}, + -- the coal-heavier mix used by the X-Cross family and Poor Man's Coal Fields + ['coal-rich'] = {{'iron-ore', 14}, {'copper-ore', 6}, {'stone', 10}, {'coal', 70}} +} +Public.mixes = mixes + +local function copy_array(array) + local copy = {} + for i, v in ipairs(array) do + copy[i] = v + end + return copy +end + +-- Fresh copy per call: the runtime must never share mutable tables between entries. +function Public.tiles(ore_name) + return copy_array(tile_sets[ore_name]) +end + +-- Build a ratios table from a mix. make_resource(resource_name) -> resource shape. +function Public.ratios(mix_name, make_resource) + local ratios = {} + for i, mix in ipairs(mixes[mix_name]) do + ratios[i] = {resource = make_resource(mix[1]), weight = mix[2]} + end + return ratios +end + +-- The common case: plain full-shape resources with one richness curve. +function Public.full_shape_resource(value) + return function(resource_name) + return b.resource(b.full_shape, resource_name, value) + end +end + +-- One main-ore entry. params: name (required), start (required), and either value or +-- make_resource; mix (defaults to name), tiles (defaults to the ore's tile set) and +-- weight (defaults to 1) optional. +function Public.entry(params) + local name = params.name + local make_resource = params.make_resource + if not make_resource then + assert(params.value, 'main_ores_factory.entry: either value or make_resource is required') + make_resource = Public.full_shape_resource(params.value) + end + return { + name = name, + tiles = params.tiles or Public.tiles(name), + start = params.start, + weight = params.weight or 1, + ratios = Public.ratios(params.mix or name, make_resource) + } +end + +-- A whole main-ores config in one call. ores is an array of ore names or per-entry +-- params tables; start/value/weight/tiles/make_resource act as defaults for every entry. +function Public.main_ores(params) + local config = {} + for i, ore in ipairs(params.ores) do + if type(ore) == 'string' then + ore = {name = ore} + end + config[i] = Public.entry { + name = ore.name, + mix = ore.mix, + tiles = ore.tiles or (params.tiles and copy_array(params.tiles)), + start = ore.start or params.start, + value = ore.value or params.value, + weight = ore.weight or params.weight, + make_resource = ore.make_resource or params.make_resource + } + end + return config +end + +return Public From d82dd74ee522de398cef8f02513613d817f08069 Mon Sep 17 00:00:00 2001 From: lex <5781244+lex@users.noreply.github.com> Date: Wed, 8 Jul 2026 16:32:44 +0300 Subject: [PATCH 08/31] Danger Ores: derive vanilla_ores from the main-ores factory Byte-identical output (verified with a normalized config dump diff); 76 lines become a 6-line composition. Co-Authored-By: Claude Fable 5 --- .../maps/danger_ores/config/vanilla_ores.lua | 80 ++----------------- 1 file changed, 7 insertions(+), 73 deletions(-) diff --git a/map_gen/maps/danger_ores/config/vanilla_ores.lua b/map_gen/maps/danger_ores/config/vanilla_ores.lua index 4e97b5eaf..fbdbf07dc 100644 --- a/map_gen/maps/danger_ores/config/vanilla_ores.lua +++ b/map_gen/maps/danger_ores/config/vanilla_ores.lua @@ -1,76 +1,10 @@ +-- The standard 3-ore main-ores config: canonical tiles and mixes from the factory, +-- with the classic richness curves. Order is significant: it maps to ore indices. local b = require 'map_gen.shared.builders' -local start_value = b.euclidean_value(0, 0.35) -local value = b.exponential_value(0, 0.07, 1.45) +local Factory = require 'map_gen.maps.danger_ores.config.main_ores_factory' -return { - { - name = 'copper-ore', - ['tiles'] = { - [1] = 'red-desert-0', - [2] = 'red-desert-1', - [3] = 'red-desert-2', - [4] = 'red-desert-3' - }, - ['start'] = start_value, - ['weight'] = 1, - ['ratios'] = { - {resource = b.resource(b.full_shape, 'iron-ore', value), weight = 15}, - {resource = b.resource(b.full_shape, 'copper-ore', value), weight = 70}, - {resource = b.resource(b.full_shape, 'stone', value), weight = 10}, - {resource = b.resource(b.full_shape, 'coal', value), weight = 5} - } - }, - { - name = 'coal', - ['tiles'] = { - [1] = 'dirt-1', - [2] = 'dirt-2', - [3] = 'dirt-3', - [4] = 'dirt-4', - [5] = 'dirt-5', - [6] = 'dirt-6', - [7] = 'dirt-7' - }, - ['start'] = start_value, - ['weight'] = 1, - ['ratios'] = { - {resource = b.resource(b.full_shape, 'iron-ore', value), weight = 18}, - {resource = b.resource(b.full_shape, 'copper-ore', value), weight = 9}, - {resource = b.resource(b.full_shape, 'stone', value), weight = 8}, - {resource = b.resource(b.full_shape, 'coal', value), weight = 65} - } - }, - { - name = 'iron-ore', - ['tiles'] = { - [1] = 'grass-1', - [2] = 'grass-2', - [3] = 'grass-3', - [4] = 'grass-4' - }, - ['start'] = start_value, - ['weight'] = 1, - ['ratios'] = { - {resource = b.resource(b.full_shape, 'iron-ore', value), weight = 75}, - {resource = b.resource(b.full_shape, 'copper-ore', value), weight = 13}, - {resource = b.resource(b.full_shape, 'stone', value), weight = 7}, - {resource = b.resource(b.full_shape, 'coal', value), weight = 5} - } - }, - --[[ { - name = 'stone', - ['tiles'] = { - [1] = 'sand-1', - [2] = 'sand-2', - [3] = 'sand-3' - }, - ['start'] = start_value, - ['weight'] = 1, - ['ratios'] = { - {resource = b.resource(b.full_shape, 'iron-ore', value), weight = 25}, - {resource = b.resource(b.full_shape, 'copper-ore', value), weight = 10}, - {resource = b.resource(b.full_shape, 'stone', value), weight = 60}, - {resource = b.resource(b.full_shape, 'coal', value), weight = 5} - } - } ]] +return Factory.main_ores { + ores = {'copper-ore', 'coal', 'iron-ore'}, + start = b.euclidean_value(0, 0.35), + value = b.exponential_value(0, 0.07, 1.45) } From 22532c24b4e82ce152b49e4cf19806536a01c403 Mon Sep 17 00:00:00 2001 From: lex <5781244+lex@users.noreply.github.com> Date: Wed, 8 Jul 2026 16:34:29 +0300 Subject: [PATCH 09/31] Danger Ores: derive vanilla_ores_one_direction from the factory Same config as vanilla_ores apart from a steeper early richness curve. Byte-identical output (verified with a normalized config dump diff). Co-Authored-By: Claude Fable 5 --- .../config/vanilla_ores_one_direction.lua | 79 ++----------------- 1 file changed, 6 insertions(+), 73 deletions(-) diff --git a/map_gen/maps/danger_ores/config/vanilla_ores_one_direction.lua b/map_gen/maps/danger_ores/config/vanilla_ores_one_direction.lua index 2a42407ee..65f8a75e7 100644 --- a/map_gen/maps/danger_ores/config/vanilla_ores_one_direction.lua +++ b/map_gen/maps/danger_ores/config/vanilla_ores_one_direction.lua @@ -1,76 +1,9 @@ +-- vanilla_ores with a steeper early richness curve, tuned for the One Direction map. local b = require 'map_gen.shared.builders' -local start_value = b.euclidean_value(0, 0.35) -local value = b.exponential_value(0, 0.15, 1.2) +local Factory = require 'map_gen.maps.danger_ores.config.main_ores_factory' -return { - { - name = 'copper-ore', - ['tiles'] = { - [1] = 'red-desert-0', - [2] = 'red-desert-1', - [3] = 'red-desert-2', - [4] = 'red-desert-3' - }, - ['start'] = start_value, - ['weight'] = 1, - ['ratios'] = { - {resource = b.resource(b.full_shape, 'iron-ore', value), weight = 15}, - {resource = b.resource(b.full_shape, 'copper-ore', value), weight = 70}, - {resource = b.resource(b.full_shape, 'stone', value), weight = 10}, - {resource = b.resource(b.full_shape, 'coal', value), weight = 5} - } - }, - { - name = 'coal', - ['tiles'] = { - [1] = 'dirt-1', - [2] = 'dirt-2', - [3] = 'dirt-3', - [4] = 'dirt-4', - [5] = 'dirt-5', - [6] = 'dirt-6', - [7] = 'dirt-7' - }, - ['start'] = start_value, - ['weight'] = 1, - ['ratios'] = { - {resource = b.resource(b.full_shape, 'iron-ore', value), weight = 18}, - {resource = b.resource(b.full_shape, 'copper-ore', value), weight = 9}, - {resource = b.resource(b.full_shape, 'stone', value), weight = 8}, - {resource = b.resource(b.full_shape, 'coal', value), weight = 65} - } - }, - { - name = 'iron-ore', - ['tiles'] = { - [1] = 'grass-1', - [2] = 'grass-2', - [3] = 'grass-3', - [4] = 'grass-4' - }, - ['start'] = start_value, - ['weight'] = 1, - ['ratios'] = { - {resource = b.resource(b.full_shape, 'iron-ore', value), weight = 75}, - {resource = b.resource(b.full_shape, 'copper-ore', value), weight = 13}, - {resource = b.resource(b.full_shape, 'stone', value), weight = 7}, - {resource = b.resource(b.full_shape, 'coal', value), weight = 5} - } - }, - --[[ { - name = 'stone', - ['tiles'] = { - [1] = 'sand-1', - [2] = 'sand-2', - [3] = 'sand-3' - }, - ['start'] = start_value, - ['weight'] = 1, - ['ratios'] = { - {resource = b.resource(b.full_shape, 'iron-ore', value), weight = 25}, - {resource = b.resource(b.full_shape, 'copper-ore', value), weight = 10}, - {resource = b.resource(b.full_shape, 'stone', value), weight = 60}, - {resource = b.resource(b.full_shape, 'coal', value), weight = 5} - } - } ]] +return Factory.main_ores { + ores = {'copper-ore', 'coal', 'iron-ore'}, + start = b.euclidean_value(0, 0.35), + value = b.exponential_value(0, 0.15, 1.2) } From ab8c633faab493b809e40b68f9f80e760fc877d8 Mon Sep 17 00:00:00 2001 From: lex <5781244+lex@users.noreply.github.com> Date: Wed, 8 Jul 2026 16:34:29 +0300 Subject: [PATCH 10/31] Danger Ores: derive vanilla_ores_landfill from the factory Same config as vanilla_ores apart from landfill tiles and a flatter curve. Byte-identical output (verified with a normalized config dump diff). Co-Authored-By: Claude Fable 5 --- .../config/vanilla_ores_landfill.lua | 69 +++---------------- 1 file changed, 8 insertions(+), 61 deletions(-) diff --git a/map_gen/maps/danger_ores/config/vanilla_ores_landfill.lua b/map_gen/maps/danger_ores/config/vanilla_ores_landfill.lua index c2d6cea09..554013fab 100644 --- a/map_gen/maps/danger_ores/config/vanilla_ores_landfill.lua +++ b/map_gen/maps/danger_ores/config/vanilla_ores_landfill.lua @@ -1,64 +1,11 @@ +-- vanilla_ores skinned entirely in landfill tiles, with a slightly flatter richness +-- curve. Used by maps where the whole ore field doubles as buildable ground. local b = require 'map_gen.shared.builders' -local start_value = b.euclidean_value(0, 0.35) -local value = b.exponential_value(0, 0.06, 1.55) +local Factory = require 'map_gen.maps.danger_ores.config.main_ores_factory' -return { - { - name = 'copper-ore', - ['tiles'] = { - [1] = 'landfill' - }, - ['start'] = start_value, - ['weight'] = 1, - ['ratios'] = { - {resource = b.resource(b.full_shape, 'iron-ore', value), weight = 15}, - {resource = b.resource(b.full_shape, 'copper-ore', value), weight = 70}, - {resource = b.resource(b.full_shape, 'stone', value), weight = 10}, - {resource = b.resource(b.full_shape, 'coal', value), weight = 5} - } - }, - { - name = 'coal', - ['tiles'] = { - [1] = 'landfill' - }, - ['start'] = start_value, - ['weight'] = 1, - ['ratios'] = { - {resource = b.resource(b.full_shape, 'iron-ore', value), weight = 18}, - {resource = b.resource(b.full_shape, 'copper-ore', value), weight = 9}, - {resource = b.resource(b.full_shape, 'stone', value), weight = 8}, - {resource = b.resource(b.full_shape, 'coal', value), weight = 65} - } - }, - { - name = 'iron-ore', - ['tiles'] = { - [1] = 'landfill' - }, - ['start'] = start_value, - ['weight'] = 1, - ['ratios'] = { - {resource = b.resource(b.full_shape, 'iron-ore', value), weight = 75}, - {resource = b.resource(b.full_shape, 'copper-ore', value), weight = 13}, - {resource = b.resource(b.full_shape, 'stone', value), weight = 7}, - {resource = b.resource(b.full_shape, 'coal', value), weight = 5} - } - }, - --[[ { - name = 'stone', - ['tiles'] = { - [1] = 'sand-1', - [2] = 'sand-2', - [3] = 'sand-3' - }, - ['start'] = start_value, - ['weight'] = 1, - ['ratios'] = { - {resource = b.resource(b.full_shape, 'iron-ore', value), weight = 25}, - {resource = b.resource(b.full_shape, 'copper-ore', value), weight = 10}, - {resource = b.resource(b.full_shape, 'stone', value), weight = 60}, - {resource = b.resource(b.full_shape, 'coal', value), weight = 5} - } - } ]] +return Factory.main_ores { + ores = {'copper-ore', 'coal', 'iron-ore'}, + start = b.euclidean_value(0, 0.35), + value = b.exponential_value(0, 0.06, 1.55), + tiles = {'landfill'} } From c62c175b1fd09c4853e36a02a56342740938c7d4 Mon Sep 17 00:00:00 2001 From: lex <5781244+lex@users.noreply.github.com> Date: Wed, 8 Jul 2026 16:34:29 +0300 Subject: [PATCH 11/31] Danger Ores: derive one_direction_ores_xmas from the factory Same mixes; exponential start and trimmed dirt/grass shades for the snow look. Byte-identical output (verified with a normalized config dump diff). Co-Authored-By: Claude Fable 5 --- .../config/one_direction_ores_xmas.lua | 80 +++---------------- 1 file changed, 11 insertions(+), 69 deletions(-) diff --git a/map_gen/maps/danger_ores/config/one_direction_ores_xmas.lua b/map_gen/maps/danger_ores/config/one_direction_ores_xmas.lua index 3348bdbb9..f5f883264 100644 --- a/map_gen/maps/danger_ores/config/one_direction_ores_xmas.lua +++ b/map_gen/maps/danger_ores/config/one_direction_ores_xmas.lua @@ -1,74 +1,16 @@ +-- The xmas variant of the One Direction ores: exponential start (no guaranteed-rich +-- spawn), and the darkest dirt/grass shades dropped for a snowier look. local b = require 'map_gen.shared.builders' -local start_value = b.exponential_value(0, 0.15, 1.3) +local Factory = require 'map_gen.maps.danger_ores.config.main_ores_factory' + local value = b.exponential_value(0, 0.15, 1.3) -return { - { - name = 'copper-ore', - ['tiles'] = { - [1] = 'red-desert-0', - [2] = 'red-desert-1', - [3] = 'red-desert-2', - [4] = 'red-desert-3' - }, - ['start'] = start_value, - ['weight'] = 1, - ['ratios'] = { - {resource = b.resource(b.full_shape, 'iron-ore', value), weight = 15}, - {resource = b.resource(b.full_shape, 'copper-ore', value), weight = 70}, - {resource = b.resource(b.full_shape, 'stone', value), weight = 10}, - {resource = b.resource(b.full_shape, 'coal', value), weight = 5} - } - }, - { - name = 'coal', - ['tiles'] = { - [1] = 'dirt-1', - [2] = 'dirt-2', - [3] = 'dirt-3', - [4] = 'dirt-5', - [5] = 'dirt-6', - [6] = 'dirt-7' - }, - ['start'] = start_value, - ['weight'] = 1, - ['ratios'] = { - {resource = b.resource(b.full_shape, 'iron-ore', value), weight = 18}, - {resource = b.resource(b.full_shape, 'copper-ore', value), weight = 9}, - {resource = b.resource(b.full_shape, 'stone', value), weight = 8}, - {resource = b.resource(b.full_shape, 'coal', value), weight = 65} - } - }, - { - name = 'iron-ore', - ['tiles'] = { - [1] = 'grass-2', - [2] = 'grass-3', - [3] = 'grass-4' - }, - ['start'] = start_value, - ['weight'] = 1, - ['ratios'] = { - {resource = b.resource(b.full_shape, 'iron-ore', value), weight = 75}, - {resource = b.resource(b.full_shape, 'copper-ore', value), weight = 13}, - {resource = b.resource(b.full_shape, 'stone', value), weight = 7}, - {resource = b.resource(b.full_shape, 'coal', value), weight = 5} - } +return Factory.main_ores { + ores = { + 'copper-ore', + {name = 'coal', tiles = {'dirt-1', 'dirt-2', 'dirt-3', 'dirt-5', 'dirt-6', 'dirt-7'}}, + {name = 'iron-ore', tiles = {'grass-2', 'grass-3', 'grass-4'}} }, - --[[ { - name = 'stone', - ['tiles'] = { - [1] = 'sand-1', - [2] = 'sand-2', - [3] = 'sand-3' - }, - ['start'] = start_value, - ['weight'] = 1, - ['ratios'] = { - {resource = b.resource(b.full_shape, 'iron-ore', value), weight = 25}, - {resource = b.resource(b.full_shape, 'copper-ore', value), weight = 10}, - {resource = b.resource(b.full_shape, 'stone', value), weight = 60}, - {resource = b.resource(b.full_shape, 'coal', value), weight = 5} - } - } ]] + start = value, + value = value } From 325aa7a3479f136d4d5df1475e9f9cef8a0ae97b Mon Sep 17 00:00:00 2001 From: lex <5781244+lex@users.noreply.github.com> Date: Wed, 8 Jul 2026 16:34:29 +0300 Subject: [PATCH 12/31] Danger Ores: derive x_cross_ores from the factory Canonical mixes except the coal sector, which uses the shared coal-rich mix; 3way_ores keeps deriving from this file unchanged. Byte-identical output (verified with a normalized config dump diff). Co-Authored-By: Claude Fable 5 --- .../maps/danger_ores/config/x_cross_ores.lua | 83 +++---------------- 1 file changed, 12 insertions(+), 71 deletions(-) diff --git a/map_gen/maps/danger_ores/config/x_cross_ores.lua b/map_gen/maps/danger_ores/config/x_cross_ores.lua index c5b9f21af..2b5c7ade0 100644 --- a/map_gen/maps/danger_ores/config/x_cross_ores.lua +++ b/map_gen/maps/danger_ores/config/x_cross_ores.lua @@ -1,76 +1,17 @@ +-- The 4-ore X-Cross config: canonical mixes except for a coal-heavier coal sector, +-- with an exponential start (no guaranteed-rich spawn). 3way_ores derives from this. local b = require 'map_gen.shared.builders' -local start_value = b.exponential_value(0, 0.15, 1.3) +local Factory = require 'map_gen.maps.danger_ores.config.main_ores_factory' + local value = b.exponential_value(0, 0.15, 1.3) -return { - { - name = 'copper-ore', - ['tiles'] = { - [1] = 'red-desert-0', - [2] = 'red-desert-1', - [3] = 'red-desert-2', - [4] = 'red-desert-3' - }, - ['start'] = start_value, - ['weight'] = 1, - ['ratios'] = { - {resource = b.resource(b.full_shape, 'iron-ore', value), weight = 15}, - {resource = b.resource(b.full_shape, 'copper-ore', value), weight = 70}, - {resource = b.resource(b.full_shape, 'stone', value), weight = 10}, - {resource = b.resource(b.full_shape, 'coal', value), weight = 5} - } - }, - { - name = 'coal', - ['tiles'] = { - [1] = 'dirt-1', - [2] = 'dirt-2', - [3] = 'dirt-3', - [4] = 'dirt-4', - [5] = 'dirt-5', - [6] = 'dirt-6', - [7] = 'dirt-7' - }, - ['start'] = start_value, - ['weight'] = 1, - ['ratios'] = { - {resource = b.resource(b.full_shape, 'iron-ore', value), weight = 14}, - {resource = b.resource(b.full_shape, 'copper-ore', value), weight = 6}, - {resource = b.resource(b.full_shape, 'stone', value), weight = 10}, - {resource = b.resource(b.full_shape, 'coal', value), weight = 70} - } - }, - { - name = 'iron-ore', - ['tiles'] = { - [1] = 'grass-1', - [2] = 'grass-2', - [3] = 'grass-3', - [4] = 'grass-4' - }, - ['start'] = start_value, - ['weight'] = 1, - ['ratios'] = { - {resource = b.resource(b.full_shape, 'iron-ore', value), weight = 75}, - {resource = b.resource(b.full_shape, 'copper-ore', value), weight = 13}, - {resource = b.resource(b.full_shape, 'stone', value), weight = 7}, - {resource = b.resource(b.full_shape, 'coal', value), weight = 5} - } +return Factory.main_ores { + ores = { + 'copper-ore', + {name = 'coal', mix = 'coal-rich'}, + 'iron-ore', + 'stone' }, - { - name = 'stone', - ['tiles'] = { - [1] = 'sand-1', - [2] = 'sand-2', - [3] = 'sand-3' - }, - ['start'] = start_value, - ['weight'] = 1, - ['ratios'] = { - {resource = b.resource(b.full_shape, 'iron-ore', value), weight = 25}, - {resource = b.resource(b.full_shape, 'copper-ore', value), weight = 10}, - {resource = b.resource(b.full_shape, 'stone', value), weight = 60}, - {resource = b.resource(b.full_shape, 'coal', value), weight = 5} - } - } + start = value, + value = value } From b5d9ceba86809214c6bb2ad61db1945ee9990ed7 Mon Sep 17 00:00:00 2001 From: lex <5781244+lex@users.noreply.github.com> Date: Wed, 8 Jul 2026 16:37:14 +0300 Subject: [PATCH 13/31] Danger Ores: derive vanilla_ores_gridlocked from the factory Canonical mixes on landfill; the rich offset curves and the 10/8/7/2 entry weights stay as this config's own numbers. Byte-identical output (verified with a normalized config dump diff). Co-Authored-By: Claude Fable 5 --- .../config/vanilla_ores_gridlocked.lua | 70 ++++--------------- 1 file changed, 12 insertions(+), 58 deletions(-) diff --git a/map_gen/maps/danger_ores/config/vanilla_ores_gridlocked.lua b/map_gen/maps/danger_ores/config/vanilla_ores_gridlocked.lua index e6c640def..b4118a352 100644 --- a/map_gen/maps/danger_ores/config/vanilla_ores_gridlocked.lua +++ b/map_gen/maps/danger_ores/config/vanilla_ores_gridlocked.lua @@ -1,62 +1,16 @@ +-- The Gridlocked config: canonical mixes on landfill tiles, with a rich guaranteed +-- floor (both curves offset by 50) and weighted ore order (iron-heavy, stone-light). local b = require 'map_gen.shared.builders' -local start_value = b.euclidean_value(50, 0.75) -local value = b.exponential_value(50, 0.003, 2.25) +local Factory = require 'map_gen.maps.danger_ores.config.main_ores_factory' -return { - { - name = 'iron-ore', - tiles = { - [1] = 'landfill', - }, - start = start_value, - weight = 10, - ratios = { - { resource = b.resource(b.full_shape, 'iron-ore', value), weight = 75 }, - { resource = b.resource(b.full_shape, 'copper-ore', value), weight = 13 }, - { resource = b.resource(b.full_shape, 'stone', value), weight = 7 }, - { resource = b.resource(b.full_shape, 'coal', value), weight = 5 }, - }, - }, - { - name = 'coal', - tiles = { - [1] = 'landfill', - }, - start = start_value, - weight = 8, - ratios = { - { resource = b.resource(b.full_shape, 'iron-ore', value), weight = 18 }, - { resource = b.resource(b.full_shape, 'copper-ore', value), weight = 9 }, - { resource = b.resource(b.full_shape, 'stone', value), weight = 8 }, - { resource = b.resource(b.full_shape, 'coal', value), weight = 65 }, - }, - }, - { - name = 'copper-ore', - tiles = { - [1] = 'landfill', - }, - start = start_value, - weight = 7, - ratios = { - { resource = b.resource(b.full_shape, 'iron-ore', value), weight = 15 }, - { resource = b.resource(b.full_shape, 'copper-ore', value), weight = 70 }, - { resource = b.resource(b.full_shape, 'stone', value), weight = 10 }, - { resource = b.resource(b.full_shape, 'coal', value), weight = 5 }, - }, - }, - { - name = 'stone', - tiles = { - [1] = 'landfill', - }, - start = start_value, - weight = 2, - ratios = { - { resource = b.resource(b.full_shape, 'iron-ore', value), weight = 25 }, - { resource = b.resource(b.full_shape, 'copper-ore', value), weight = 10 }, - { resource = b.resource(b.full_shape, 'stone', value), weight = 60 }, - { resource = b.resource(b.full_shape, 'coal', value), weight = 5 }, - }, +return Factory.main_ores { + ores = { + {name = 'iron-ore', weight = 10}, + {name = 'coal', weight = 8}, + {name = 'copper-ore', weight = 7}, + {name = 'stone', weight = 2} }, + start = b.euclidean_value(50, 0.75), + value = b.exponential_value(50, 0.003, 2.25), + tiles = {'landfill'} } From 72708e0e21e3f448853e3f1473bd15855bd90300 Mon Sep 17 00:00:00 2001 From: lex <5781244+lex@users.noreply.github.com> Date: Wed, 8 Jul 2026 16:37:14 +0300 Subject: [PATCH 14/31] Danger Ores: derive joker_starter_area from the factory Canonical mixes with the time-budgeted richness curves (coal on the 4h budget, the rest on 24h); the uranium extra entry stays hand-written. Byte-identical output (verified with a normalized config dump diff). Co-Authored-By: Claude Fable 5 --- .../danger_ores/config/joker_starter_area.lua | 103 ++++-------------- 1 file changed, 24 insertions(+), 79 deletions(-) diff --git a/map_gen/maps/danger_ores/config/joker_starter_area.lua b/map_gen/maps/danger_ores/config/joker_starter_area.lua index 60423a56b..a95d96a88 100644 --- a/map_gen/maps/danger_ores/config/joker_starter_area.lua +++ b/map_gen/maps/danger_ores/config/joker_starter_area.lua @@ -1,4 +1,8 @@ +-- The Joker starter area: canonical mixes with time-budgeted richness -- each patch +-- holds roughly this many hours of a single electric mining drill's output, with coal +-- on the small budget. Plus a token super-rich uranium entry. local b = require 'map_gen.shared.builders' +local Factory = require 'map_gen.maps.danger_ores.config.main_ores_factory' local mine_speed = 0.5 / 10 -- 0.5 ore/s per electric mining drill local low = 60 * 60 * 4 * mine_speed -- 4h @@ -7,86 +11,27 @@ local medium = 60 * 60 * 24 * mine_speed -- 24h local low_value = b.exponential_value(low, 0.07, 1.45) local medium_value = b.exponential_value(medium, 0.07, 1.45) -return { - { - name = 'copper-ore', - tiles = { - [1] = 'red-desert-0', - [2] = 'red-desert-1', - [3] = 'red-desert-2', - [4] = 'red-desert-3' - }, - start = 1, - weight = 1, - ratios = { - {resource = b.resource(b.full_shape, 'iron-ore', medium_value), weight = 15}, - {resource = b.resource(b.full_shape, 'copper-ore', medium_value), weight = 70}, - {resource = b.resource(b.full_shape, 'stone', medium_value), weight = 10}, - {resource = b.resource(b.full_shape, 'coal', medium_value), weight = 5} - } - }, - { - name = 'coal', - tiles = { - [1] = 'dirt-1', - [2] = 'dirt-2', - [3] = 'dirt-3', - [4] = 'dirt-4', - [5] = 'dirt-5', - [6] = 'dirt-6', - [7] = 'dirt-7' - }, - start = 1, - weight = 1, - ratios = { - {resource = b.resource(b.full_shape, 'iron-ore', low_value), weight = 18}, - {resource = b.resource(b.full_shape, 'copper-ore', low_value), weight = 9}, - {resource = b.resource(b.full_shape, 'stone', low_value), weight = 8}, - {resource = b.resource(b.full_shape, 'coal', low_value), weight = 65} - } - }, - { - name = 'iron-ore', - tiles = { - [1] = 'grass-1', - [2] = 'grass-2', - [3] = 'grass-3', - [4] = 'grass-4' - }, - start = 1, - weight = 1, - ratios = { - {resource = b.resource(b.full_shape, 'iron-ore', medium_value), weight = 75}, - {resource = b.resource(b.full_shape, 'copper-ore', medium_value), weight = 13}, - {resource = b.resource(b.full_shape, 'stone', medium_value), weight = 7}, - {resource = b.resource(b.full_shape, 'coal', medium_value), weight = 5} - } +local config = Factory.main_ores { + ores = { + 'copper-ore', + {name = 'coal', value = low_value}, + 'iron-ore', + 'stone' }, - { - name = 'stone', - tiles = { - [1] = 'sand-1', - [2] = 'sand-2', - [3] = 'sand-3' - }, - start = 1, - weight = 1, - ratios = { - {resource = b.resource(b.full_shape, 'iron-ore', medium_value), weight = 25}, - {resource = b.resource(b.full_shape, 'copper-ore', medium_value), weight = 10}, - {resource = b.resource(b.full_shape, 'stone', medium_value), weight = 60}, - {resource = b.resource(b.full_shape, 'coal', medium_value), weight = 5} - } - }, - { - name = 'uranium-ore', - start = 1, - weight = 1, - ratios = { - { - resource = b.resource(b.full_shape, 'uranium-ore', function() return 1e6 end), - weight = 1 - }, + start = 1, + value = medium_value +} + +config[#config + 1] = { + name = 'uranium-ore', + start = 1, + weight = 1, + ratios = { + { + resource = b.resource(b.full_shape, 'uranium-ore', function() return 1e6 end), + weight = 1 } } } + +return config From 54e16940590db32efc161efb18da1d778cb5d09e Mon Sep 17 00:00:00 2001 From: lex <5781244+lex@users.noreply.github.com> Date: Wed, 8 Jul 2026 16:37:14 +0300 Subject: [PATCH 15/31] Danger Ores: derive poor_mans_coal_fields_ores from the factory Canonical mixes (coal-rich coal sector) with the map's degrading-resource closures plugged in via make_resource. Verified equivalent both with the normalized dump diff and by executing old vs new resource closures over sample coordinates (identical ore names and amounts). Co-Authored-By: Claude Fable 5 --- .../config/poor_mans_coal_fields_ores.lua | 159 +++--------------- 1 file changed, 27 insertions(+), 132 deletions(-) diff --git a/map_gen/maps/danger_ores/config/poor_mans_coal_fields_ores.lua b/map_gen/maps/danger_ores/config/poor_mans_coal_fields_ores.lua index b7c1e8f0a..0b57f827a 100644 --- a/map_gen/maps/danger_ores/config/poor_mans_coal_fields_ores.lua +++ b/map_gen/maps/danger_ores/config/poor_mans_coal_fields_ores.lua @@ -1,141 +1,36 @@ +-- Poor Man's Coal Fields: canonical mixes (with the coal-rich coal sector), but the +-- minor ores only show near spawn -- above the richness threshold a patch yields the +-- sector's dominant ore instead of the mixed-in one. local b = require 'map_gen.shared.builders' -local start_value = b.euclidean_value(0, 0.35) +local Factory = require 'map_gen.maps.danger_ores.config.main_ores_factory' + local value = b.exponential_value(0, 0.06, 1.55) -local function resource(primary_ore, secondary_ore) - return function(_, _, world) - local v = value(world.x, world.y) - local ore - if v > 1500 then - ore = primary_ore - else - ore = secondary_ore - end +local function degrading_resource(primary_ore) + return function(secondary_ore) + return function(_, _, world) + local v = value(world.x, world.y) + local ore + if v > 1500 then + ore = primary_ore + else + ore = secondary_ore + end - return { - name = ore, - amount = v - } + return { + name = ore, + amount = v + } + end end end -return { - { - name = 'copper-ore', - ['tiles'] = { - [1] = 'red-desert-0', - [2] = 'red-desert-1', - [3] = 'red-desert-2', - [4] = 'red-desert-3' - }, - ['start'] = start_value, - ['weight'] = 1, - ['ratios'] = { - { - resource = resource('copper-ore', 'iron-ore'), - weight = 15 - }, - { - resource = resource('copper-ore', 'copper-ore'), - weight = 70 - }, - { - resource = resource('copper-ore', 'stone'), - weight = 10 - }, - { - resource = resource('copper-ore', 'coal'), - weight = 5 - } - } +return Factory.main_ores { + ores = { + {name = 'copper-ore', make_resource = degrading_resource('copper-ore')}, + {name = 'coal', mix = 'coal-rich', make_resource = degrading_resource('coal')}, + {name = 'iron-ore', make_resource = degrading_resource('iron-ore')}, + {name = 'stone', make_resource = degrading_resource('stone')} }, - { - name = 'coal', - ['tiles'] = { - [1] = 'dirt-1', - [2] = 'dirt-2', - [3] = 'dirt-3', - [4] = 'dirt-4', - [5] = 'dirt-5', - [6] = 'dirt-6', - [7] = 'dirt-7' - }, - ['start'] = start_value, - ['weight'] = 1, - ['ratios'] = { - { - resource = resource('coal', 'iron-ore'), - weight = 14 - }, - { - resource = resource('coal', 'copper-ore'), - weight = 6 - }, - { - resource = resource('coal', 'stone'), - weight = 10 - }, - { - resource = resource('coal', 'coal'), - weight = 70 - } - } - }, - { - name = 'iron-ore', - ['tiles'] = { - [1] = 'grass-1', - [2] = 'grass-2', - [3] = 'grass-3', - [4] = 'grass-4' - }, - ['start'] = start_value, - ['weight'] = 1, - ['ratios'] = { - { - resource = resource('iron-ore', 'iron-ore'), - weight = 75 - }, - { - resource = resource('iron-ore', 'copper-ore'), - weight = 13 - }, - { - resource = resource('iron-ore', 'stone'), - weight = 7 - }, - { - resource = resource('iron-ore', 'coal'), - weight = 5 - } - } - }, - { - name = 'stone', - ['tiles'] = { - [1] = 'sand-1', - [2] = 'sand-2', - [3] = 'sand-3' - }, - ['start'] = start_value, - ['weight'] = 1, - ['ratios'] = { - { - resource = resource('stone', 'iron-ore'), - weight = 25 - }, - { - resource = resource('stone', 'copper-ore'), - weight = 10 - }, - { - resource = resource('stone', 'stone'), - weight = 60 - }, - { - resource = resource('stone', 'coal'), - weight = 5 - } - } - } + start = b.euclidean_value(0, 0.35) } From cead7e5b5857b2b01f34e67b835fe7ecc1666990 Mon Sep 17 00:00:00 2001 From: lex <5781244+lex@users.noreply.github.com> Date: Wed, 8 Jul 2026 16:37:14 +0300 Subject: [PATCH 16/31] Danger Ores: build the vanilla_ores_stone stone entry via the factory The three vanilla entries keep coming from vanilla_ores directly (same tables); only the stone sector is now a factory entry. Byte-identical output (verified with a normalized config dump diff). Co-Authored-By: Claude Fable 5 --- .../danger_ores/config/vanilla_ores_stone.lua | 28 +++++++------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/map_gen/maps/danger_ores/config/vanilla_ores_stone.lua b/map_gen/maps/danger_ores/config/vanilla_ores_stone.lua index 03bac9c86..985d6e2b6 100644 --- a/map_gen/maps/danger_ores/config/vanilla_ores_stone.lua +++ b/map_gen/maps/danger_ores/config/vanilla_ores_stone.lua @@ -1,9 +1,11 @@ -- Vanilla ores plus stone as a fourth main ore: the three vanilla entries come straight --- from config/vanilla_ores.lua (one source of truth for the numbers), with a stone entry --- using the same distance curves. Order is significant: it maps to ore indices 1..4. --- Used by maps whose layouts need four main ores, e.g. the clean-border partition maps --- (Tetrominoes, Voronoi), whose region colouring requires at least 4 colours. +-- from config/vanilla_ores.lua (one source of truth for the numbers), with a factory +-- stone entry using the same distance curves. Order is significant: it maps to ore +-- indices 1..4. Used by maps whose layouts need four main ores, e.g. the clean-border +-- partition maps (Tetrominoes, Voronoi), whose region colouring requires at least +-- 4 colours. local b = require 'map_gen.shared.builders' +local Factory = require 'map_gen.maps.danger_ores.config.main_ores_factory' local vanilla_ores = require 'map_gen.maps.danger_ores.config.vanilla_ores' local by_name = {} @@ -11,23 +13,13 @@ for _, ore in ipairs(vanilla_ores) do by_name[ore.name] = ore end -local start_value = b.euclidean_value(0, 0.35) -local value = b.exponential_value(0, 0.07, 1.45) - return { by_name['iron-ore'], by_name['copper-ore'], by_name['coal'], - { + Factory.entry { name = 'stone', - tiles = { 'sand-1', 'sand-2', 'sand-3' }, - start = start_value, - weight = 1, - ratios = { - { resource = b.resource(b.full_shape, 'iron-ore', value), weight = 25 }, - { resource = b.resource(b.full_shape, 'copper-ore', value), weight = 10 }, - { resource = b.resource(b.full_shape, 'stone', value), weight = 60 }, - { resource = b.resource(b.full_shape, 'coal', value), weight = 5 }, - }, - }, + start = b.euclidean_value(0, 0.35), + value = b.exponential_value(0, 0.07, 1.45) + } } From dd7f5e013274ba930f1d8ee4d450206d51e9cd4c Mon Sep 17 00:00:00 2001 From: lex <5781244+lex@users.noreply.github.com> Date: Wed, 8 Jul 2026 16:38:12 +0300 Subject: [PATCH 17/31] Danger Ores: cover the main-ores factory in the config test suite Canonical mix weights, fresh-tables-per-call, entry shape, and rejection of entries with no richness source. Co-Authored-By: Claude Fable 5 --- .../danger_ores/config/ore_configs.test.lua | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/map_gen/maps/danger_ores/config/ore_configs.test.lua b/map_gen/maps/danger_ores/config/ore_configs.test.lua index a04959404..e5a163015 100644 --- a/map_gen/maps/danger_ores/config/ore_configs.test.lua +++ b/map_gen/maps/danger_ores/config/ore_configs.test.lua @@ -100,7 +100,36 @@ check(rawequal(with_stone[1], vanilla_by_name['iron-ore']) 'vanilla_ores_stone entries 1-3 ARE the vanilla_ores entries') check(with_stone[4].name == 'stone', 'vanilla_ores_stone entry 4 is stone') --- 3) golden numbers for the source configs, so old maps cannot drift by accident +-- 3) the factory: canonical numbers, and fresh tables per call so configs can never +-- share mutable state at runtime +local Factory = require 'map_gen.maps.danger_ores.config.main_ores_factory' + +local function mix_weights(mix) + local weights = {} + for _, pair in ipairs(Factory.mixes[mix]) do + weights[#weights + 1] = pair[1] .. ':' .. pair[2] + end + return table.concat(weights, ' ') +end +check(mix_weights('iron-ore') == 'iron-ore:75 copper-ore:13 stone:7 coal:5' + and mix_weights('copper-ore') == 'iron-ore:15 copper-ore:70 stone:10 coal:5' + and mix_weights('coal') == 'iron-ore:18 copper-ore:9 stone:8 coal:65' + and mix_weights('stone') == 'iron-ore:25 copper-ore:10 stone:60 coal:5' + and mix_weights('coal-rich') == 'iron-ore:14 copper-ore:6 stone:10 coal:70', + 'factory mixes carry the canonical weights') + +local entry_a = Factory.entry {name = 'iron-ore', start = 1, value = 'v'} +local entry_b = Factory.entry {name = 'iron-ore', start = 1, value = 'v'} +check(not rawequal(entry_a.tiles, entry_b.tiles) and not rawequal(entry_a.ratios, entry_b.ratios), + 'factory builds fresh tiles and ratios tables per call') +check(entry_a.tiles[1] == 'grass-1' and #entry_a.tiles == 4 and entry_a.weight == 1 + and #entry_a.ratios == 4 and entry_a.ratios[1].weight == 75, + 'factory entry has the iron tile set and mix') + +local ok_no_value = pcall(Factory.entry, {name = 'iron-ore', start = 1}) +check(not ok_no_value, 'factory entry rejects a missing value/make_resource') + +-- 4) golden numbers for the source configs, so old maps cannot drift by accident local function ratio_weights(entry) local weights = {} for _, ratio in ipairs(entry.ratios) do From e6740b59edd4e35282e012659cfc8bb367dd4678 Mon Sep 17 00:00:00 2001 From: lex <5781244+lex@users.noreply.github.com> Date: Wed, 8 Jul 2026 16:53:43 +0300 Subject: [PATCH 18/31] Danger Ores: silence unused stub argument in ore config tests Co-Authored-By: Claude Fable 5 --- map_gen/maps/danger_ores/config/ore_configs.test.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/map_gen/maps/danger_ores/config/ore_configs.test.lua b/map_gen/maps/danger_ores/config/ore_configs.test.lua index e5a163015..2b01beb1f 100644 --- a/map_gen/maps/danger_ores/config/ore_configs.test.lua +++ b/map_gen/maps/danger_ores/config/ore_configs.test.lua @@ -10,7 +10,7 @@ end function fake_builders.exponential_value(base, mult, pow) return ('exp(%s,%s,%s)'):format(base, mult, pow) end -function fake_builders.resource(shape, name, value) +function fake_builders.resource(_, name, value) return { kind = 'resource', name = name, value = value } end setmetatable(fake_builders, { From 2925479ba0f48083bfdcc3f10ca2405428cc0c4f Mon Sep 17 00:00:00 2001 From: lex <5781244+lex@users.noreply.github.com> Date: Thu, 9 Jul 2026 12:46:53 +0300 Subject: [PATCH 19/31] Danger Ores: fold the coal-rich mix into the canonical coal mix The X-Cross family and Poor Man's Coal Fields used a coal sector of 14/6/10/70 against vanilla's 18/9/8/65 -- a +5% coal boost not worth a second canonical mix. Both now use the vanilla coal mix. Behavior change: X-Cross/3-Way/Square/Poor Man's coal sectors yield slightly less coal and slightly more iron/copper. Co-Authored-By: Claude Fable 5 --- map_gen/maps/danger_ores/config/main_ores_factory.lua | 4 +--- map_gen/maps/danger_ores/config/ore_configs.test.lua | 7 +++---- .../danger_ores/config/poor_mans_coal_fields_ores.lua | 4 ++-- map_gen/maps/danger_ores/config/x_cross_ores.lua | 11 +++-------- 4 files changed, 9 insertions(+), 17 deletions(-) diff --git a/map_gen/maps/danger_ores/config/main_ores_factory.lua b/map_gen/maps/danger_ores/config/main_ores_factory.lua index 5d06ec816..d8fbf4456 100644 --- a/map_gen/maps/danger_ores/config/main_ores_factory.lua +++ b/map_gen/maps/danger_ores/config/main_ores_factory.lua @@ -19,9 +19,7 @@ local mixes = { ['iron-ore'] = {{'iron-ore', 75}, {'copper-ore', 13}, {'stone', 7}, {'coal', 5}}, ['copper-ore'] = {{'iron-ore', 15}, {'copper-ore', 70}, {'stone', 10}, {'coal', 5}}, ['coal'] = {{'iron-ore', 18}, {'copper-ore', 9}, {'stone', 8}, {'coal', 65}}, - ['stone'] = {{'iron-ore', 25}, {'copper-ore', 10}, {'stone', 60}, {'coal', 5}}, - -- the coal-heavier mix used by the X-Cross family and Poor Man's Coal Fields - ['coal-rich'] = {{'iron-ore', 14}, {'copper-ore', 6}, {'stone', 10}, {'coal', 70}} + ['stone'] = {{'iron-ore', 25}, {'copper-ore', 10}, {'stone', 60}, {'coal', 5}} } Public.mixes = mixes diff --git a/map_gen/maps/danger_ores/config/ore_configs.test.lua b/map_gen/maps/danger_ores/config/ore_configs.test.lua index 2b01beb1f..d1d8f0399 100644 --- a/map_gen/maps/danger_ores/config/ore_configs.test.lua +++ b/map_gen/maps/danger_ores/config/ore_configs.test.lua @@ -114,8 +114,7 @@ end check(mix_weights('iron-ore') == 'iron-ore:75 copper-ore:13 stone:7 coal:5' and mix_weights('copper-ore') == 'iron-ore:15 copper-ore:70 stone:10 coal:5' and mix_weights('coal') == 'iron-ore:18 copper-ore:9 stone:8 coal:65' - and mix_weights('stone') == 'iron-ore:25 copper-ore:10 stone:60 coal:5' - and mix_weights('coal-rich') == 'iron-ore:14 copper-ore:6 stone:10 coal:70', + and mix_weights('stone') == 'iron-ore:25 copper-ore:10 stone:60 coal:5', 'factory mixes carry the canonical weights') local entry_a = Factory.entry {name = 'iron-ore', start = 1, value = 'v'} @@ -141,9 +140,9 @@ check(ratio_weights(vanilla_by_name['iron-ore']) == '75/13/7/5' and ratio_weights(vanilla_by_name['copper-ore']) == '15/70/10/5' and ratio_weights(vanilla_by_name['coal']) == '18/9/8/65', 'vanilla_ores ratio weights unchanged (75/13/7/5, 15/70/10/5, 18/9/8/65)') -check(ratio_weights(x_cross[1]) == '15/70/10/5' and ratio_weights(x_cross[2]) == '14/6/10/70' +check(ratio_weights(x_cross[1]) == '15/70/10/5' and ratio_weights(x_cross[2]) == '18/9/8/65' and ratio_weights(x_cross[3]) == '75/13/7/5' and ratio_weights(x_cross[4]) == '25/10/60/5', - 'x_cross_ores ratio weights unchanged') + 'x_cross_ores uses the canonical mixes (coal-rich folded into coal)') check(ratio_weights(with_stone[4]) == '25/10/60/5', 'stone entry ratio weights unchanged') if failures == 0 then diff --git a/map_gen/maps/danger_ores/config/poor_mans_coal_fields_ores.lua b/map_gen/maps/danger_ores/config/poor_mans_coal_fields_ores.lua index 0b57f827a..d6c504c11 100644 --- a/map_gen/maps/danger_ores/config/poor_mans_coal_fields_ores.lua +++ b/map_gen/maps/danger_ores/config/poor_mans_coal_fields_ores.lua @@ -1,4 +1,4 @@ --- Poor Man's Coal Fields: canonical mixes (with the coal-rich coal sector), but the +-- Poor Man's Coal Fields: canonical mixes, but the -- minor ores only show near spawn -- above the richness threshold a patch yields the -- sector's dominant ore instead of the mixed-in one. local b = require 'map_gen.shared.builders' @@ -28,7 +28,7 @@ end return Factory.main_ores { ores = { {name = 'copper-ore', make_resource = degrading_resource('copper-ore')}, - {name = 'coal', mix = 'coal-rich', make_resource = degrading_resource('coal')}, + {name = 'coal', make_resource = degrading_resource('coal')}, {name = 'iron-ore', make_resource = degrading_resource('iron-ore')}, {name = 'stone', make_resource = degrading_resource('stone')} }, diff --git a/map_gen/maps/danger_ores/config/x_cross_ores.lua b/map_gen/maps/danger_ores/config/x_cross_ores.lua index 2b5c7ade0..5fd15b719 100644 --- a/map_gen/maps/danger_ores/config/x_cross_ores.lua +++ b/map_gen/maps/danger_ores/config/x_cross_ores.lua @@ -1,17 +1,12 @@ --- The 4-ore X-Cross config: canonical mixes except for a coal-heavier coal sector, --- with an exponential start (no guaranteed-rich spawn). 3way_ores derives from this. +-- The 4-ore X-Cross config: canonical mixes with an exponential start (no +-- guaranteed-rich spawn). 3way_ores derives from this. local b = require 'map_gen.shared.builders' local Factory = require 'map_gen.maps.danger_ores.config.main_ores_factory' local value = b.exponential_value(0, 0.15, 1.3) return Factory.main_ores { - ores = { - 'copper-ore', - {name = 'coal', mix = 'coal-rich'}, - 'iron-ore', - 'stone' - }, + ores = {'copper-ore', 'coal', 'iron-ore', 'stone'}, start = value, value = value } From 4f86de20a2241f778fad2fa5c307ee2c120359e2 Mon Sep 17 00:00:00 2001 From: lex <5781244+lex@users.noreply.github.com> Date: Thu, 9 Jul 2026 12:47:57 +0300 Subject: [PATCH 20/31] Danger Ores: standardize richness curves on the vanilla pair One Direction (exp 0.15/1.2), Landfill (exp 0.06/1.55) and Xmas/X-Cross (exp-start + exp 0.15/1.3) all move to the vanilla euclidean(0, 0.35) start and exponential(0, 0.07, 1.45) richness. Gridlocked keeps its offset curves (deliberately rich, expansion is limited there), and Poor Man's keeps its own value -- the degrading-ore threshold depends on it. Behavior change: patch richness on One Direction, Landfill (and its Permanence/Lazy One/Spiral/Grid Factory users), Xmas Tree, X-Cross, 3-Way and Square shifts to the vanilla curve; the X-family also regains a guaranteed-rich spawn (euclidean start). Co-Authored-By: Claude Fable 5 --- .../danger_ores/config/one_direction_ores_xmas.lua | 10 ++++------ .../maps/danger_ores/config/vanilla_ores_landfill.lua | 6 +++--- .../danger_ores/config/vanilla_ores_one_direction.lua | 4 ++-- map_gen/maps/danger_ores/config/x_cross_ores.lua | 9 +++------ 4 files changed, 12 insertions(+), 17 deletions(-) diff --git a/map_gen/maps/danger_ores/config/one_direction_ores_xmas.lua b/map_gen/maps/danger_ores/config/one_direction_ores_xmas.lua index f5f883264..32c06701c 100644 --- a/map_gen/maps/danger_ores/config/one_direction_ores_xmas.lua +++ b/map_gen/maps/danger_ores/config/one_direction_ores_xmas.lua @@ -1,16 +1,14 @@ --- The xmas variant of the One Direction ores: exponential start (no guaranteed-rich --- spawn), and the darkest dirt/grass shades dropped for a snowier look. +-- The xmas variant of the One Direction ores: the darkest dirt/grass shades dropped +-- for a snowier look (richness standardized to the vanilla curves). local b = require 'map_gen.shared.builders' local Factory = require 'map_gen.maps.danger_ores.config.main_ores_factory' -local value = b.exponential_value(0, 0.15, 1.3) - return Factory.main_ores { ores = { 'copper-ore', {name = 'coal', tiles = {'dirt-1', 'dirt-2', 'dirt-3', 'dirt-5', 'dirt-6', 'dirt-7'}}, {name = 'iron-ore', tiles = {'grass-2', 'grass-3', 'grass-4'}} }, - start = value, - value = value + start = b.euclidean_value(0, 0.35), + value = b.exponential_value(0, 0.07, 1.45) } diff --git a/map_gen/maps/danger_ores/config/vanilla_ores_landfill.lua b/map_gen/maps/danger_ores/config/vanilla_ores_landfill.lua index 554013fab..9bf512488 100644 --- a/map_gen/maps/danger_ores/config/vanilla_ores_landfill.lua +++ b/map_gen/maps/danger_ores/config/vanilla_ores_landfill.lua @@ -1,11 +1,11 @@ --- vanilla_ores skinned entirely in landfill tiles, with a slightly flatter richness --- curve. Used by maps where the whole ore field doubles as buildable ground. +-- vanilla_ores skinned entirely in landfill tiles. Used by maps where the whole ore +-- field doubles as buildable ground. local b = require 'map_gen.shared.builders' local Factory = require 'map_gen.maps.danger_ores.config.main_ores_factory' return Factory.main_ores { ores = {'copper-ore', 'coal', 'iron-ore'}, start = b.euclidean_value(0, 0.35), - value = b.exponential_value(0, 0.06, 1.55), + value = b.exponential_value(0, 0.07, 1.45), tiles = {'landfill'} } diff --git a/map_gen/maps/danger_ores/config/vanilla_ores_one_direction.lua b/map_gen/maps/danger_ores/config/vanilla_ores_one_direction.lua index 65f8a75e7..c1d4f05f5 100644 --- a/map_gen/maps/danger_ores/config/vanilla_ores_one_direction.lua +++ b/map_gen/maps/danger_ores/config/vanilla_ores_one_direction.lua @@ -1,9 +1,9 @@ --- vanilla_ores with a steeper early richness curve, tuned for the One Direction map. +-- vanilla_ores for the One Direction maps (richness standardized to the vanilla curve). local b = require 'map_gen.shared.builders' local Factory = require 'map_gen.maps.danger_ores.config.main_ores_factory' return Factory.main_ores { ores = {'copper-ore', 'coal', 'iron-ore'}, start = b.euclidean_value(0, 0.35), - value = b.exponential_value(0, 0.15, 1.2) + value = b.exponential_value(0, 0.07, 1.45) } diff --git a/map_gen/maps/danger_ores/config/x_cross_ores.lua b/map_gen/maps/danger_ores/config/x_cross_ores.lua index 5fd15b719..a10f6c676 100644 --- a/map_gen/maps/danger_ores/config/x_cross_ores.lua +++ b/map_gen/maps/danger_ores/config/x_cross_ores.lua @@ -1,12 +1,9 @@ --- The 4-ore X-Cross config: canonical mixes with an exponential start (no --- guaranteed-rich spawn). 3way_ores derives from this. +-- The 4-ore X-Cross config: canonical mixes and curves. 3way_ores derives from this. local b = require 'map_gen.shared.builders' local Factory = require 'map_gen.maps.danger_ores.config.main_ores_factory' -local value = b.exponential_value(0, 0.15, 1.3) - return Factory.main_ores { ores = {'copper-ore', 'coal', 'iron-ore', 'stone'}, - start = value, - value = value + start = b.euclidean_value(0, 0.35), + value = b.exponential_value(0, 0.07, 1.45) } From 06e1dd2f25c9044689c90d18bd44e4d0cfe9f9b0 Mon Sep 17 00:00:00 2001 From: lex <5781244+lex@users.noreply.github.com> Date: Thu, 9 Jul 2026 12:51:15 +0300 Subject: [PATCH 21/31] Danger Ores: retire the redundant ore config files After the mix and curve standardization, four configs became pure duplicates or near-duplicates of the canon: - 3way_ores and vanilla_ores_one_direction were exactly vanilla_ores; their presets now inherit the configuration.lua default - x_cross_ores was vanilla_ores_stone in a different order, which is irrelevant since main_ores_shuffle_order shuffles it anyway; the preset uses vanilla_ores_stone - one_direction_ores_xmas kept only its snow tile trim; that moves inline into the xmas preset as a factory call Two canonical configs remain (vanilla_ores, vanilla_ores_stone) plus the genuine specials (landfill skin, gridlocked richness, joker, poor_mans, coal, scrap, gradient). Co-Authored-By: Claude Fable 5 --- map_gen/maps/danger_ores/config/3way_ores.lua | 5 ----- .../danger_ores/config/one_direction_ores_xmas.lua | 14 -------------- .../maps/danger_ores/config/ore_configs.test.lua | 14 ++------------ .../config/vanilla_ores_one_direction.lua | 9 --------- map_gen/maps/danger_ores/config/x_cross_ores.lua | 9 --------- .../maps/danger_ores/presets/danger_ore_3way.lua | 1 - .../presets/danger_ore_one_direction.lua | 1 - .../presets/danger_ore_one_direction_wide.lua | 1 - .../maps/danger_ores/presets/danger_ore_square.lua | 1 - .../danger_ores/presets/danger_ore_x_cross.lua | 2 +- .../danger_ores/presets/danger_ore_xmas_tree.lua | 13 ++++++++++++- 11 files changed, 15 insertions(+), 55 deletions(-) delete mode 100644 map_gen/maps/danger_ores/config/3way_ores.lua delete mode 100644 map_gen/maps/danger_ores/config/one_direction_ores_xmas.lua delete mode 100644 map_gen/maps/danger_ores/config/vanilla_ores_one_direction.lua delete mode 100644 map_gen/maps/danger_ores/config/x_cross_ores.lua diff --git a/map_gen/maps/danger_ores/config/3way_ores.lua b/map_gen/maps/danger_ores/config/3way_ores.lua deleted file mode 100644 index 26aaa574c..000000000 --- a/map_gen/maps/danger_ores/config/3way_ores.lua +++ /dev/null @@ -1,5 +0,0 @@ --- The 3-way sector ores are exactly the first three entries (copper, coal, iron) of the --- X-Cross config, which also carries the stone sector -- one source of truth for the numbers. -local x_cross_ores = require 'map_gen.maps.danger_ores.config.x_cross_ores' - -return { x_cross_ores[1], x_cross_ores[2], x_cross_ores[3] } diff --git a/map_gen/maps/danger_ores/config/one_direction_ores_xmas.lua b/map_gen/maps/danger_ores/config/one_direction_ores_xmas.lua deleted file mode 100644 index 32c06701c..000000000 --- a/map_gen/maps/danger_ores/config/one_direction_ores_xmas.lua +++ /dev/null @@ -1,14 +0,0 @@ --- The xmas variant of the One Direction ores: the darkest dirt/grass shades dropped --- for a snowier look (richness standardized to the vanilla curves). -local b = require 'map_gen.shared.builders' -local Factory = require 'map_gen.maps.danger_ores.config.main_ores_factory' - -return Factory.main_ores { - ores = { - 'copper-ore', - {name = 'coal', tiles = {'dirt-1', 'dirt-2', 'dirt-3', 'dirt-5', 'dirt-6', 'dirt-7'}}, - {name = 'iron-ore', tiles = {'grass-2', 'grass-3', 'grass-4'}} - }, - start = b.euclidean_value(0, 0.35), - value = b.exponential_value(0, 0.07, 1.45) -} diff --git a/map_gen/maps/danger_ores/config/ore_configs.test.lua b/map_gen/maps/danger_ores/config/ore_configs.test.lua index d1d8f0399..acb007c6c 100644 --- a/map_gen/maps/danger_ores/config/ore_configs.test.lua +++ b/map_gen/maps/danger_ores/config/ore_configs.test.lua @@ -40,9 +40,8 @@ end -- common ore_builder consumes; VARIANT configs (gradient's shape+weight-function entries, -- joker's special starter area) only need to load and have named entries. local STRICT_CONFIGS = { - 'vanilla_ores', '3way_ores', 'x_cross_ores', 'vanilla_ores_one_direction', - 'vanilla_ores_gridlocked', 'vanilla_ores_landfill', - 'coal', 'one_direction_ores_xmas', 'poor_mans_coal_fields_ores', 'scrap', + 'vanilla_ores', 'vanilla_ores_gridlocked', 'vanilla_ores_landfill', + 'coal', 'poor_mans_coal_fields_ores', 'scrap', 'vanilla_ores_stone', 'joker_outer_area', } local VARIANT_CONFIGS = { 'vanilla_gradient_ores', 'joker_starter_area' } @@ -81,12 +80,6 @@ for _, config_name in ipairs(VARIANT_CONFIGS) do end -- 2) derived configs share their source's tables (one source of truth for the numbers) -local three_way = load_config('3way_ores') -local x_cross = load_config('x_cross_ores') -check(#three_way == 3, '3way_ores has 3 sectors') -check(rawequal(three_way[1], x_cross[1]) and rawequal(three_way[2], x_cross[2]) - and rawequal(three_way[3], x_cross[3]), '3way_ores entries ARE the x_cross_ores entries') - local with_stone = load_config('vanilla_ores_stone') local vanilla = load_config('vanilla_ores') local vanilla_by_name = {} @@ -140,9 +133,6 @@ check(ratio_weights(vanilla_by_name['iron-ore']) == '75/13/7/5' and ratio_weights(vanilla_by_name['copper-ore']) == '15/70/10/5' and ratio_weights(vanilla_by_name['coal']) == '18/9/8/65', 'vanilla_ores ratio weights unchanged (75/13/7/5, 15/70/10/5, 18/9/8/65)') -check(ratio_weights(x_cross[1]) == '15/70/10/5' and ratio_weights(x_cross[2]) == '18/9/8/65' - and ratio_weights(x_cross[3]) == '75/13/7/5' and ratio_weights(x_cross[4]) == '25/10/60/5', - 'x_cross_ores uses the canonical mixes (coal-rich folded into coal)') check(ratio_weights(with_stone[4]) == '25/10/60/5', 'stone entry ratio weights unchanged') if failures == 0 then diff --git a/map_gen/maps/danger_ores/config/vanilla_ores_one_direction.lua b/map_gen/maps/danger_ores/config/vanilla_ores_one_direction.lua deleted file mode 100644 index c1d4f05f5..000000000 --- a/map_gen/maps/danger_ores/config/vanilla_ores_one_direction.lua +++ /dev/null @@ -1,9 +0,0 @@ --- vanilla_ores for the One Direction maps (richness standardized to the vanilla curve). -local b = require 'map_gen.shared.builders' -local Factory = require 'map_gen.maps.danger_ores.config.main_ores_factory' - -return Factory.main_ores { - ores = {'copper-ore', 'coal', 'iron-ore'}, - start = b.euclidean_value(0, 0.35), - value = b.exponential_value(0, 0.07, 1.45) -} diff --git a/map_gen/maps/danger_ores/config/x_cross_ores.lua b/map_gen/maps/danger_ores/config/x_cross_ores.lua deleted file mode 100644 index a10f6c676..000000000 --- a/map_gen/maps/danger_ores/config/x_cross_ores.lua +++ /dev/null @@ -1,9 +0,0 @@ --- The 4-ore X-Cross config: canonical mixes and curves. 3way_ores derives from this. -local b = require 'map_gen.shared.builders' -local Factory = require 'map_gen.maps.danger_ores.config.main_ores_factory' - -return Factory.main_ores { - ores = {'copper-ore', 'coal', 'iron-ore', 'stone'}, - start = b.euclidean_value(0, 0.35), - value = b.exponential_value(0, 0.07, 1.45) -} diff --git a/map_gen/maps/danger_ores/presets/danger_ore_3way.lua b/map_gen/maps/danger_ores/presets/danger_ore_3way.lua index 55598f056..2621c3d91 100644 --- a/map_gen/maps/danger_ores/presets/danger_ore_3way.lua +++ b/map_gen/maps/danger_ores/presets/danger_ore_3way.lua @@ -11,7 +11,6 @@ ScenarioInfo.add_map_extra_info([[ DOC.scenario_name = 'danger-ore-3way' DOC.map_config.main_ores_builder = require 'map_gen.maps.danger_ores.modules.main_ores_3way' -DOC.map_config.main_ores = require 'map_gen.maps.danger_ores.config.3way_ores' DOC.map_config.main_ores_rotate = nil DOC.map_config.spawn_shape = B.rectangle(72) DOC.map_config.start_ore_shape = B.rotate(B.rectangle(88), math.rad(135)) diff --git a/map_gen/maps/danger_ores/presets/danger_ore_one_direction.lua b/map_gen/maps/danger_ores/presets/danger_ore_one_direction.lua index 6177b6536..29843cf38 100644 --- a/map_gen/maps/danger_ores/presets/danger_ore_one_direction.lua +++ b/map_gen/maps/danger_ores/presets/danger_ore_one_direction.lua @@ -12,7 +12,6 @@ ScenarioInfo.add_map_extra_info([[ DOC.scenario_name = 'danger-ore-one-direction' DOC.rocket_launched.win_satellite_count = 250 DOC.map_config.main_ores_builder = require 'map_gen.maps.danger_ores.modules.main_ores_one_direction' -DOC.map_config.main_ores = require 'map_gen.maps.danger_ores.config.vanilla_ores_one_direction' DOC.map_config.main_ores_rotate = nil DOC.map_config.no_resource_patch_shape = B.rectangle(160) DOC.map_config.spawn_shape = B.rectangle(72) diff --git a/map_gen/maps/danger_ores/presets/danger_ore_one_direction_wide.lua b/map_gen/maps/danger_ores/presets/danger_ore_one_direction_wide.lua index ed6ae935f..a17e55640 100644 --- a/map_gen/maps/danger_ores/presets/danger_ore_one_direction_wide.lua +++ b/map_gen/maps/danger_ores/presets/danger_ore_one_direction_wide.lua @@ -11,7 +11,6 @@ ScenarioInfo.add_map_extra_info([[ DOC.scenario_name = 'danger-ore-one-direction-wide' DOC.map_config.main_ores_builder = require 'map_gen.maps.danger_ores.modules.main_ores_one_direction' -DOC.map_config.main_ores = require 'map_gen.maps.danger_ores.config.vanilla_ores_one_direction' DOC.map_config.main_ores_rotate = nil DOC.map_config.no_resource_patch_shape = B.rectangle(160) DOC.map_config.ore_width = 96 diff --git a/map_gen/maps/danger_ores/presets/danger_ore_square.lua b/map_gen/maps/danger_ores/presets/danger_ore_square.lua index 5e5ebd268..aed006632 100644 --- a/map_gen/maps/danger_ores/presets/danger_ore_square.lua +++ b/map_gen/maps/danger_ores/presets/danger_ore_square.lua @@ -13,7 +13,6 @@ local start_ore_offset = 44 DOC.scenario_name = 'danger-ore-square' DOC.map_config.main_ores_builder = require 'map_gen.maps.danger_ores.modules.main_ores_square' -DOC.map_config.main_ores = require 'map_gen.maps.danger_ores.config.3way_ores' DOC.map_config.main_ores_rotate = nil DOC.map_config.main_ores_shuffle_order = true DOC.map_config.main_ores_start_ore_offset = start_ore_offset diff --git a/map_gen/maps/danger_ores/presets/danger_ore_x_cross.lua b/map_gen/maps/danger_ores/presets/danger_ore_x_cross.lua index ce293499b..7148b3a35 100644 --- a/map_gen/maps/danger_ores/presets/danger_ore_x_cross.lua +++ b/map_gen/maps/danger_ores/presets/danger_ore_x_cross.lua @@ -14,7 +14,7 @@ local cross = B.rotate(B.any{ h_bound, v_bound }, math.rad(135)) DOC.scenario_name = 'danger-ore-x-cross' DOC.map_config.main_ores_builder = require 'map_gen.maps.danger_ores.modules.main_ores_x_cross' -DOC.map_config.main_ores = require 'map_gen.maps.danger_ores.config.x_cross_ores' +DOC.map_config.main_ores = require 'map_gen.maps.danger_ores.config.vanilla_ores_stone' DOC.map_config.main_ores_rotate = 135 DOC.map_config.spawn_shape = B.rectangle(100) DOC.map_config.start_ore_shape = B.rotate(B.rectangle(116), math.rad(135)) diff --git a/map_gen/maps/danger_ores/presets/danger_ore_xmas_tree.lua b/map_gen/maps/danger_ores/presets/danger_ore_xmas_tree.lua index 381a5dce5..f65c5547d 100644 --- a/map_gen/maps/danger_ores/presets/danger_ore_xmas_tree.lua +++ b/map_gen/maps/danger_ores/presets/danger_ore_xmas_tree.lua @@ -1,3 +1,4 @@ +local B = require 'map_gen.shared.builders' local DOC = require 'map_gen.maps.danger_ores.configuration' local Config = require 'config' local Scenario = require 'map_gen.maps.danger_ores.scenario' @@ -15,7 +16,17 @@ Config.day_night.fixed_brightness = 0.70 DOC.scenario_name = 'danger-ore-xmas-tree' DOC.map_config.main_ores_builder = require 'map_gen.maps.danger_ores.modules.main_ores_xmas_tree' -DOC.map_config.main_ores = require 'map_gen.maps.danger_ores.config.one_direction_ores_xmas' +-- canonical ores with the darkest dirt/grass shades dropped for the snow look +local Factory = require 'map_gen.maps.danger_ores.config.main_ores_factory' +DOC.map_config.main_ores = Factory.main_ores { + ores = { + 'copper-ore', + {name = 'coal', tiles = {'dirt-1', 'dirt-2', 'dirt-3', 'dirt-5', 'dirt-6', 'dirt-7'}}, + {name = 'iron-ore', tiles = {'grass-2', 'grass-3', 'grass-4'}} + }, + start = B.euclidean_value(0, 0.35), + value = B.exponential_value(0, 0.07, 1.45) +} DOC.map_config.main_ores_rotate = nil DOC.map_config.water = nil DOC.game.always_day = false From 76b2bee6ebfdedae5dce1a0744334219a0b7533f Mon Sep 17 00:00:00 2001 From: lex <5781244+lex@users.noreply.github.com> Date: Thu, 9 Jul 2026 12:52:01 +0300 Subject: [PATCH 22/31] Danger Ores: joker starter coal joins the standard richness budget The coal sector sat on a 4h-of-one-drill budget against everyone else's 24h. One budget for all four ores; the uranium extra stays. Behavior change: Joker starter coal patches are ~6x richer. Co-Authored-By: Claude Fable 5 --- .../maps/danger_ores/config/joker_starter_area.lua | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/map_gen/maps/danger_ores/config/joker_starter_area.lua b/map_gen/maps/danger_ores/config/joker_starter_area.lua index a95d96a88..e9099bb7a 100644 --- a/map_gen/maps/danger_ores/config/joker_starter_area.lua +++ b/map_gen/maps/danger_ores/config/joker_starter_area.lua @@ -1,23 +1,16 @@ -- The Joker starter area: canonical mixes with time-budgeted richness -- each patch --- holds roughly this many hours of a single electric mining drill's output, with coal --- on the small budget. Plus a token super-rich uranium entry. +-- holds roughly a day of a single electric mining drill's output. Plus a token +-- super-rich uranium entry. local b = require 'map_gen.shared.builders' local Factory = require 'map_gen.maps.danger_ores.config.main_ores_factory' local mine_speed = 0.5 / 10 -- 0.5 ore/s per electric mining drill -local low = 60 * 60 * 4 * mine_speed -- 4h local medium = 60 * 60 * 24 * mine_speed -- 24h -local low_value = b.exponential_value(low, 0.07, 1.45) local medium_value = b.exponential_value(medium, 0.07, 1.45) local config = Factory.main_ores { - ores = { - 'copper-ore', - {name = 'coal', value = low_value}, - 'iron-ore', - 'stone' - }, + ores = {'copper-ore', 'coal', 'iron-ore', 'stone'}, start = 1, value = medium_value } From d7513712bee8078f878877cbb7d49a8f27c42284 Mon Sep 17 00:00:00 2001 From: lex <5781244+lex@users.noreply.github.com> Date: Thu, 9 Jul 2026 13:22:56 +0300 Subject: [PATCH 23/31] Danger Ores: fluent API for the main-ores factory Factory.default(), Factory.blank(), :add_ore(name, opts), :change_tiles (all or one sector), :richness / :start_value (functions, or shorthand {base, mult, pow} / {base, mult} curves), :sector_weights. The returned object IS the main_ores array -- assign it to map_config.main_ores directly -- with methods on its metatable; every call rebuilds the array in place from a declarative spec, so method order never matters and the object is always a complete, valid config. Unknown (modded) ores default to a pure self-mix and no tiles; canonical ores bring their mix and tile set. Every fluent config is built from fresh tables. The low-level entry/main_ores builders stay for the special cases (poor_mans' resource closures) and now accept explicit mix tables and tiles = false for tile-less entries. Co-Authored-By: Claude Fable 5 --- .../danger_ores/config/main_ores_factory.lua | 157 +++++++++++++++++- .../danger_ores/config/ore_configs.test.lua | 55 +++++- 2 files changed, 198 insertions(+), 14 deletions(-) diff --git a/map_gen/maps/danger_ores/config/main_ores_factory.lua b/map_gen/maps/danger_ores/config/main_ores_factory.lua index d8fbf4456..de0dbffbc 100644 --- a/map_gen/maps/danger_ores/config/main_ores_factory.lua +++ b/map_gen/maps/danger_ores/config/main_ores_factory.lua @@ -2,6 +2,16 @@ -- per-ore floor tiles and the same dominant+minor resource mixes, differing only in -- richness curves, ore order, entry weights or tile skins. This module holds those -- canonical numbers once; each config composes its entries from here. +-- +-- Two levels of API: +-- * the low-level table builders (entry / main_ores) for configs with special needs +-- * a fluent API for everything else: +-- Factory.default() -- canonical copper/coal/iron +-- Factory.default():add_ore('stone') -- plus the stone sector +-- Factory.default():change_tiles('landfill') -- reskin every sector +-- Factory.blank():add_ore('omnite') -- modded worlds from scratch +-- The returned object IS the main_ores array (assign it to map_config.main_ores +-- directly); every method mutates it in place and returns it for chaining. local b = require 'map_gen.shared.builders' local Public = {} @@ -23,6 +33,10 @@ local mixes = { } Public.mixes = mixes +-- The canonical richness curves. +local default_start = b.euclidean_value(0, 0.35) +local default_value = b.exponential_value(0, 0.07, 1.45) + local function copy_array(array) local copy = {} for i, v in ipairs(array) do @@ -32,15 +46,22 @@ local function copy_array(array) end -- Fresh copy per call: the runtime must never share mutable tables between entries. +-- Unknown (modded) ores have no canonical tile set and return nil. function Public.tiles(ore_name) - return copy_array(tile_sets[ore_name]) + local tiles = tile_sets[ore_name] + return tiles and copy_array(tiles) end --- Build a ratios table from a mix. make_resource(resource_name) -> resource shape. -function Public.ratios(mix_name, make_resource) +-- Build a ratios table from a mix: a canonical mix name, an explicit array of +-- {resource name, weight} pairs, or an unknown ore name (pure self-mix). +-- make_resource(resource_name) -> resource shape. +function Public.ratios(mix, make_resource) + if type(mix) == 'string' then + mix = mixes[mix] or {{mix, 1}} + end local ratios = {} - for i, mix in ipairs(mixes[mix_name]) do - ratios[i] = {resource = make_resource(mix[1]), weight = mix[2]} + for i, pair in ipairs(mix) do + ratios[i] = {resource = make_resource(pair[1]), weight = pair[2]} end return ratios end @@ -53,8 +74,8 @@ function Public.full_shape_resource(value) end -- One main-ore entry. params: name (required), start (required), and either value or --- make_resource; mix (defaults to name), tiles (defaults to the ore's tile set) and --- weight (defaults to 1) optional. +-- make_resource; mix (defaults to name), tiles (defaults to the ore's tile set; pass +-- false for a tile-less entry) and weight (defaults to 1) optional. function Public.entry(params) local name = params.name local make_resource = params.make_resource @@ -62,9 +83,15 @@ function Public.entry(params) assert(params.value, 'main_ores_factory.entry: either value or make_resource is required') make_resource = Public.full_shape_resource(params.value) end + local tiles + if params.tiles == nil then + tiles = Public.tiles(name) + elseif params.tiles then + tiles = params.tiles + end return { name = name, - tiles = params.tiles or Public.tiles(name), + tiles = tiles, start = params.start, weight = params.weight or 1, ratios = Public.ratios(params.mix or name, make_resource) @@ -92,4 +119,118 @@ function Public.main_ores(params) return config end +-- == Fluent API ============================================================= + +local Fluent = {} + +-- {base, mult} shorthand becomes a euclidean curve; functions and plain numbers +-- (flat richness) pass through. +local function normalize_start(start) + if type(start) == 'table' then + return b.euclidean_value(start[1], start[2]) + end + return start +end + +-- {base, mult, pow} shorthand becomes an exponential curve; functions pass through. +local function normalize_value(value) + if type(value) == 'table' then + return b.exponential_value(value[1], value[2], value[3]) + end + return value +end + +-- Rebuild the array part from the spec. Called after every fluent method, so the +-- object is always a complete, valid main_ores config and method order never matters. +local function rebuild(self) + local spec = getmetatable(self).spec + for i = #self, 1, -1 do + self[i] = nil + end + for index, ore in ipairs(spec.ores) do + local tiles = ore.tiles + if type(tiles) == 'table' then + tiles = copy_array(tiles) + end + self[index] = Public.entry { + name = ore.name, + mix = ore.mix, + tiles = tiles, + start = ore.start ~= nil and ore.start or spec.start, + value = ore.value or spec.value, + weight = ore.weight, + make_resource = ore.make_resource + } + end + return self +end + +local function fluent(ores) + local spec = {start = default_start, value = default_value, ores = ores} + return rebuild(setmetatable({}, {__index = Fluent, spec = spec})) +end + +--- The canonical vanilla config: copper/coal/iron sectors, vanilla curves. +function Public.default() + return fluent {{name = 'copper-ore'}, {name = 'coal'}, {name = 'iron-ore'}} +end + +--- No sectors at all; add them with add_ore. Curves default to the vanilla pair. +function Public.blank() + return fluent {} +end + +--- Append a sector. Canonical ores bring their mix and tiles; unknown (modded) ores +-- default to a pure self-mix and no tiles. opts (all optional): mix (name or +-- {resource, weight} pairs), tiles (array, or false for none), start, value, weight, +-- make_resource. +function Fluent:add_ore(name, opts) + local ore = {name = name} + for key, option in pairs(opts or {}) do + ore[key] = option + end + local spec = getmetatable(self).spec + spec.ores[#spec.ores + 1] = ore + return rebuild(self) +end + +--- Reskin every sector, or just ore_name's. tiles: tile name or array of tile names. +function Fluent:change_tiles(tiles, ore_name) + if type(tiles) == 'string' then + tiles = {tiles} + end + local spec = getmetatable(self).spec + for _, ore in ipairs(spec.ores) do + if ore_name == nil or ore.name == ore_name then + ore.tiles = tiles + end + end + return rebuild(self) +end + +--- Set the richness curve for every sector without its own: a function, or +-- {base, mult, pow} for an exponential curve. +function Fluent:richness(value) + getmetatable(self).spec.value = normalize_value(value) + return rebuild(self) +end + +--- Set the start (guaranteed spawn richness) curve for every sector without its own: +-- a function, a plain number, or {base, mult} for a euclidean curve. +function Fluent:start_value(start) + getmetatable(self).spec.start = normalize_start(start) + return rebuild(self) +end + +--- Set per-sector selection weights from a map of ore name -> weight. +function Fluent:sector_weights(weights) + local spec = getmetatable(self).spec + for _, ore in ipairs(spec.ores) do + if weights[ore.name] then + ore.weight = weights[ore.name] + end + end + return rebuild(self) +end + return Public diff --git a/map_gen/maps/danger_ores/config/ore_configs.test.lua b/map_gen/maps/danger_ores/config/ore_configs.test.lua index acb007c6c..a5cac0579 100644 --- a/map_gen/maps/danger_ores/config/ore_configs.test.lua +++ b/map_gen/maps/danger_ores/config/ore_configs.test.lua @@ -79,19 +79,27 @@ for _, config_name in ipairs(VARIANT_CONFIGS) do end end --- 2) derived configs share their source's tables (one source of truth for the numbers) +-- 2) derived configs carry the same canonical numbers as their source local with_stone = load_config('vanilla_ores_stone') local vanilla = load_config('vanilla_ores') local vanilla_by_name = {} for _, ore in ipairs(vanilla) do vanilla_by_name[ore.name] = ore end +check(#vanilla == 3, 'vanilla_ores has 3 sectors') check(#with_stone == 4, 'vanilla_ores_stone has 4 ores') -check(rawequal(with_stone[1], vanilla_by_name['iron-ore']) - and rawequal(with_stone[2], vanilla_by_name['copper-ore']) - and rawequal(with_stone[3], vanilla_by_name['coal']), - 'vanilla_ores_stone entries 1-3 ARE the vanilla_ores entries') +local same_sectors = true +for i = 1, 3 do + local a, v = with_stone[i], vanilla[i] + same_sectors = same_sectors and a.name == v.name and #a.ratios == #v.ratios + for r = 1, #a.ratios do + same_sectors = same_sectors and a.ratios[r].weight == v.ratios[r].weight + and a.ratios[r].resource.name == v.ratios[r].resource.name + end +end +check(same_sectors, 'vanilla_ores_stone sectors 1-3 match vanilla_ores') check(with_stone[4].name == 'stone', 'vanilla_ores_stone entry 4 is stone') +check(not rawequal(load_config('vanilla_ores'), nil) and true, 'vanilla_ores loads') -- 3) the factory: canonical numbers, and fresh tables per call so configs can never -- share mutable state at runtime @@ -121,7 +129,42 @@ check(entry_a.tiles[1] == 'grass-1' and #entry_a.tiles == 4 and entry_a.weight = local ok_no_value = pcall(Factory.entry, {name = 'iron-ore', start = 1}) check(not ok_no_value, 'factory entry rejects a missing value/make_resource') --- 4) golden numbers for the source configs, so old maps cannot drift by accident +-- 4) the fluent API +local fl = Factory.default() +check(#fl == 3 and fl[1].name == 'copper-ore' and fl[2].name == 'coal' and fl[3].name == 'iron-ore', + 'Factory.default() is the canonical 3-sector config') +check(rawequal(fl:add_ore('stone'), fl) and #fl == 4 and fl[4].name == 'stone' + and fl[4].tiles[1] == 'sand-1', + 'add_ore chains on the same object and appends the canonical stone sector') + +local landfill_fl = Factory.default():change_tiles('landfill') +local all_landfill = true +for _, entry in ipairs(landfill_fl) do + all_landfill = all_landfill and #entry.tiles == 1 and entry.tiles[1] == 'landfill' +end +check(all_landfill, 'change_tiles reskins every sector') + +local snow = Factory.default():change_tiles({'grass-2', 'grass-3', 'grass-4'}, 'iron-ore') +check(snow[3].tiles[1] == 'grass-2' and #snow[3].tiles == 3 and snow[1].tiles[1] == 'red-desert-0', + 'change_tiles with an ore name reskins only that sector') + +local rich = Factory.default():richness{50, 0.003, 2.25}:start_value{50, 0.75} +check(rich[1].ratios[1].resource.value == 'exp(50,0.003,2.25)' and rich[1].start == 'euclid(50,0.75)', + 'richness/start_value shorthands rebuild every ratio') + +local weighted = Factory.default():sector_weights{coal = 8} +check(weighted[2].weight == 8 and weighted[1].weight == 1, 'sector_weights sets weights by name') + +local modded = Factory.blank():add_ore('omnite') +check(#modded == 1 and modded[1].tiles == nil and #modded[1].ratios == 1 + and modded[1].ratios[1].resource.name == 'omnite' and modded[1].ratios[1].weight == 1, + 'blank():add_ore gives unknown ores a pure self-mix and no tiles') + +local d1, d2 = Factory.default(), Factory.default() +check(not rawequal(d1, d2) and not rawequal(d1[1], d2[1]) and not rawequal(d1[1].ratios, d2[1].ratios), + 'every fluent config is built from fresh tables') + +-- 5) golden numbers for the source configs, so old maps cannot drift by accident local function ratio_weights(entry) local weights = {} for _, ratio in ipairs(entry.ratios) do From 69bbe6c4f36dfecde8557760c81d7da1f6f2ae0a Mon Sep 17 00:00:00 2001 From: lex <5781244+lex@users.noreply.github.com> Date: Thu, 9 Jul 2026 13:22:56 +0300 Subject: [PATCH 24/31] Danger Ores: express the standard configs through the fluent API vanilla_ores = Factory.default() vanilla_ores_stone = Factory.default():add_ore('stone') vanilla_ores_landfill = Factory.default():change_tiles('landfill') vanilla_ores_gridlocked = default + stone + landfill + offset curves + sector weights joker_starter_area = default + stone + uranium extra + 24h budget xmas preset = default + snow tile trims inline Verified with normalized config dumps: identical to before except entry order within vanilla_ores_stone and gridlocked (weights follow their names), which main_ores_shuffle_order randomizes at map build anyway. Co-Authored-By: Claude Fable 5 --- .../danger_ores/config/joker_starter_area.lua | 28 ++++--------------- .../maps/danger_ores/config/vanilla_ores.lua | 10 ++----- .../config/vanilla_ores_gridlocked.lua | 23 ++++++--------- .../config/vanilla_ores_landfill.lua | 8 +----- .../danger_ores/config/vanilla_ores_stone.lua | 27 +++--------------- .../presets/danger_ore_xmas_tree.lua | 13 ++------- 6 files changed, 25 insertions(+), 84 deletions(-) diff --git a/map_gen/maps/danger_ores/config/joker_starter_area.lua b/map_gen/maps/danger_ores/config/joker_starter_area.lua index e9099bb7a..748811fd4 100644 --- a/map_gen/maps/danger_ores/config/joker_starter_area.lua +++ b/map_gen/maps/danger_ores/config/joker_starter_area.lua @@ -1,4 +1,4 @@ --- The Joker starter area: canonical mixes with time-budgeted richness -- each patch +-- The Joker starter area: canonical sectors with time-budgeted richness -- each patch -- holds roughly a day of a single electric mining drill's output. Plus a token -- super-rich uranium entry. local b = require 'map_gen.shared.builders' @@ -7,24 +7,8 @@ local Factory = require 'map_gen.maps.danger_ores.config.main_ores_factory' local mine_speed = 0.5 / 10 -- 0.5 ore/s per electric mining drill local medium = 60 * 60 * 24 * mine_speed -- 24h -local medium_value = b.exponential_value(medium, 0.07, 1.45) - -local config = Factory.main_ores { - ores = {'copper-ore', 'coal', 'iron-ore', 'stone'}, - start = 1, - value = medium_value -} - -config[#config + 1] = { - name = 'uranium-ore', - start = 1, - weight = 1, - ratios = { - { - resource = b.resource(b.full_shape, 'uranium-ore', function() return 1e6 end), - weight = 1 - } - } -} - -return config +return Factory.default() + :add_ore('stone') + :add_ore('uranium-ore', {tiles = false, value = function() return 1e6 end}) + :start_value(1) + :richness(b.exponential_value(medium, 0.07, 1.45)) diff --git a/map_gen/maps/danger_ores/config/vanilla_ores.lua b/map_gen/maps/danger_ores/config/vanilla_ores.lua index fbdbf07dc..2ab4578c8 100644 --- a/map_gen/maps/danger_ores/config/vanilla_ores.lua +++ b/map_gen/maps/danger_ores/config/vanilla_ores.lua @@ -1,10 +1,4 @@ --- The standard 3-ore main-ores config: canonical tiles and mixes from the factory, --- with the classic richness curves. Order is significant: it maps to ore indices. -local b = require 'map_gen.shared.builders' +-- The standard 3-ore main-ores config: canonical sectors, mixes, tiles and curves. local Factory = require 'map_gen.maps.danger_ores.config.main_ores_factory' -return Factory.main_ores { - ores = {'copper-ore', 'coal', 'iron-ore'}, - start = b.euclidean_value(0, 0.35), - value = b.exponential_value(0, 0.07, 1.45) -} +return Factory.default() diff --git a/map_gen/maps/danger_ores/config/vanilla_ores_gridlocked.lua b/map_gen/maps/danger_ores/config/vanilla_ores_gridlocked.lua index b4118a352..c7dd0c91f 100644 --- a/map_gen/maps/danger_ores/config/vanilla_ores_gridlocked.lua +++ b/map_gen/maps/danger_ores/config/vanilla_ores_gridlocked.lua @@ -1,16 +1,11 @@ --- The Gridlocked config: canonical mixes on landfill tiles, with a rich guaranteed --- floor (both curves offset by 50) and weighted ore order (iron-heavy, stone-light). -local b = require 'map_gen.shared.builders' +-- The Gridlocked config: canonical sectors on landfill tiles, with a rich guaranteed +-- floor (both curves offset by 50) and weighted sectors (iron-heavy, stone-light) -- +-- expansion is very limited there, so richness is buffed to prevent softlocks. local Factory = require 'map_gen.maps.danger_ores.config.main_ores_factory' -return Factory.main_ores { - ores = { - {name = 'iron-ore', weight = 10}, - {name = 'coal', weight = 8}, - {name = 'copper-ore', weight = 7}, - {name = 'stone', weight = 2} - }, - start = b.euclidean_value(50, 0.75), - value = b.exponential_value(50, 0.003, 2.25), - tiles = {'landfill'} -} +return Factory.default() + :add_ore('stone') + :change_tiles('landfill') + :start_value{50, 0.75} + :richness{50, 0.003, 2.25} + :sector_weights{['iron-ore'] = 10, coal = 8, ['copper-ore'] = 7, stone = 2} diff --git a/map_gen/maps/danger_ores/config/vanilla_ores_landfill.lua b/map_gen/maps/danger_ores/config/vanilla_ores_landfill.lua index 9bf512488..863217b76 100644 --- a/map_gen/maps/danger_ores/config/vanilla_ores_landfill.lua +++ b/map_gen/maps/danger_ores/config/vanilla_ores_landfill.lua @@ -1,11 +1,5 @@ -- vanilla_ores skinned entirely in landfill tiles. Used by maps where the whole ore -- field doubles as buildable ground. -local b = require 'map_gen.shared.builders' local Factory = require 'map_gen.maps.danger_ores.config.main_ores_factory' -return Factory.main_ores { - ores = {'copper-ore', 'coal', 'iron-ore'}, - start = b.euclidean_value(0, 0.35), - value = b.exponential_value(0, 0.07, 1.45), - tiles = {'landfill'} -} +return Factory.default():change_tiles('landfill') diff --git a/map_gen/maps/danger_ores/config/vanilla_ores_stone.lua b/map_gen/maps/danger_ores/config/vanilla_ores_stone.lua index 985d6e2b6..80c6d2410 100644 --- a/map_gen/maps/danger_ores/config/vanilla_ores_stone.lua +++ b/map_gen/maps/danger_ores/config/vanilla_ores_stone.lua @@ -1,25 +1,6 @@ --- Vanilla ores plus stone as a fourth main ore: the three vanilla entries come straight --- from config/vanilla_ores.lua (one source of truth for the numbers), with a factory --- stone entry using the same distance curves. Order is significant: it maps to ore --- indices 1..4. Used by maps whose layouts need four main ores, e.g. the clean-border --- partition maps (Tetrominoes, Voronoi), whose region colouring requires at least --- 4 colours. -local b = require 'map_gen.shared.builders' +-- Vanilla ores plus stone as a fourth main sector. Used by maps whose layouts need +-- four main ores, e.g. the clean-border partition maps (Tetrominoes, Voronoi), whose +-- region colouring requires at least 4 colours. local Factory = require 'map_gen.maps.danger_ores.config.main_ores_factory' -local vanilla_ores = require 'map_gen.maps.danger_ores.config.vanilla_ores' -local by_name = {} -for _, ore in ipairs(vanilla_ores) do - by_name[ore.name] = ore -end - -return { - by_name['iron-ore'], - by_name['copper-ore'], - by_name['coal'], - Factory.entry { - name = 'stone', - start = b.euclidean_value(0, 0.35), - value = b.exponential_value(0, 0.07, 1.45) - } -} +return Factory.default():add_ore('stone') diff --git a/map_gen/maps/danger_ores/presets/danger_ore_xmas_tree.lua b/map_gen/maps/danger_ores/presets/danger_ore_xmas_tree.lua index f65c5547d..a31f2613c 100644 --- a/map_gen/maps/danger_ores/presets/danger_ore_xmas_tree.lua +++ b/map_gen/maps/danger_ores/presets/danger_ore_xmas_tree.lua @@ -1,4 +1,3 @@ -local B = require 'map_gen.shared.builders' local DOC = require 'map_gen.maps.danger_ores.configuration' local Config = require 'config' local Scenario = require 'map_gen.maps.danger_ores.scenario' @@ -18,15 +17,9 @@ DOC.scenario_name = 'danger-ore-xmas-tree' DOC.map_config.main_ores_builder = require 'map_gen.maps.danger_ores.modules.main_ores_xmas_tree' -- canonical ores with the darkest dirt/grass shades dropped for the snow look local Factory = require 'map_gen.maps.danger_ores.config.main_ores_factory' -DOC.map_config.main_ores = Factory.main_ores { - ores = { - 'copper-ore', - {name = 'coal', tiles = {'dirt-1', 'dirt-2', 'dirt-3', 'dirt-5', 'dirt-6', 'dirt-7'}}, - {name = 'iron-ore', tiles = {'grass-2', 'grass-3', 'grass-4'}} - }, - start = B.euclidean_value(0, 0.35), - value = B.exponential_value(0, 0.07, 1.45) -} +DOC.map_config.main_ores = Factory.default() + :change_tiles({'dirt-1', 'dirt-2', 'dirt-3', 'dirt-5', 'dirt-6', 'dirt-7'}, 'coal') + :change_tiles({'grass-2', 'grass-3', 'grass-4'}, 'iron-ore') DOC.map_config.main_ores_rotate = nil DOC.map_config.water = nil DOC.game.always_day = false From ac71c953543c3f7d9c58c58c7ad229ac94682717 Mon Sep 17 00:00:00 2001 From: lex <5781244+lex@users.noreply.github.com> Date: Thu, 9 Jul 2026 13:53:42 +0300 Subject: [PATCH 25/31] Danger Ores: complete the fluent API, use utils.table's deep_copy Per review: the fluent builder now covers the full add/remove/update combination -- :remove_ore, :update_ore(name, opts), :add_tile and :remove_tile (all sectors or one), and :richness / :start_value accept an optional sector name to override one ore instead of the default. Also replaces the hand-rolled copy_array with utils.table's deep_copy; the standalone config tests stub Factorio's built-in util module that utils.table pulls in. New tests cover every added method. Co-Authored-By: Claude Fable 5 --- .../danger_ores/config/main_ores_factory.lua | 115 +++++++++++++++--- .../danger_ores/config/ore_configs.test.lua | 45 +++++++ 2 files changed, 141 insertions(+), 19 deletions(-) diff --git a/map_gen/maps/danger_ores/config/main_ores_factory.lua b/map_gen/maps/danger_ores/config/main_ores_factory.lua index de0dbffbc..da9d7b19f 100644 --- a/map_gen/maps/danger_ores/config/main_ores_factory.lua +++ b/map_gen/maps/danger_ores/config/main_ores_factory.lua @@ -13,6 +13,9 @@ -- The returned object IS the main_ores array (assign it to map_config.main_ores -- directly); every method mutates it in place and returns it for chaining. local b = require 'map_gen.shared.builders' +local table = require 'utils.table' + +local deep_copy = table.deep_copy local Public = {} @@ -37,19 +40,11 @@ Public.mixes = mixes local default_start = b.euclidean_value(0, 0.35) local default_value = b.exponential_value(0, 0.07, 1.45) -local function copy_array(array) - local copy = {} - for i, v in ipairs(array) do - copy[i] = v - end - return copy -end - -- Fresh copy per call: the runtime must never share mutable tables between entries. -- Unknown (modded) ores have no canonical tile set and return nil. function Public.tiles(ore_name) local tiles = tile_sets[ore_name] - return tiles and copy_array(tiles) + return tiles and deep_copy(tiles) end -- Build a ratios table from a mix: a canonical mix name, an explicit array of @@ -109,7 +104,7 @@ function Public.main_ores(params) config[i] = Public.entry { name = ore.name, mix = ore.mix, - tiles = ore.tiles or (params.tiles and copy_array(params.tiles)), + tiles = ore.tiles or (params.tiles and deep_copy(params.tiles)), start = ore.start or params.start, value = ore.value or params.value, weight = ore.weight or params.weight, @@ -150,7 +145,7 @@ local function rebuild(self) for index, ore in ipairs(spec.ores) do local tiles = ore.tiles if type(tiles) == 'table' then - tiles = copy_array(tiles) + tiles = deep_copy(tiles) end self[index] = Public.entry { name = ore.name, @@ -194,6 +189,30 @@ function Fluent:add_ore(name, opts) return rebuild(self) end +--- Remove every sector called name. +function Fluent:remove_ore(name) + local spec = getmetatable(self).spec + for i = #spec.ores, 1, -1 do + if spec.ores[i].name == name then + table.remove(spec.ores, i) + end + end + return rebuild(self) +end + +--- Patch a sector's options in place: mix, tiles, start, value, weight, make_resource. +function Fluent:update_ore(name, opts) + local spec = getmetatable(self).spec + for _, ore in ipairs(spec.ores) do + if ore.name == name then + for key, option in pairs(opts) do + ore[key] = option + end + end + end + return rebuild(self) +end + --- Reskin every sector, or just ore_name's. tiles: tile name or array of tile names. function Fluent:change_tiles(tiles, ore_name) if type(tiles) == 'string' then @@ -208,17 +227,75 @@ function Fluent:change_tiles(tiles, ore_name) return rebuild(self) end ---- Set the richness curve for every sector without its own: a function, or --- {base, mult, pow} for an exponential curve. -function Fluent:richness(value) - getmetatable(self).spec.value = normalize_value(value) +-- Materialize a sector's tile list so it can be edited (defaults live in tile_sets). +local function own_tiles(ore) + if ore.tiles == nil then + ore.tiles = Public.tiles(ore.name) + end + return ore.tiles +end + +--- Append a tile to every sector's list, or just ore_name's. +function Fluent:add_tile(tile, ore_name) + local spec = getmetatable(self).spec + for _, ore in ipairs(spec.ores) do + if ore_name == nil or ore.name == ore_name then + local tiles = own_tiles(ore) + if tiles then + tiles[#tiles + 1] = tile + else + ore.tiles = {tile} + end + end + end + return rebuild(self) +end + +--- Remove a tile from every sector's list, or just ore_name's. +function Fluent:remove_tile(tile, ore_name) + local spec = getmetatable(self).spec + for _, ore in ipairs(spec.ores) do + if ore_name == nil or ore.name == ore_name then + local tiles = own_tiles(ore) + if tiles then + table.remove_element(tiles, tile) + end + end + end + return rebuild(self) +end + +--- Set the richness curve -- for one sector, or as the default for every sector +-- without its own: a function, or {base, mult, pow} for an exponential curve. +function Fluent:richness(value, ore_name) + value = normalize_value(value) + local spec = getmetatable(self).spec + if ore_name then + for _, ore in ipairs(spec.ores) do + if ore.name == ore_name then + ore.value = value + end + end + else + spec.value = value + end return rebuild(self) end ---- Set the start (guaranteed spawn richness) curve for every sector without its own: --- a function, a plain number, or {base, mult} for a euclidean curve. -function Fluent:start_value(start) - getmetatable(self).spec.start = normalize_start(start) +--- Set the start (guaranteed spawn richness) curve -- for one sector, or as the +-- default: a function, a plain number, or {base, mult} for a euclidean curve. +function Fluent:start_value(start, ore_name) + start = normalize_start(start) + local spec = getmetatable(self).spec + if ore_name then + for _, ore in ipairs(spec.ores) do + if ore.name == ore_name then + ore.start = start + end + end + else + spec.start = start + end return rebuild(self) end diff --git a/map_gen/maps/danger_ores/config/ore_configs.test.lua b/map_gen/maps/danger_ores/config/ore_configs.test.lua index a5cac0579..48f088132 100644 --- a/map_gen/maps/danger_ores/config/ore_configs.test.lua +++ b/map_gen/maps/danger_ores/config/ore_configs.test.lua @@ -2,6 +2,26 @@ -- Run from the repo root: lua map_gen/maps/danger_ores/config/ore_configs.test.lua package.path = './?.lua;' .. package.path +-- stub Factorio's built-in util module (utils.table pulls table.deepcopy from it) +package.preload['util'] = function() + local function deepcopy(object) + if type(object) ~= 'table' then + return object + end + local copy = {} + for k, v in pairs(object) do + copy[deepcopy(k)] = deepcopy(v) + end + return setmetatable(copy, getmetatable(object)) + end + table.deepcopy = deepcopy -- luacheck: ignore 122 + table.compare = function(a, b) -- luacheck: ignore 122 + return a == b + end + util = {merge = function(tables) return tables[1] end} -- luacheck: globals util + return util +end + -- stub the builders so configs load outside Factorio; value functions become readable tags local fake_builders = { full_shape = 'FULL' } function fake_builders.euclidean_value(base, mult) @@ -160,6 +180,31 @@ check(#modded == 1 and modded[1].tiles == nil and #modded[1].ratios == 1 and modded[1].ratios[1].resource.name == 'omnite' and modded[1].ratios[1].weight == 1, 'blank():add_ore gives unknown ores a pure self-mix and no tiles') +local trimmed = Factory.default():remove_ore('coal') +check(#trimmed == 2 and trimmed[1].name == 'copper-ore' and trimmed[2].name == 'iron-ore', + 'remove_ore drops the named sector') + +local updated = Factory.default():update_ore('coal', {weight = 5, mix = {{'coal', 1}}}) +check(updated[2].weight == 5 and #updated[2].ratios == 1 and updated[2].ratios[1].resource.name == 'coal', + 'update_ore patches a sector in place') + +local no_dark = Factory.default():remove_tile('grass-4') +check(#no_dark[3].tiles == 3 and no_dark[3].tiles[3] == 'grass-3' and #no_dark[1].tiles == 4, + 'remove_tile drops a tile from the sector that has it') + +local extra_tile = Factory.default():add_tile('snow-0', 'coal') +check(extra_tile[2].tiles[8] == 'snow-0' and #extra_tile[1].tiles == 4, + 'add_tile appends only to the named sector') + +local low_coal = Factory.default():richness{0, 0.5, 1.1}:richness({0, 0.9, 1.9}, 'coal') +check(low_coal[2].ratios[1].resource.value == 'exp(0,0.9,1.9)' + and low_coal[1].ratios[1].resource.value == 'exp(0,0.5,1.1)', + 'richness with an ore name overrides one sector, default covers the rest') + +local flat_iron = Factory.default():start_value(7, 'iron-ore') +check(flat_iron[3].start == 7 and flat_iron[1].start ~= 7, + 'start_value with an ore name overrides one sector') + local d1, d2 = Factory.default(), Factory.default() check(not rawequal(d1, d2) and not rawequal(d1[1], d2[1]) and not rawequal(d1[1].ratios, d2[1].ratios), 'every fluent config is built from fresh tables') From 2491ec8c782a966bd468fb8f422cdc774d78b57a Mon Sep 17 00:00:00 2001 From: lex <5781244+lex@users.noreply.github.com> Date: Thu, 9 Jul 2026 13:53:42 +0300 Subject: [PATCH 26/31] Danger Ores: restore the Joker starter's low coal budget The 4h coal budget is deliberate -- cheap-to-clear coal lets players mine through and expand the starter area. Now expressed as a per-sector override: :richness(low_value, 'coal'). Dump-verified identical to the original pre-refactor config. Co-Authored-By: Claude Fable 5 --- map_gen/maps/danger_ores/config/joker_starter_area.lua | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/map_gen/maps/danger_ores/config/joker_starter_area.lua b/map_gen/maps/danger_ores/config/joker_starter_area.lua index 748811fd4..49ccc9033 100644 --- a/map_gen/maps/danger_ores/config/joker_starter_area.lua +++ b/map_gen/maps/danger_ores/config/joker_starter_area.lua @@ -1,10 +1,12 @@ -- The Joker starter area: canonical sectors with time-budgeted richness -- each patch --- holds roughly a day of a single electric mining drill's output. Plus a token --- super-rich uranium entry. +-- holds roughly this many hours of a single electric mining drill's output. Coal sits +-- on the small budget so players can mine through it to expand the starter area more +-- easily. Plus a token super-rich uranium entry. local b = require 'map_gen.shared.builders' local Factory = require 'map_gen.maps.danger_ores.config.main_ores_factory' local mine_speed = 0.5 / 10 -- 0.5 ore/s per electric mining drill +local low = 60 * 60 * 4 * mine_speed -- 4h local medium = 60 * 60 * 24 * mine_speed -- 24h return Factory.default() @@ -12,3 +14,4 @@ return Factory.default() :add_ore('uranium-ore', {tiles = false, value = function() return 1e6 end}) :start_value(1) :richness(b.exponential_value(medium, 0.07, 1.45)) + :richness(b.exponential_value(low, 0.07, 1.45), 'coal') From ea42815356e3077d402ef2c282f994468a2105c2 Mon Sep 17 00:00:00 2001 From: lex <5781244+lex@users.noreply.github.com> Date: Thu, 9 Jul 2026 13:53:42 +0300 Subject: [PATCH 27/31] Danger Ores: xmas tree keeps default tiles minus grass-4 Per review, the old dirt-4/grass-1 trim makes no visible difference; the one tile that interrupts the tree aesthetic is grass-4. Now: Factory.default():remove_tile('grass-4'). Co-Authored-By: Claude Fable 5 --- map_gen/maps/danger_ores/presets/danger_ore_xmas_tree.lua | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/map_gen/maps/danger_ores/presets/danger_ore_xmas_tree.lua b/map_gen/maps/danger_ores/presets/danger_ore_xmas_tree.lua index a31f2613c..3b4c4ec1b 100644 --- a/map_gen/maps/danger_ores/presets/danger_ore_xmas_tree.lua +++ b/map_gen/maps/danger_ores/presets/danger_ore_xmas_tree.lua @@ -15,11 +15,9 @@ Config.day_night.fixed_brightness = 0.70 DOC.scenario_name = 'danger-ore-xmas-tree' DOC.map_config.main_ores_builder = require 'map_gen.maps.danger_ores.modules.main_ores_xmas_tree' --- canonical ores with the darkest dirt/grass shades dropped for the snow look +-- canonical ores minus grass-4, the one dark green that interrupts the tree aesthetic local Factory = require 'map_gen.maps.danger_ores.config.main_ores_factory' -DOC.map_config.main_ores = Factory.default() - :change_tiles({'dirt-1', 'dirt-2', 'dirt-3', 'dirt-5', 'dirt-6', 'dirt-7'}, 'coal') - :change_tiles({'grass-2', 'grass-3', 'grass-4'}, 'iron-ore') +DOC.map_config.main_ores = Factory.default():remove_tile('grass-4') DOC.map_config.main_ores_rotate = nil DOC.map_config.water = nil DOC.game.always_day = false From b5806cfb0a989e82256d057c3260003f6e3f2152 Mon Sep 17 00:00:00 2001 From: lex <5781244+lex@users.noreply.github.com> Date: Thu, 9 Jul 2026 14:03:30 +0300 Subject: [PATCH 28/31] Danger Ores: add :change_mix to the fluent API The mix counterpart of :change_tiles -- rewrite every sector's resource mix, or one sector's, from a canonical mix name or explicit {resource, weight} pairs. Co-Authored-By: Claude Fable 5 --- .../maps/danger_ores/config/main_ores_factory.lua | 12 ++++++++++++ map_gen/maps/danger_ores/config/ore_configs.test.lua | 9 +++++++++ 2 files changed, 21 insertions(+) diff --git a/map_gen/maps/danger_ores/config/main_ores_factory.lua b/map_gen/maps/danger_ores/config/main_ores_factory.lua index da9d7b19f..eef4e4e13 100644 --- a/map_gen/maps/danger_ores/config/main_ores_factory.lua +++ b/map_gen/maps/danger_ores/config/main_ores_factory.lua @@ -213,6 +213,18 @@ function Fluent:update_ore(name, opts) return rebuild(self) end +--- Change the resource mix of every sector, or just ore_name's. mix: a canonical mix +-- name or an array of {resource name, ratio weight} pairs. +function Fluent:change_mix(mix, ore_name) + local spec = getmetatable(self).spec + for _, ore in ipairs(spec.ores) do + if ore_name == nil or ore.name == ore_name then + ore.mix = mix + end + end + return rebuild(self) +end + --- Reskin every sector, or just ore_name's. tiles: tile name or array of tile names. function Fluent:change_tiles(tiles, ore_name) if type(tiles) == 'string' then diff --git a/map_gen/maps/danger_ores/config/ore_configs.test.lua b/map_gen/maps/danger_ores/config/ore_configs.test.lua index 48f088132..13dbc30fc 100644 --- a/map_gen/maps/danger_ores/config/ore_configs.test.lua +++ b/map_gen/maps/danger_ores/config/ore_configs.test.lua @@ -180,6 +180,15 @@ check(#modded == 1 and modded[1].tiles == nil and #modded[1].ratios == 1 and modded[1].ratios[1].resource.name == 'omnite' and modded[1].ratios[1].weight == 1, 'blank():add_ore gives unknown ores a pure self-mix and no tiles') +local all_coal = Factory.default():change_mix{{'coal', 1}} +check(#all_coal[1].ratios == 1 and all_coal[1].ratios[1].resource.name == 'coal' + and #all_coal[3].ratios == 1 and all_coal[3].ratios[1].resource.name == 'coal', + 'change_mix rewrites every sector') + +local iron_pure = Factory.default():change_mix({{'iron-ore', 1}}, 'iron-ore') +check(#iron_pure[3].ratios == 1 and #iron_pure[1].ratios == 4, + 'change_mix with an ore name rewrites only that sector') + local trimmed = Factory.default():remove_ore('coal') check(#trimmed == 2 and trimmed[1].name == 'copper-ore' and trimmed[2].name == 'iron-ore', 'remove_ore drops the named sector') From 912bf061f9166e13b2a4d1b33fb819ec91f2ec8d Mon Sep 17 00:00:00 2001 From: lex <5781244+lex@users.noreply.github.com> Date: Thu, 9 Jul 2026 14:03:30 +0300 Subject: [PATCH 29/31] Danger Ores: express the all-coal config through the fluent API Standard four sectors, every mix rewritten to pure coal, dirt skin, flat guaranteed spawn. Dump-verified identical apart from sector order, which main_ores_shuffle_order randomizes anyway. Co-Authored-By: Claude Fable 5 --- map_gen/maps/danger_ores/config/coal.lua | 53 +++++------------------- 1 file changed, 10 insertions(+), 43 deletions(-) diff --git a/map_gen/maps/danger_ores/config/coal.lua b/map_gen/maps/danger_ores/config/coal.lua index c3cdd9481..41292712d 100644 --- a/map_gen/maps/danger_ores/config/coal.lua +++ b/map_gen/maps/danger_ores/config/coal.lua @@ -1,44 +1,11 @@ -local b = require 'map_gen.shared.builders' -local start_value = b.euclidean_value(50, 0) -local value = b.exponential_value(0, 0.035, 1.45) +-- The all-coal world (Coal Maze, Collapse, Patches): the four standard sectors so the +-- layout matches the vanilla maps', but every sector yields pure coal on dirt, with a +-- flat guaranteed spawn and a gentle richness curve. +local Factory = require 'map_gen.maps.danger_ores.config.main_ores_factory' -local tiles = { - [1] = 'dirt-1', - [2] = 'dirt-2', - [3] = 'dirt-3', - [4] = 'dirt-4', - [5] = 'dirt-5', - [6] = 'dirt-6', - [7] = 'dirt-7' -} - -return { - { - name = 'coal', - ['tiles'] = tiles, - ['start'] = start_value, - ['weight'] = 1, - ['ratios'] = {{resource = b.resource(b.full_shape, 'coal', value), weight = 1}} - }, - { - name = 'copper-ore', - ['tiles'] = tiles, - ['start'] = start_value, - ['weight'] = 1, - ['ratios'] = {{resource = b.resource(b.full_shape, 'coal', value), weight = 1}} - }, - { - name = 'iron-ore', - ['tiles'] = tiles, - ['start'] = start_value, - ['weight'] = 1, - ['ratios'] = {{resource = b.resource(b.full_shape, 'coal', value), weight = 1}} - }, - { - name = 'stone', - ['tiles'] = tiles, - ['start'] = start_value, - ['weight'] = 1, - ['ratios'] = {{resource = b.resource(b.full_shape, 'coal', value), weight = 1}} - } -} +return Factory.default() + :add_ore('stone') + :change_mix{{'coal', 1}} + :change_tiles{'dirt-1', 'dirt-2', 'dirt-3', 'dirt-4', 'dirt-5', 'dirt-6', 'dirt-7'} + :start_value{50, 0} + :richness{0, 0.035, 1.45} From a6b31eafde08de4989120e9981a8ab8ab0989912 Mon Sep 17 00:00:00 2001 From: lex <5781244+lex@users.noreply.github.com> Date: Thu, 9 Jul 2026 14:03:30 +0300 Subject: [PATCH 30/31] Danger Ores: express the scrap config through the fluent API One scrap sector wearing every terrain skin; dump-verified byte-identical. Co-Authored-By: Claude Fable 5 --- map_gen/maps/danger_ores/config/scrap.lua | 47 +++++++---------------- 1 file changed, 14 insertions(+), 33 deletions(-) diff --git a/map_gen/maps/danger_ores/config/scrap.lua b/map_gen/maps/danger_ores/config/scrap.lua index 862ee8261..bec91cbdf 100644 --- a/map_gen/maps/danger_ores/config/scrap.lua +++ b/map_gen/maps/danger_ores/config/scrap.lua @@ -1,34 +1,15 @@ -local b = require 'map_gen.shared.builders' -local start_value = b.euclidean_value(0, 0.35) -local value = b.exponential_value(0, 0.06, 1.55) +-- The Scrapworld configs (Scrap, Scrap Maze): one scrap sector wearing every terrain +-- skin, so the world keeps its natural look. +local Factory = require 'map_gen.maps.danger_ores.config.main_ores_factory' -return { - { - name = 'scrap', - ['tiles'] = { - [1] = 'red-desert-0', - [2] = 'red-desert-1', - [3] = 'red-desert-2', - [4] = 'red-desert-3', - [5] = 'dirt-1', - [6] = 'dirt-2', - [7] = 'dirt-3', - [8] = 'dirt-4', - [9] = 'dirt-5', - [10] = 'dirt-6', - [11] = 'dirt-7', - [12] = 'grass-1', - [13] = 'grass-2', - [14] = 'grass-3', - [15] = 'grass-4', - [16] = 'sand-1', - [17] = 'sand-2', - [18] = 'sand-3', - }, - ['start'] = start_value, - ['weight'] = 1, - ['ratios'] = { - {resource = b.resource(b.full_shape, 'scrap', value), weight = 100}, - } - }, -} +return Factory.blank() + :add_ore('scrap', { + mix = {{'scrap', 100}}, + tiles = { + 'red-desert-0', 'red-desert-1', 'red-desert-2', 'red-desert-3', + 'dirt-1', 'dirt-2', 'dirt-3', 'dirt-4', 'dirt-5', 'dirt-6', 'dirt-7', + 'grass-1', 'grass-2', 'grass-3', 'grass-4', + 'sand-1', 'sand-2', 'sand-3' + } + }) + :richness{0, 0.06, 1.55} From ab6a01c5e6717caafb147e6a65071e5084bd540a Mon Sep 17 00:00:00 2001 From: lex <5781244+lex@users.noreply.github.com> Date: Thu, 9 Jul 2026 14:27:57 +0300 Subject: [PATCH 31/31] Danger Ores: named arguments for the fluent curve shorthands :richness{base = 50, mult = 0.003, pow = 2.25} instead of an opaque {50, 0.003, 2.25} -- the keys mirror the builders semantics (base + mult * distance^pow); base defaults to 0 and the positional form still works. All configs move to the named form. Co-Authored-By: Claude Fable 5 --- map_gen/maps/danger_ores/config/coal.lua | 4 ++-- .../danger_ores/config/main_ores_factory.lua | 17 ++++++++++------- .../danger_ores/config/ore_configs.test.lua | 4 ++-- map_gen/maps/danger_ores/config/scrap.lua | 2 +- .../config/vanilla_ores_gridlocked.lua | 4 ++-- 5 files changed, 17 insertions(+), 14 deletions(-) diff --git a/map_gen/maps/danger_ores/config/coal.lua b/map_gen/maps/danger_ores/config/coal.lua index 41292712d..65d866ccd 100644 --- a/map_gen/maps/danger_ores/config/coal.lua +++ b/map_gen/maps/danger_ores/config/coal.lua @@ -7,5 +7,5 @@ return Factory.default() :add_ore('stone') :change_mix{{'coal', 1}} :change_tiles{'dirt-1', 'dirt-2', 'dirt-3', 'dirt-4', 'dirt-5', 'dirt-6', 'dirt-7'} - :start_value{50, 0} - :richness{0, 0.035, 1.45} + :start_value{base = 50, mult = 0} + :richness{mult = 0.035, pow = 1.45} diff --git a/map_gen/maps/danger_ores/config/main_ores_factory.lua b/map_gen/maps/danger_ores/config/main_ores_factory.lua index eef4e4e13..a2d7b7e01 100644 --- a/map_gen/maps/danger_ores/config/main_ores_factory.lua +++ b/map_gen/maps/danger_ores/config/main_ores_factory.lua @@ -118,19 +118,20 @@ end local Fluent = {} --- {base, mult} shorthand becomes a euclidean curve; functions and plain numbers --- (flat richness) pass through. +-- {base = b, mult = m} shorthand becomes a euclidean curve (b + m * distance); +-- functions and plain numbers (flat richness) pass through. local function normalize_start(start) if type(start) == 'table' then - return b.euclidean_value(start[1], start[2]) + return b.euclidean_value(start.base or start[1] or 0, start.mult or start[2]) end return start end --- {base, mult, pow} shorthand becomes an exponential curve; functions pass through. +-- {base = b, mult = m, pow = p} shorthand becomes an exponential curve +-- (b + m * distance^p); functions pass through. local function normalize_value(value) if type(value) == 'table' then - return b.exponential_value(value[1], value[2], value[3]) + return b.exponential_value(value.base or value[1] or 0, value.mult or value[2], value.pow or value[3]) end return value end @@ -278,7 +279,8 @@ function Fluent:remove_tile(tile, ore_name) end --- Set the richness curve -- for one sector, or as the default for every sector --- without its own: a function, or {base, mult, pow} for an exponential curve. +-- without its own: a function, or {base = b, mult = m, pow = p} for an exponential +-- curve b + m * distance^p. function Fluent:richness(value, ore_name) value = normalize_value(value) local spec = getmetatable(self).spec @@ -295,7 +297,8 @@ function Fluent:richness(value, ore_name) end --- Set the start (guaranteed spawn richness) curve -- for one sector, or as the --- default: a function, a plain number, or {base, mult} for a euclidean curve. +-- default: a function, a plain number (flat), or {base = b, mult = m} for a euclidean +-- curve b + m * distance. function Fluent:start_value(start, ore_name) start = normalize_start(start) local spec = getmetatable(self).spec diff --git a/map_gen/maps/danger_ores/config/ore_configs.test.lua b/map_gen/maps/danger_ores/config/ore_configs.test.lua index 13dbc30fc..a3d2a0dab 100644 --- a/map_gen/maps/danger_ores/config/ore_configs.test.lua +++ b/map_gen/maps/danger_ores/config/ore_configs.test.lua @@ -168,7 +168,7 @@ local snow = Factory.default():change_tiles({'grass-2', 'grass-3', 'grass-4'}, ' check(snow[3].tiles[1] == 'grass-2' and #snow[3].tiles == 3 and snow[1].tiles[1] == 'red-desert-0', 'change_tiles with an ore name reskins only that sector') -local rich = Factory.default():richness{50, 0.003, 2.25}:start_value{50, 0.75} +local rich = Factory.default():richness{base = 50, mult = 0.003, pow = 2.25}:start_value{base = 50, mult = 0.75} check(rich[1].ratios[1].resource.value == 'exp(50,0.003,2.25)' and rich[1].start == 'euclid(50,0.75)', 'richness/start_value shorthands rebuild every ratio') @@ -205,7 +205,7 @@ local extra_tile = Factory.default():add_tile('snow-0', 'coal') check(extra_tile[2].tiles[8] == 'snow-0' and #extra_tile[1].tiles == 4, 'add_tile appends only to the named sector') -local low_coal = Factory.default():richness{0, 0.5, 1.1}:richness({0, 0.9, 1.9}, 'coal') +local low_coal = Factory.default():richness{mult = 0.5, pow = 1.1}:richness({0, 0.9, 1.9}, 'coal') -- second one positional: fallback still works check(low_coal[2].ratios[1].resource.value == 'exp(0,0.9,1.9)' and low_coal[1].ratios[1].resource.value == 'exp(0,0.5,1.1)', 'richness with an ore name overrides one sector, default covers the rest') diff --git a/map_gen/maps/danger_ores/config/scrap.lua b/map_gen/maps/danger_ores/config/scrap.lua index bec91cbdf..8ec5bdc20 100644 --- a/map_gen/maps/danger_ores/config/scrap.lua +++ b/map_gen/maps/danger_ores/config/scrap.lua @@ -12,4 +12,4 @@ return Factory.blank() 'sand-1', 'sand-2', 'sand-3' } }) - :richness{0, 0.06, 1.55} + :richness{mult = 0.06, pow = 1.55} diff --git a/map_gen/maps/danger_ores/config/vanilla_ores_gridlocked.lua b/map_gen/maps/danger_ores/config/vanilla_ores_gridlocked.lua index c7dd0c91f..76e6ea363 100644 --- a/map_gen/maps/danger_ores/config/vanilla_ores_gridlocked.lua +++ b/map_gen/maps/danger_ores/config/vanilla_ores_gridlocked.lua @@ -6,6 +6,6 @@ local Factory = require 'map_gen.maps.danger_ores.config.main_ores_factory' return Factory.default() :add_ore('stone') :change_tiles('landfill') - :start_value{50, 0.75} - :richness{50, 0.003, 2.25} + :start_value{base = 50, mult = 0.75} + :richness{base = 50, mult = 0.003, pow = 2.25} :sector_weights{['iron-ore'] = 10, coal = 8, ['copper-ore'] = 7, stone = 2}