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 64789ca6c..000000000 --- a/map_gen/maps/danger_ores/config/3way_ores.lua +++ /dev/null @@ -1,76 +0,0 @@ -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) - -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} - } - } ]] -} diff --git a/map_gen/maps/danger_ores/config/coal.lua b/map_gen/maps/danger_ores/config/coal.lua index c3cdd9481..65d866ccd 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{base = 50, mult = 0} + :richness{mult = 0.035, pow = 1.45} 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..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,92 +1,17 @@ +-- The Joker starter area: canonical sectors with time-budgeted richness -- each patch +-- 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 -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} - } - }, - { - 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 - }, - } - } -} +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)) + :richness(b.exponential_value(low, 0.07, 1.45), 'coal') 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..a2d7b7e01 --- /dev/null +++ b/map_gen/maps/danger_ores/config/main_ores_factory.lua @@ -0,0 +1,328 @@ +-- 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. +-- +-- 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 table = require 'utils.table' + +local deep_copy = table.deep_copy + +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}} +} +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) + +-- 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 deep_copy(tiles) +end + +-- 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, pair in ipairs(mix) do + ratios[i] = {resource = make_resource(pair[1]), weight = pair[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; 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 + 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 + local tiles + if params.tiles == nil then + tiles = Public.tiles(name) + elseif params.tiles then + tiles = params.tiles + end + return { + name = name, + tiles = tiles, + 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 deep_copy(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 + +-- == Fluent API ============================================================= + +local Fluent = {} + +-- {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.base or start[1] or 0, start.mult or start[2]) + end + return start +end + +-- {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.base or value[1] or 0, value.mult or value[2], value.pow or 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 = deep_copy(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 + +--- 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 + +--- 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 + 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 + +-- 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 = 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 + 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 one sector, or as the +-- 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 + 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 + +--- 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/one_direction_ores_xmas.lua b/map_gen/maps/danger_ores/config/one_direction_ores_xmas.lua deleted file mode 100644 index 3348bdbb9..000000000 --- a/map_gen/maps/danger_ores/config/one_direction_ores_xmas.lua +++ /dev/null @@ -1,74 +0,0 @@ -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) - -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} - } - }, - --[[ { - 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} - } - } ]] -} 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..a3d2a0dab --- /dev/null +++ b/map_gen/maps/danger_ores/config/ore_configs.test.lua @@ -0,0 +1,240 @@ +-- 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 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) + 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(_, 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', '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' } + +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 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') +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 +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', + '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) 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{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') + +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 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') + +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{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') + +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') + +-- 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 + 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(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 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..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,141 +1,36 @@ +-- 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' -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', 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) } diff --git a/map_gen/maps/danger_ores/config/scrap.lua b/map_gen/maps/danger_ores/config/scrap.lua index 862ee8261..8ec5bdc20 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{mult = 0.06, pow = 1.55} 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.lua b/map_gen/maps/danger_ores/config/vanilla_ores.lua index 4e97b5eaf..2ab4578c8 100644 --- a/map_gen/maps/danger_ores/config/vanilla_ores.lua +++ b/map_gen/maps/danger_ores/config/vanilla_ores.lua @@ -1,76 +1,4 @@ -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) +-- 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 { - { - 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.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 e6c640def..76e6ea363 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,11 @@ -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) +-- 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 { - { - 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.default() + :add_ore('stone') + :change_tiles('landfill') + :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} 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..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,64 +1,5 @@ -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) +-- vanilla_ores skinned entirely in landfill tiles. Used by maps where the whole ore +-- field doubles as buildable ground. +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.default():change_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 deleted file mode 100644 index 2a42407ee..000000000 --- a/map_gen/maps/danger_ores/config/vanilla_ores_one_direction.lua +++ /dev/null @@ -1,76 +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.15, 1.2) - -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} - } - } ]] -} 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..80c6d2410 --- /dev/null +++ b/map_gen/maps/danger_ores/config/vanilla_ores_stone.lua @@ -0,0 +1,6 @@ +-- 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' + +return Factory.default():add_ore('stone') 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/config/x_cross_ores.lua b/map_gen/maps/danger_ores/config/x_cross_ores.lua deleted file mode 100644 index c5b9f21af..000000000 --- a/map_gen/maps/danger_ores/config/x_cross_ores.lua +++ /dev/null @@ -1,76 +0,0 @@ -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) - -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} - } - } -} 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 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 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_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) 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) 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..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,7 +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' -DOC.map_config.main_ores = require 'map_gen.maps.danger_ores.config.one_direction_ores_xmas' +-- 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():remove_tile('grass-4') DOC.map_config.main_ores_rotate = nil DOC.map_config.water = nil DOC.game.always_day = false