Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 14 additions & 33 deletions map_gen/maps/danger_ores/compatibility/omnimatter/ores.lua
Original file line number Diff line number Diff line change
@@ -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 Omnimatter configs (Omnimatter, Cages, Maze): one omnite 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 = 'omnite',
['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, 'omnite', value), weight = 100},
}
},
}
return Factory.blank()
:add_ore('omnite', {
mix = {{'omnite', 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}
131 changes: 131 additions & 0 deletions map_gen/maps/danger_ores/config/main_ores_factory.lua
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,49 @@ function Fluent:start_value(start, ore_name)
return rebuild(self)
end

-- Multiply a curve by a factor: numbers scale directly, functions get wrapped.
local function scale_curve(curve, multiplier)
if curve == nil or multiplier == 1 then
return curve
end
if type(curve) == 'number' then
return multiplier * curve
end
return function(x, y)
return multiplier * curve(x, y)
end
end

--- Scale the ore density: multiply every start and richness curve by multiplier --
-- for one sector, or the whole config. e.g. :scale_richness(0.25) for a quarter of
-- the source config's ore. Entries with a custom make_resource are not affected.
function Fluent:scale_richness(multiplier, ore_name)
local spec = getmetatable(self).spec
if ore_name then
for _, ore in ipairs(spec.ores) do
if ore.name == ore_name then
-- materialize the defaults before scaling only this sector
if ore.start == nil then
ore.start = spec.start
end
if ore.value == nil then
ore.value = spec.value
end
ore.start = scale_curve(ore.start, multiplier)
ore.value = scale_curve(ore.value, multiplier)
end
end
else
spec.start = scale_curve(spec.start, multiplier)
spec.value = scale_curve(spec.value, multiplier)
for _, ore in ipairs(spec.ores) do
ore.start = scale_curve(ore.start, multiplier)
ore.value = scale_curve(ore.value, multiplier)
end
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
Expand All @@ -325,4 +368,92 @@ function Fluent:sector_weights(weights)
return rebuild(self)
end

-- == Resource patches =======================================================
-- A fluent builder for resource patches configs (main_ore_resource_patches_config /
-- resource_patches_config): perlin-noise patches of dense resources on top of the
-- main ores. Same contract as the main-ores fluent API: the returned object IS the
-- patches array, every method mutates it in place and returns it for chaining, and
-- method order never matters.
-- Factory.patches():add_patch('stone', {scale = 1 / 32, threshold = 0.6})
-- Factory.patches(shared_config):scale{richness = 0.5, size = 2}

local PatchesFluent = {}

-- The canonical patch richness curve, as used by the vanilla resource patches.
local default_patch_value = b.exponential_value(0, 1.4, 1.45)

-- Multiply the amounts a resource shape yields.
local function scale_amount(resource, multiplier)
if multiplier == 1 then
return resource
end
return function(x, y, world)
local entity = resource(x, y, world)
if entity and entity.amount then
entity.amount = multiplier * entity.amount
end
return entity
end
end

local function rebuild_patches(self)
local spec = getmetatable(self).spec
for i = #self, 1, -1 do
self[i] = nil
end
for index, patch in ipairs(spec.patches) do
local resource
if patch.resource then -- entry carried over from a source config
resource = scale_amount(patch.resource, spec.richness)
else
resource = b.resource(b.full_shape, patch.name, scale_curve(patch.value or default_patch_value, spec.richness))
end
self[index] = {
scale = patch.scale / spec.size,
threshold = patch.threshold,
seed = patch.seed,
resource = resource
}
end
return self
end

--- A fluent resource patches config, optionally seeded from an existing plain
-- config. The source is not modified; its entries are carried over and can still
-- be scaled.
function Public.patches(source)
local patches = {}
for i, data in ipairs(source or {}) do
patches[i] = {scale = data.scale, threshold = data.threshold, seed = data.seed, resource = data.resource}
end
local spec = {patches = patches, richness = 1, size = 1}
return rebuild_patches(setmetatable({}, {__index = PatchesFluent, spec = spec}))
end

--- Append a pure-resource patch. opts (all optional): scale (patch size, default
-- 1 / 24), threshold (rarity, default 0.5), richness (a function or
-- {base = b, mult = m, pow = p}, default the canonical patch curve) and seed.
function PatchesFluent:add_patch(resource_name, opts)
opts = opts or {}
local spec = getmetatable(self).spec
spec.patches[#spec.patches + 1] = {
name = resource_name,
scale = opts.scale or 1 / 24,
threshold = opts.threshold or 0.5,
value = normalize_value(opts.richness),
seed = opts.seed
}
return rebuild_patches(self)
end

--- Scale every patch: richness multiplies the ore amounts, size multiplies the
-- linear patch dimensions (patch spacing grows with it, so the ore coverage
-- fraction stays the same). Multipliers compound when applied twice.
function PatchesFluent:scale(multipliers)
local spec = getmetatable(self).spec
spec.richness = spec.richness * (multipliers.richness or 1)
spec.size = spec.size * (multipliers.size or 1)
return rebuild_patches(self)
end

return Public
85 changes: 85 additions & 0 deletions map_gen/maps/danger_ores/config/ore_configs.test.lua
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,91 @@ 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')

-- function curves so the scaled results stay callable under the stubbed builders
local function flat(amount)
return function()
return amount
end
end

local function test_scale_richness()
local scaled = Factory.blank()
:add_ore('scrap', {mix = {{'scrap', 100}}})
:richness(flat(100))
:start_value(flat(40))
:scale_richness(0.25)
check(scaled[1].ratios[1].resource.value() == 25 and scaled[1].start() == 10,
'scale_richness scales the richness and start curves')

local half_coal = Factory.default()
:richness(flat(100))
:start_value(50)
:scale_richness(0.5, 'coal')
check(half_coal[2].ratios[1].resource.value() == 50 and half_coal[2].start == 25
and half_coal[1].ratios[1].resource.value() == 100 and half_coal[1].start == 50,
'scale_richness with an ore name scales only that sector')

local compounded = Factory.blank()
:add_ore('omnite')
:richness(flat(100))
:start_value(8)
:scale_richness(0.5)
:scale_richness(0.5)
check(compounded[1].ratios[1].resource.value() == 25 and compounded[1].start == 2,
'scale_richness compounds when applied twice')

local scaled_then_added = Factory.blank()
:richness(flat(100))
:start_value(flat(40))
:scale_richness(0.25)
:add_ore('scrap', {mix = {{'scrap', 100}}})
check(scaled_then_added[1].ratios[1].resource.value() == 25 and scaled_then_added[1].start() == 10,
'sectors added after scale_richness still get the scaled defaults')
end
test_scale_richness()

local function test_patches()
local stone = Factory.patches()
:add_patch('stone', {scale = 1 / 32, threshold = 0.6, richness = flat(100)})
check(#stone == 1 and stone[1].scale == 1 / 32 and stone[1].threshold == 0.6
and stone[1].resource.name == 'stone' and stone[1].resource.value() == 100,
'patches():add_patch builds a pure resource patch entry')

stone:scale{richness = 0.25, size = 2}
check(stone[1].scale == 1 / 64 and stone[1].resource.value() == 25,
'patches scale multiplies richness and grows the patch size')

local canonical = Factory.patches():add_patch('iron-ore')
check(canonical[1].scale == 1 / 24 and canonical[1].threshold == 0.5
and canonical[1].resource.value == 'exp(0,1.4,1.45)',
'add_patch defaults to the canonical patch size, rarity and curve')

local source = {
{scale = 1 / 24, threshold = 0.5, resource = function()
return {name = 'iron-ore', amount = 100}
end},
{scale = 1 / 24, threshold = 0.5, resource = function()
return nil
end}
}
local wrapped = Factory.patches(source):scale{richness = 0.5, size = 2}
check(wrapped[1].scale == 1 / 48 and wrapped[1].resource(0, 0, {}).amount == 50,
'patches(source) carries over an existing config and scales it')
check(wrapped[2].resource(0, 0, {}) == nil,
'patches(source) preserves empty resource results')
check(source[1].scale == 1 / 24 and source[1].resource(0, 0, {}).amount == 100,
'patches(source) does not modify the original config')

local compounded = Factory.patches(source):scale{richness = 0.5}:scale{richness = 0.5}
check(compounded[1].resource(0, 0, {}).amount == 25,
'patches scale compounds when applied twice')

local late = Factory.patches():scale{richness = 0.5}:add_patch('stone', {richness = flat(100)})
check(late[1].resource.value() == 50,
'patches added after scale still get the scale applied')
end
test_patches()

-- 5) golden numbers for the source configs, so old maps cannot drift by accident
local function ratio_weights(entry)
local weights = {}
Expand Down
5 changes: 4 additions & 1 deletion map_gen/maps/danger_ores/presets/danger_ore_coal_maze.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
local B = require 'map_gen.shared.builders'
local DOC = require 'map_gen.maps.danger_ores.configuration'
local Factory = require 'map_gen.maps.danger_ores.config.main_ores_factory'
local Scenario = require 'map_gen.maps.danger_ores.scenario'
local ScenarioInfo = require 'features.gui.info'

Expand All @@ -13,7 +14,9 @@ DOC.scenario_name = 'danger-ore-coal-maze'
DOC.map_config.spawn_shape = B.translate(B.rectangle(64), 2, 2)
DOC.map_config.start_ore_shape = B.translate(B.rectangle(68), 2, 2)
DOC.map_config.no_resource_patch_shape = B.translate(B.rectangle(80), 2, 2)
DOC.map_config.main_ore_resource_patches_config = require 'map_gen.maps.danger_ores.config.main_ore_resource_patches'
DOC.map_config.main_ore_resource_patches_config = Factory
.patches(require 'map_gen.maps.danger_ores.config.main_ore_resource_patches')
:scale{richness = 0.5, size = 2}
DOC.map_config.main_ores_builder = require 'map_gen.maps.danger_ores.modules.main_ores_patches'
DOC.map_config.main_ores = require 'map_gen.maps.danger_ores.config.coal'
DOC.map_config.main_ores_rotate = nil
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Config.player_create.starting_items = {
DOC.scenario_name = 'danger-ore-omnimatter-maze'
DOC.concrete_on_landfill.refund_tile = 'omnite-refined-concrete'
DOC.game.technology_price_multiplier = 1
DOC.map_config.main_ores = require 'map_gen.maps.danger_ores.compatibility.omnimatter.ores'
DOC.map_config.main_ores = require('map_gen.maps.danger_ores.compatibility.omnimatter.ores'):scale_richness(0.25)
DOC.map_config.main_ores_rotate = nil
DOC.map_config.no_resource_patch_shape = B.translate(B.rectangle(80), 2, 2)
DOC.map_config.spawn_shape = B.translate(B.rectangle(64), 2, 2)
Expand Down
9 changes: 7 additions & 2 deletions map_gen/maps/danger_ores/presets/danger_ore_scrap_maze.lua
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
local B = require 'map_gen.shared.builders'
local H = require 'map_gen.maps.danger_ores.modules.helper'
local DOC = require 'map_gen.maps.danger_ores.configuration'
local Factory = require 'map_gen.maps.danger_ores.config.main_ores_factory'
local Config = require 'config'
local Scenario = require 'map_gen.maps.danger_ores.scenario'
local ScenarioInfo = require 'features.gui.info'

ScenarioInfo.set_map_name('Danger Ores - Scrapworld Maze')
ScenarioInfo.add_map_extra_info([[
This maze is covered in [entity=scrap].
This maze is covered in [entity=scrap], with occasional dense patches of
[item=stone] for landfill.
Mine it to make room for your factory, and explore the corridors to expand.
]])

Expand All @@ -19,7 +21,10 @@ Config.player_create.starting_items = {
}

DOC.scenario_name = 'danger-ore-scrap-maze'
DOC.map_config.main_ores = require 'map_gen.maps.danger_ores.config.scrap'
DOC.map_config.main_ore_resource_patches_config = Factory.patches()
:add_patch('stone', {scale = 1 / 32, threshold = 0.6, richness = {mult = 0.7, pow = 1.45}})
DOC.map_config.main_ores_builder = require 'map_gen.maps.danger_ores.modules.main_ores_patches'
DOC.map_config.main_ores = require('map_gen.maps.danger_ores.config.scrap'):scale_richness(0.25)
DOC.map_config.main_ores_rotate = nil
DOC.map_config.no_resource_patch_shape = B.translate(B.rectangle(80), 2, 2)
DOC.map_config.spawn_shape = B.translate(B.rectangle(64), 2, 2)
Expand Down
Loading