Add biome optimizations + decorations

main
Bram van den Heuvel 2024-09-10 13:02:44 +02:00
parent 6b38a7d7b2
commit 99e9753772
3 changed files with 379 additions and 74 deletions

413
init.lua
View File

@ -63,63 +63,57 @@ function internal.cave_vastness(pos)
end
-- Classify all nodes in the chunk into what they are
-- TODO: Perhaps instead of iterating over ALL nodes, we can sort all node types
-- TODO: into separate tables, effectively allowing us to process the nodes
-- TODO: independently, saving computation time.
function internal.classify_nodes(used_shapes, minp, maxp)
local sminp = vector.offset(minp, 1, 1, 1)
local smaxp = vector.offset(maxp, -1, -1, -1)
local sva = VoxelArea(sminp, smaxp)
local va = VoxelArea(minp, maxp)
local items = {}
local ceiling_decos = {}
local ceilings = {}
local contents = {}
local floor_decos = {}
local floors = {}
local walls = {}
for i in sva:iterp(sminp, smaxp) do
local pos = sva:position(i)
local bi = va:index(pos.x, pos.y, pos.z)
local function is_part_of_cave(v)
return used_shapes[va:index(v.x, v.y, v.z)]
end
if used_shapes[bi] == true then
if used_shapes[va:index(pos.x, pos.y, pos.z)] == true then
-- Part of cave
if not used_shapes[va:index(pos.x, pos.y + 1, pos.z)] then
items[i] = ENUM_CEILING_DECORATION
table.insert(ceiling_decos, i)
elseif not used_shapes[va:index(pos.x, pos.y - 1, pos.z)] then
items[i] = ENUM_FLOOR_DECORATION
table.insert(floor_decos, i)
else
items[i] = ENUM_AIR
table.insert(contents, i)
end
else
-- Not part of cave
if used_shapes[va:index(pos.x, pos.y + 1, pos.z)] then
items[i] = ENUM_FLOOR
table.insert(floors, i)
elseif used_shapes[va:index(pos.x, pos.y - 1, pos.z)] then
items[i] = ENUM_CEILING
table.insert(ceilings, i)
elseif used_shapes[va:index(pos.x - 1, pos.y, pos.z)] then
items[i] = ENUM_WALL
table.insert(walls, i)
elseif used_shapes[va:index(pos.x + 1, pos.y, pos.z)] then
items[i] = ENUM_WALL
table.insert(walls, i)
elseif used_shapes[va:index(pos.x, pos.y, pos.z - 1)] then
items[i] = ENUM_WALL
table.insert(walls, i)
elseif used_shapes[va:index(pos.x, pos.y, pos.z + 1)] then
items[i] = ENUM_WALL
else
items[i] = ENUM_STONE
table.insert(walls, i)
end
end
end
local custom_y = -13
if sminp.y < custom_y and custom_y < smaxp.y then
for i in sva:iterp({ x = sminp.x, y = custom_y, z = sminp.z }, { x = smaxp.x, y = custom_y, z = smaxp.z }) do
items[i] = ENUM_CEILING
end
end
return items
return {
ceiling_decos = ceiling_decos,
ceilings = ceilings,
contents = contents,
floor_decos = floor_decos,
floors = floors,
walls = walls,
}
end
function internal.clean_biome_def(def)
@ -156,6 +150,41 @@ function internal.clean_biome_def(def)
return def
end
function internal.clean_deco_def(def)
def.deco_type = def.deco_type or "simple"
def.place_on = def.place_on or "floor"
def.y_min = def.y_min or WORLD_MINP.y
def.y_max = def.y_max or WORLD_MAXP.y
assert(def.deco_type == "simple" or def.deco_type == "schematic")
assert(def.place_on == "floor" or def.place_on == "ceiling")
assert(type(def.fill_ratio) == "number")
assert(def.biomes == nil or type(def.biomes) == "table")
if def.deco_type == "simple" then
def.height = def.height or 1
def.height_max = def.height_max or def.height
def.place_offset_y = def.place_offset_y or 0
assert(type(def.deco_type) == "string")
assert(type(def.height) == "number")
assert(type(def.height_max) == "number")
assert(type(def.place_offset_y) == "number")
elseif def.deco_type == "schematic" then
def.replacements = def.replacements or {}
def.place_offset_y = def.place_offset_y or 0
assert(type(def.schematic) == "string" or type(def.schematic) == "table")
assert(type(def.replacements) == "table")
assert(def.rotation == "0" or def.rotation == "90" or def.rotation == "180" or def.rotation == "270" or def.rotation == "random")
assert(type(def.place_offset_y) == "number")
end
return def
end
function internal.clean_shape_def(def)
assert(
type(def) == "table",
@ -455,13 +484,52 @@ function internal.mapgen(minp, maxp, blockseed, vm, va)
-- Manipulate `data` table by adding classified nodes based on which biome
-- they're in.
local sva = VoxelArea(minp, maxp)
local data = vm:get_data()
internal.write_classified_biome_nodes(
data, va, classified_nodes, used_biomes, minp, maxp
internal.write_classified_node(
data, va, used_biomes, classified_nodes.ceilings, sva, "node_roof"
)
internal.write_classified_node(
data, va, used_biomes, classified_nodes.contents, sva, "node_air"
)
internal.write_classified_node(
data, va, used_biomes, classified_nodes.floors, sva, "node_floor"
)
internal.write_classified_node(
data, va, used_biomes, classified_nodes.walls, sva, "node_wall"
)
-- Place floor decorations
-- In case the dust has not been defined, place air nodes first
internal.write_classified_node(
data, va, used_biomes, classified_nodes.floor_decos, sva, "node_air"
)
internal.write_classified_node(
data, va, used_biomes, classified_nodes.floor_decos, sva, "node_dust"
)
local claimed_floor = internal.write_simple_floor_decorations(
data, va, used_biomes, classified_nodes.floor_decos, sva
)
-- Place ceiling decorations
internal.write_classified_node(
data, va, used_biomes, classified_nodes.ceiling_decos, sva, "node_air"
)
local claimed_ceiling = internal.write_simple_ceiling_decorations(
data, va, used_biomes, classified_nodes.ceiling_decos, sva
)
vm:set_data(data)
-- Set schematic decorations
internal.write_schematic_floor_decoration(
vm, used_biomes, classified_nodes.floor_decos, sva, claimed_floor
)
internal.write_schematic_ceiling_decoration(
vm, used_biomes, classified_nodes.ceiling_decos, sva, claimed_ceiling
)
vm:write_to_map()
end
@ -486,6 +554,14 @@ function internal.register_biome(biome)
ns_caves.registered_biomes[biome.name] = biome
end
-- Register a new decoration
function internal.register_decoration(deco)
table.insert(
ns_caves.registered_decorations,
internal.clean_deco_def(deco)
)
end
-- Register a new shape
function internal.register_shape(shape)
shape = internal.clean_shape_def(shape)
@ -530,57 +606,242 @@ function internal.verticality_noise_params()
}
end
function internal.write_classified_biome_nodes(vm_data, va, classified_nodes, used_biomes, minp, maxp)
local small_va = VoxelArea(minp, maxp)
-- assert(#classified_nodes == #used_biomes)
-- assert(#classified_nodes == small_va:getVolume())
function internal.write_classified_node(vm_data, va, used_biomes, classified_nodes, small_va, biome_key)
local default_biome = internal.default_biome()
local schems = {}
for i in va:iterp(minp, maxp) do
local pos = va:position(i)
local si = small_va:index(pos.x, pos.y, pos.z)
local biome_to_id = {}
biome_to_id[default_biome.name] = default_biome[biome_key] or ""
local node_type = classified_nodes[si]
local biome = used_biomes[si]
for _, i in ipairs(classified_nodes) do
local pos = small_va:position(i)
local biome = used_biomes[i] or default_biome.name
if biome == nil then
biome = default_biome
else
biome = ns_caves.registered_biomes[biome] or default_biome
if biome_to_id[biome] == nil then
local biome_def
if biome == default_biome.name then
biome_def = default_biome
else
biome_def = ns_caves.registered_biomes[biome] or default_biome
end
if biome_def[biome_key] == nil then
biome_to_id[biome] = ""
else
biome_to_id[biome] = minetest.get_content_id(biome_def[biome_key]) or ""
end
end
if node_type == ENUM_AIR then
internal.place_node_on_data(vm_data, i, biome.node_air)
elseif node_type == ENUM_CEILING then
internal.place_node_on_data(vm_data, i, biome.node_roof)
elseif node_type == ENUM_CEILING_DECORATION then
-- TODO: Return schematics to be placed
internal.place_node_on_data(vm_data, i, biome.node_air)
elseif node_type == ENUM_FLOOR then
internal.place_node_on_data(vm_data, i, biome.node_floor)
elseif node_type == ENUM_FLOOR_DECORATION then
-- TODO: Return schematics to be placed
internal.place_node_on_data(vm_data, i, biome.node_air)
elseif node_type == ENUM_STONE then
-- Nothing needs to be placed
elseif node_type == ENUM_WALL then
internal.place_node_on_data(vm_data, i, biome.node_wall)
else
if node_type == nil then
error(
"Expected enum value, encountered nil at index " .. si .. " (originally " .. i .. ")"
)
local content_id = biome_to_id[biome]
if content_id ~= "" then
vm_data[va:index(pos.x, pos.y, pos.z)] = content_id
end
end
end
function internal.write_schematic_ceiling_decoration(vmanip, used_biomes, classified_nodes, sva, claimed_spots)
for _, def in pairs(ns_caves.registered_decorations) do
if def.deco_type == "schematic" and def.place_on == "ceiling" then
-- Place the decoration, if they're in the appropriate biome
for _, i in ipairs(classified_nodes) do
local good_biome = def.biomes == nil
local unclaimed_spot = true
for _, ci in ipairs(claimed_spots) do
if ci == i then
unclaimed_spot = false
break
end
end
if unclaimed_spot and not good_biome then
local current_biome = used_biomes[i]
for _, name in ipairs(def.biomes) do
if name == current_biome then
good_biome = true
break
end
end
end
if unclaimed_spot and good_biome and math.random() < def.fill_ratio then
-- Automatically place the top at the top of the cave
local pos = sva:position(i)
local h = def.schematic
if type(def.schematic) == "string" then
h = minetest.read_schematic(h, {})
if type(h) == "nil" then
error("Could not find schematic! Perhaps it the filename is incorrect?")
end
end
h = h.size.y
minetest.place_schematic_on_vmanip(vmanip,
{ x = pos.x, y = pos.y - h + 1 + def.place_offset_y, z = pos.z },
def.schematic, def.rotation, def.replacement, true,
def.flags
)
table.insert(claimed_spots, i)
end
end
error(
"Encountered unknown node type enum value " .. node_type
)
end
end
return schems
return claimed_spots
end
function internal.write_schematic_floor_decoration(vmanip, used_biomes, classified_nodes, sva, claimed_spots)
for _, def in pairs(ns_caves.registered_decorations) do
if def.deco_type == "schematic" and def.place_on == "floor" then
-- Place the decoration, if they're in the appropriate biome
for _, i in ipairs(classified_nodes) do
local good_biome = def.biomes == nil
local unclaimed_spot = true
for _, ci in ipairs(claimed_spots) do
if ci == i then
unclaimed_spot = false
break
end
end
if unclaimed_spot and not good_biome then
local current_biome = used_biomes[i]
for _, name in ipairs(def.biomes) do
if name == current_biome then
good_biome = true
break
end
end
end
if unclaimed_spot and good_biome and math.random() < def.fill_ratio then
minetest.place_schematic_on_vmanip(
vmanip, sva:position(i), def.schematic, def.rotation,
def.replacement, true, def.flags
)
table.insert(claimed_spots, i)
end
end
end
end
return claimed_spots
end
function internal.write_simple_ceiling_decorations(vm_data, va, used_biomes, classified_nodes, sva)
local claimed_spots = {}
for _, def in pairs(ns_caves.registered_decorations) do
if def.deco_type == "simple" and def.place_on == "ceiling" then
-- Place the decoration, if they're in the appropriate biome.
for _, i in ipairs(classified_nodes) do
local pos = sva:position(i)
local good_biome = def.biomes == nil
local unclaimed_spot = true
for _, ci in ipairs(claimed_spots) do
if ci == i then
unclaimed_spot = false
break
end
end
if not good_biome and unclaimed_spot then
local current_biome = used_biomes[i]
for _, name in ipairs(def.biomes) do
if name == current_biome then
good_biome = true
break
end
end
end
if unclaimed_spot and good_biome and math.random() < def.fill_ratio then
-- Determine the height
local height = def.height
if def.height_max > height then
height = math.min(
16, math.random(def.height, def.height_max)
)
end
-- Place the structure!
for h = 1, height, 1 do
local y = pos.y - h + def.place_offset_y + 1
if sva:contains(pos.x, y, pos.z) then
vm_data[va:index(pos.x, y, pos.z)] = minetest.get_content_id(def.decoration)
end
end
table.insert(claimed_spots, i)
end
end
end
end
return claimed_spots
end
function internal.write_simple_floor_decorations(vm_data, va, used_biomes, classified_nodes, sva)
local claimed_spots = {}
for _, def in pairs(ns_caves.registered_decorations) do
if def.deco_type == "simple" and def.place_on == "floor" then
-- Place the decoration, if they're in the appropriate biome.
for _, i in ipairs(classified_nodes) do
local pos = sva:position(i)
local good_biome = def.biomes == nil
local unclaimed_spot = true
for _, ci in ipairs(claimed_spots) do
if ci == i then
unclaimed_spot = false
break
end
end
if unclaimed_spot and not good_biome then
local current_biome = used_biomes[i]
for _, name in ipairs(def.biomes) do
if name == current_biome then
good_biome = true
break
end
end
end
if unclaimed_spot and good_biome and math.random() < def.fill_ratio then
-- Determine the height
local height = def.height
if def.height_max > height then
height = math.min(
16, math.random(def.height, def.height_max)
)
end
-- Place the structure!
for h = 1, height, 1 do
local y = pos.y + h + def.place_offset_y - 1
if sva:contains(pos.x, y, pos.z) then
vm_data[va:index(pos.x, y, pos.z)] = minetest.get_content_id(def.decoration)
end
end
table.insert(claimed_spots, i)
end
end
end
end
return claimed_spots
end
-------------------------------------------------------------------------------
@ -597,6 +858,8 @@ minetest.register_on_generated(function(minp, maxp, blockseed)
local vm = minetest.get_mapgen_object("voxelmanip")
local va = VoxelArea(vm:get_emerged_area())
math.randomseed(blockseed)
internal.mapgen(minp, maxp, blockseed, vm, va)
end)
@ -613,10 +876,14 @@ ns_caves = {
register_biome = internal.register_biome,
register_decoration = internal.register_decoration,
register_shape = internal.register_shape,
registered_biomes = {},
registered_decorations = {},
registered_shapes = {},
}

View File

@ -44,3 +44,41 @@ ns_caves.register_biome({
heat_point = 0,
humidity_point = 50,
})
ns_caves.register_biome({
name = "ns_caves:drip",
node_floor = "dripstone:dry_dripstone_block",
node_wall = "dripstone:dry_dripstone_block",
node_roof = "dripstone:dry_dripstone_block",
heat_point = 50,
humidity_point = 0,
})
ns_caves.register_decoration({
deco_type = "simple",
place_on = "ceiling",
fill_ratio = 0.25,
biomes = { "ns_caves:snow" },
decoration = "mcl_core:water_source",
height = 1,
place_offset_y = 2,
})
ns_caves.register_decoration({
deco_type = "simple",
place_on = "ceiling",
fill_ratio = 0.025,
decoration = "mcl_bamboo:bamboo_block",
height = 3,
height_max = 16,
})
ns_caves.register_decoration({
deco_type = "simple",
place_on = "floor",
fill_ratio = 0.025,
decoration = "mcl_bamboo:bamboo_block",
height = 3,
height_max = 16,
})

View File

@ -2,5 +2,5 @@ name=ns_caves
description=A mod that adds more depths and caves to VoxeLibre or Mineclonia
author=Noordstar
title=Noordstar Caves
depends=mcl_init
depends=dripstone,mcl_init
optional_depends=mcl_init,mcl_worlds