EinsDreiDreiSieben/mods/asuna/asuna_core/decor.lua
2025-05-04 16:01:41 +02:00

816 lines
No EOL
19 KiB
Lua

asuna.decor = {}
--[[
Flowers
]]
-- Group flowers by color
local flower_colors = {
black = {
"flowers:tulip_black",
},
white = {
"beautiflowers:carla",
"beautiflowers:cloe",
"beautiflowers:genesis",
"beautiflowers:gloria",
"beautiflowers:hadassa",
"beautiflowers:ingrid",
"beautiflowers:irene",
"beautiflowers:iris",
"beautiflowers:ivette",
"beautiflowers:michelle",
"beautiflowers:suri",
"flowers:dandelion_white",
"herbs:achillea_white",
"herbs:leontopodium_white",
"herbs:leucanthemum_white",
"herbs:trifolium_white",
},
blue = {
"beautiflowers:beatriz",
"beautiflowers:berta",
"flowers:geranium",
"herbs:centaurea",
"herbs:campanula_blue",
"herbs:digitalis_blue",
"herbs:iris",
"bakedclay:delphinium",
},
cyan = {
"beautiflowers:thais",
"beautiflowers:valentina",
"beautiflowers:valeria",
"beautiflowers:vera",
"beautiflowers:victoria",
"beautiflowers:virginia",
"beautiflowers:xenia",
"beautiflowers:zaida",
},
orange = {
"beautiflowers:dafne",
"beautiflowers:dana",
"beautiflowers:delia",
"beautiflowers:elena",
"beautiflowers:erica",
"beautiflowers:estela",
"beautiflowers:eva",
"beautiflowers:fabiola",
"beautiflowers:fiona",
"beautiflowers:gala",
"flowers:tulip",
},
yellow = {
"beautiflowers:ada",
"beautiflowers:agnes",
"beautiflowers:alicia",
"beautiflowers:alma",
"beautiflowers:amaia",
"beautiflowers:anastasia",
"beautiflowers:any",
"flowers:dandelion_yellow",
"farming:sunflower_8",
"herbs:digitalis_yellow",
"herbs:plantago",
},
purple = {
"beautiflowers:arleth",
"beautiflowers:astrid",
"beautiflowers:belen",
"beautiflowers:blanca",
"beautiflowers:casandra",
"beautiflowers:clara",
"beautiflowers:claudia",
"beautiflowers:minerva",
"beautiflowers:miriam",
"beautiflowers:nazareth",
"beautiflowers:noemi",
"beautiflowers:olga",
"beautiflowers:paula",
"beautiflowers:regina",
"beautiflowers:rocio",
"beautiflowers:sabrina",
"beautiflowers:vanesa",
"flowers:viola",
"beautiflowers:xena",
},
red = {
"beautiflowers:arcoiris",
"beautiflowers:jennifer",
"beautiflowers:lara",
"beautiflowers:laura",
"beautiflowers:lidia",
"beautiflowers:lucia",
"beautiflowers:mara",
"beautiflowers:martina",
"beautiflowers:melania",
"beautiflowers:mireia",
"beautiflowers:nadia",
"beautiflowers:nerea",
"beautiflowers:noelia",
"flowers:rose",
"herbs:dosera",
"herbs:papaver_red",
},
pink = {
"beautiflowers:caroline",
"beautiflowers:cristina",
"beautiflowers:diana",
"beautiflowers:gisela",
"beautiflowers:olimpia",
"beautiflowers:oriana",
"beautiflowers:pia",
"beautiflowers:raquel",
"beautiflowers:ruth",
"beautiflowers:sandra",
"beautiflowers:sara",
"beautiflowers:silvia",
"beautiflowers:sofia",
"beautiflowers:sonia",
"beautiflowers:talia",
"herbs:antirrhinum",
"herbs:trifolium_red",
"bakedclay:thistle",
"bakedclay:lazarus",
"ethereal:lilac",
},
green = {
"flowers:chrysanthemum_green",
"beautiflowers:pasto_1",
"beautiflowers:pasto_2",
"beautiflowers:pasto_3",
"beautiflowers:pasto_4",
"beautiflowers:pasto_5",
"beautiflowers:pasto_6",
"beautiflowers:pasto_7",
"beautiflowers:pasto_8",
"beautiflowers:pasto_9",
"beautiflowers:pasto_10",
"bakedclay:mannagrass",
},
red_mushroom = {
"flowers:mushroom_red",
},
brown_mushroom = {
"flowers:mushroom_brown",
"herbs:mushroom_boletus",
"herbs:mushroom_cantharellus",
"herbs:mushroom_macrolepiota",
},
odd_mushroom = {
"herbs:mushroom_amanita_green",
"herbs:mushroom_gyromitra",
"herbs:mushroom_galerina",
}
}
-- Register flower decorations per biome
local function cf(biome)
local decor = {
flowers = {},
mushrooms = {},
}
-- Generate list of flowers
for _,flower in ipairs(biome.flowers or {}) do
for _,flower in ipairs(flower_colors[flower]) do
table.insert(decor.flowers,flower)
end
end
-- Generate list of mushrooms
for _,mushroom in ipairs(biome.mushrooms or {}) do
for _,mushroom in ipairs(flower_colors[mushroom .. "_mushroom"]) do
table.insert(decor.mushrooms,mushroom)
end
end
return decor
end
-- Do actual decoration registration after other mods are finished
minetest.register_on_mods_loaded(function()
-- Register shore grass
local sandy_biomes = {}
local desert_biomes = {
desert = true,
sandstone_desert = true,
desert_shore = true,
sandstone_desert_shore = true,
desert_below = true,
sandstone_desert_below = true,
}
for biome,def in pairs(asuna.biomes) do
if def.shore == "default:sand" and
(def.ocean == "temperate" or def.ocean == "tropical" or def.ocean == "cold") and
not desert_biomes[biome]
then
table.insert(sandy_biomes,biome)
end
end
minetest.register_decoration(asuna.biome_groups.shore.inject_decoration({
deco_type = "simple",
place_on = "default:sand",
decoration = {"default:marram_grass_1","default:marram_grass_2","default:marram_grass_3"},
y_min = 2,
y_max = 2,
sidelen = 16,
noise_params = {
offset = 0.004,
scale = 0.0195,
spread = {x = 11, y = 11, z = 11},
seed = 69420,
octaves = 1,
},
biomes = sandy_biomes,
}))
minetest.register_decoration(asuna.biome_groups.shore.inject_decoration({
deco_type = "simple",
place_on = "default:sand",
decoration = {"default:marram_grass_1","default:marram_grass_2","default:marram_grass_3"},
y_min = 3,
y_max = 4,
sidelen = 16,
noise_params = {
offset = 0.075,
scale = 0.175,
spread = {x = 10, y = 10, z = 10},
seed = 42069,
octaves = 1,
},
biomes = sandy_biomes,
}))
-- Register flowers and mushrooms
for name,biome in pairs(asuna.biomes) do
-- Get node groups for biome
local decor = cf(biome)
-- Register flowers
if #biome.flowers > 0 then
minetest.register_decoration({
name = "asuna_core:flowers_" .. name,
deco_type = "simple",
sidelen = 80,
place_on = biome.nodes.extra and {biome.nodes[1],unpack(biome.nodes.extra)} or biome.nodes[1],
noise_params = {
offset = 0.006786,
scale = 0.004175,
spread = {x = 8, y = 8, z = 8},
seed = 1999,
octaves = 2,
persist = 0.44,
lacunarity = 0.75,
},
biomes = {name},
y_max = 31000,
y_min = 1,
decoration = decor.flowers,
})
end
-- Register mushrooms
if #biome.mushrooms > 0 then
minetest.register_decoration({
name = "asuna_core:mushrooms_" .. name,
deco_type = "simple",
sidelen = 8,
place_on = biome.nodes.extra and {biome.nodes[1],unpack(biome.nodes.extra)} or biome.nodes[1],
noise_params = {
offset = -0.0069,
scale = 0.027525,
spread = {x = 8, y = 8, z = 8},
seed = 60659,
octaves = 2,
persist = 0.7625,
lacunarity = 0.6,
},
biomes = {name},
y_max = 31000,
y_min = 1,
decoration = decor.mushrooms,
})
end
end
-- Special sunflower decor for the Plains biome
minetest.register_decoration({
name = "asuna_core:plains_special_sunflower",
deco_type = "simple",
place_on = {"default:dry_dirt_with_dry_grass"},
sidelen = 80,
fill_ratio = 0.075,
biomes = {"plains"},
y_max = 31000,
y_min = 1,
decoration = "farming:sunflower_8",
})
-- Special dense flower decor for the Prairie biome
minetest.register_decoration({
name = "asuna_core:prairie_special_flowers",
deco_type = "simple",
place_on = "prairie:prairie_dirt_with_grass",
sidelen = 80,
fill_ratio = 0.265,
biomes = {"prairie"},
y_max = 31000,
y_min = 1,
decoration = cf({ flowers = {"blue","cyan","white","orange","yellow"} }).flowers,
})
-- Special mushroom decor for the Mushroom biome
minetest.register_decoration({
name = "asuna_core:mushroom_special_mushrooms",
deco_type = "simple",
place_on = "ethereal:mushroom_dirt",
sidelen = 80,
fill_ratio = 0.075,
biomes = {"mushroom"},
y_max = 31000,
y_min = 1,
decoration = cf({ mushrooms = {"odd","brown"} }).mushrooms,
})
-- Bushes for certain grassy biomes
minetest.register_decoration({
name = "bushes:default_replacement",
deco_type = "schematic",
place_on = "default:dirt_with_grass",
sidelen = 80,
fill_ratio = 0.00025,
y_min = 2,
y_max = 31000,
biomes = {
"grassland",
"deciduous_forest",
"grassytwo",
"jumble",
"marsh",
},
schematic = minetest.get_modpath("default") .. "/schematics/bush.mts",
flags = "place_center_x,place_center_z",
})
--[[
Butterflies
]]
if minetest.get_modpath("butterflies") then
minetest.register_decoration({
name = "butterflies:butterfly",
deco_type = "simple",
place_on = "group:soil",
place_offset_y = 1,
sidelen = 80,
fill_ratio = 0.005,
biomes = {
"grassland",
"deciduous_forest",
"grassytwo",
"prairie",
"dorwinion",
"jumble",
"bamboo",
"naturalbiomes:heath",
"naturalbiomes:alpine",
"everness:bamboo_forest",
},
y_max = 31000,
y_min = 1,
decoration = {
"butterflies:butterfly_white",
"butterflies:butterfly_red",
"butterflies:butterfly_violet",
},
spawn_by = "group:flower",
num_spawn_by = 1,
})
end
--[[
Fireflies
]]
if minetest.get_modpath("fireflies") then
minetest.register_decoration({
name = "fireflies:firefly_low",
deco_type = "simple",
place_on = "group:soil",
place_offset_y = 2,
sidelen = 16,
noise_params = {
offset = -0.005,
scale = 0.015,
spread = {x = 60, y = 20, z = 60},
seed = 2112,
octaves = 1,
persistence = 0.75,
flags = "eased"
},
biomes = {
"deciduous_forest",
"grassland",
"grassytwo",
"prairie",
"dorwinion",
"jumble",
"swamp",
"marsh",
"naturalbiomes:alderswamp",
"naturalbiomes:alpine",
"naturalbiomes:bushland",
"everness:bamboo_forest",
},
y_max = 31000,
y_min = -1,
place_offset_y = 2,
decoration = "fireflies:hidden_firefly",
})
minetest.register_decoration({
name = "fireflies:firefly_low_bamboo_cave",
deco_type = "simple",
place_on = "everness:moss_block",
place_offset_y = 2,
sidelen = 16,
fill_ratio = 0.0065,
biomes = asuna.features.cave.bamboo,
y_max = 0,
y_min = -31000,
place_offset_y = 1,
decoration = "fireflies:hidden_firefly",
})
minetest.register_decoration({
name = "fireflies:firefly_low_dorwinion_cave",
deco_type = "simple",
place_on = "dorwinion:dorwinion_grass",
place_offset_y = 2,
sidelen = 16,
fill_ratio = 0.0065,
biomes = asuna.features.cave.dorwinion,
y_max = 0,
y_min = -31000,
place_offset_y = 1,
decoration = "fireflies:hidden_firefly",
})
end
--[[
Large jungle trees
]]
local chunksize = tonumber(minetest.get_mapgen_setting("chunksize"))
if chunksize >= 5 then
minetest.register_decoration({
name = "default:emergent_jungle_tree",
deco_type = "schematic",
place_on = {
"default:dirt_with_rainforest_litter",
},
sidelen = 80,
noise_params = {
offset = 0.0,
scale = 0.0025,
spread = {x = 200, y = 200, z = 200},
seed = 2685,
octaves = 3,
persist = 0.7
},
biomes = {"rainforest"},
y_max = 30000,
y_min = 1,
schematic = minetest.get_modpath("default") .. "/schematics/emergent_jungle_tree.mts",
flags = "place_center_x, place_center_z",
rotation = "random",
place_offset_y = -4,
})
end
--[[
Hanging vines
]]
local soil_nodes = {}
for _,soil_node in ipairs({
"default:dirt",
"default:dirt_with_grass",
"default:dirt_with_rainforest_litter",
"livingjungle:jungleground",
"livingjungle:leafyjungleground",
"naturalbiomes:alderswamp_litter",
"naturalbiomes:alderswamp_dirt",
"default:tree",
"default:jungletree",
"default:jungleleaves",
}) do
soil_nodes[minetest.get_content_id(soil_node)] = true
end
local cids = {
air = minetest.get_content_id("air"),
vine = minetest.get_content_id("ethereal:vine"),
water = minetest.get_content_id("default:water_source"), -- used for waterfalls below; unrelated to vines
water_flowing = minetest.get_content_id("default:water_flowing"), -- used for cave liquids below; unrelated to vines
lava = minetest.get_content_id("default:lava_source"), -- used for cave liquids below; unrelated to vines
lava_flowing = minetest.get_content_id("default:lava_flowing"), -- used for cave liquids below; unrelated to vines
stone = minetest.get_content_id("default:stone"),
jungleleaves = minetest.get_content_id("default:jungleleaves"),
}
local asuna_hanging_vines_fn = function(mapgen)
-- Get provided values
local pos = mapgen.pos
local va = mapgen.voxelarea
local vdata = mapgen.data
local vparam2 = mapgen.param2
-- Get stride values and set position
local ystride = va.ystride
local zstride = va.zstride
pos = va:index(pos.x,pos.y,pos.z)
-- Scan for dirt or surface nodes for a short distance above the position
for above = 1, 2 do
above = pos + above * ystride
if soil_nodes[vdata[above]] then
pos = above
break
end
end
-- List of cardinal directions relative to the current position
local cardinal = {
pos - 1,
pos + 1,
pos - zstride,
pos + zstride,
}
-- Iterate over cardinal positions and place vines at and below those positions
for i = 1, 4 do
local dir = cardinal[i]
if vdata[dir] == cids.air then
for below = 0, ((dir ^ 2 + (dir + pos) % 3) % 4 + 2) do
below = dir - below * ystride
if vdata[below] == cids.air then
vdata[below] = cids.vine
vparam2[below] = i + 1
else
break
end
end
end
end
end
abdecor.register_advanced_decoration("asuna_hanging_vines_jungletree",{
target = {
place_on = {
"default:jungletree",
},
sidelen = 80,
fill_ratio = 0.3,
biomes = {
"rainforest",
},
y_max = 31000,
y_min = 1,
flags = "all_ceilings",
},
fn = function(mapgen)
-- Get provided values
local pos = mapgen.pos
local va = mapgen.voxelarea
local vdata = mapgen.data
local vparam2 = mapgen.param2
-- Get stride values and set position
local ystride = va.ystride
local zstride = va.zstride
pos = va:index(pos.x,pos.y,pos.z)
-- Check surrounding nodes for jungle leaves
for x = -1, 1 do
for z = -zstride, zstride, zstride do
local lpos = pos + x + z
if vdata[lpos] == cids.jungleleaves and (pos * x + z) % 3 > 0 then
mapgen.pos = va:position(lpos)
asuna_hanging_vines_fn(mapgen)
end
end
end
end,
flags = {
param2 = true,
}
})
abdecor.register_advanced_decoration("asuna_hanging_vines",{
target = {
place_on = {
"group:stone",
"default:dirt",
"default:jungletree",
},
spawn_by = "air",
num_spawn_by = 5,
sidelen = 80,
fill_ratio = 0.3,
biomes = {
"swamp",
"naturalbiomes:alderswamp",
"marsh",
"rainforest",
"jumble",
"livingjungle:jungle",
},
y_max = 31000,
y_min = 1,
flags = "all_ceilings",
},
fn = asuna_hanging_vines_fn,
flags = {
param2 = true,
},
})
--[[
Ocean waterfalls
]]
local wall_stones = {
"default:stone_with_coal",
"default:stone_with_iron",
"default:stone_with_tin",
"default:stone_with_copper",
"default:stone_with_gold",
"default:stone_with_diamond",
"default:stone_with_mese",
}
for node,def in pairs(minetest.registered_nodes) do
if def.groups and def.groups.stone and def.groups.stone > 0 then
table.insert(wall_stones,node)
end
end
local valid_wall_stones = {}
for _,node in ipairs(wall_stones) do
valid_wall_stones[minetest.get_content_id(node)] = true
end
abdecor.register_advanced_decoration("asuna_waterfalls",{
target = {
place_on = "default:water_source",
spawn_by = wall_stones,
num_spawn_by = 3,
sidelen = 80,
fill_ratio = 0.11,
biomes = asuna.biome_groups.shore,
y_max = 1,
y_min = 1,
flags = "liquid_surface",
},
fn = function(mapgen)
-- Get provided values
local pos = mapgen.pos
local va = mapgen.voxelarea
local vdata = mapgen.data
-- Get stride values and adjust position
local ystride = va.ystride
local zstride = va.zstride
pos = va:index(pos.x,pos.y + 1,pos.z)
local too_low_pos = 0
-- Get stone wall direction
local cardinal = {
-1,
-zstride,
1,
zstride,
}
local found_stone = false
local check_wall = nil
for i = 1, 4 do
local wallpos = pos + cardinal[i]
local wallleft = cardinal[i % 4 + 1]
local wallright = cardinal[(i + 2) % 4 + 1]
check_wall = function(pos) -- is the entire row of wall nodes made of stone?
return valid_wall_stones[vdata[pos]] and valid_wall_stones[vdata[pos + wallleft]] and valid_wall_stones[vdata[pos + wallright]] and true or false
end
if check_wall(wallpos) then
pos = wallpos
too_low_pos = pos
found_stone = true
break
end
end
-- Do nothing if no stone wall found
if not found_stone then
return
end
-- Iterate above 'rows' of stone wall until we find something that isn't stone
repeat
pos = pos + ystride
until not check_wall(pos)
-- Set two below stone position to water if higher than one node
local placepos = pos - 2 * ystride
if placepos > too_low_pos then
vdata[placepos] = cids.water
end
end,
flags = {
liquid = true,
},
})
--[[
Cave ceiling liquids
]]
local enclosing_nodes = valid_wall_stones
abdecor.register_advanced_decoration("asuna_cave_liquids",{
target = {
place_on = {
"group:stone",
},
sidelen = 80,
spawn_by = {
"group:stone",
},
num_spawn_by = 8,
fill_ratio = 0.000015,
y_max = -30,
y_min = -31000,
flags = "all_ceilings",
},
fn = function(mapgen)
-- Get provided values
local va = mapgen.voxelarea
local vdata = mapgen.data
local vparam2 = mapgen.param2
local pos = mapgen.pos
-- Get stride values and set position
local ystride = va.ystride
local zstride = va.zstride
local pos = va:index(pos.x,pos.y,pos.z)
-- Liquid must be enclosed to its sides and above
for _,adjacent in ipairs({
ystride,
1,
-1,
zstride,
-zstride,
}) do
if not enclosing_nodes[vdata[pos + adjacent]] then
return -- liquid is not fully enclosed
end
end
-- Liquid must have sufficient clearance below
-- Scanning from bottom up should typically fail faster than top down
for below = pos - ystride * 8, pos - ystride, ystride do
if vdata[below] ~= minetest.CONTENT_AIR then
return -- not enough space between ceiling and ground
end
end
-- Fill the position and all air below with liquid based on climate + bias
-- Dry/hot climates are more likely to be lava, vice-versa with water
local liquid = (function()
local heatmap = minetest.get_mapgen_object("heatmap") or {}
local humiditymap = minetest.get_mapgen_object("humiditymap") or {}
local pos2d = mapgen.index2d(mapgen.pos)
local heat = heatmap[pos2d] or 50
local humidity = humiditymap[pos2d] or 50
local climate = 50 + (heat / 2 - 25) - (humidity / 2 - 25)
local pos_random = (pos ^ 2 + pos) % 38 * (pos % 2 == 0 and 1 or -1) + climate -- not actually random but good enough
return pos_random > 56 and {cids.lava,cids.lava_flowing} or {cids.water,cids.water_flowing} -- bias in favor of water
end)()
vdata[pos] = liquid[1]
pos = pos - ystride
while vdata[pos] == minetest.CONTENT_AIR do
vdata[pos] = liquid[2]
vparam2[pos] = 15
pos = pos - ystride
end
end,
flags = {
liquid = true,
param2 = true,
},
})
end)