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)