-------------- -- Spawning -- -------------- local random = math.random local path = minetest.get_modpath("animalia") local storage = dofile(path .. "/api/storage.lua") animalia.spawn_points = storage.spawn_points -- Get Biomes -- local chicken_biomes = {} local frog_biomes = {} local pig_biomes = {} local function insert_all(tbl, tbl2) for i = 1, #tbl2 do table.insert(tbl, tbl2[i]) end end minetest.register_on_mods_loaded(function() insert_all(chicken_biomes, animalia.registered_biome_groups["grassland"].biomes) insert_all(chicken_biomes, animalia.registered_biome_groups["tropical"].biomes) insert_all(pig_biomes, animalia.registered_biome_groups["temperate"].biomes) insert_all(pig_biomes, animalia.registered_biome_groups["boreal"].biomes) insert_all(frog_biomes, animalia.registered_biome_groups["swamp"].biomes) insert_all(frog_biomes, animalia.registered_biome_groups["tropical"].biomes) end) creatura.register_mob_spawn("animalia:bat", { chance = 2, min_radius = 4, max_radius = 16, min_light = 0, min_height = -512, max_height = 0, min_group = 3, max_group = 5, biomes = animalia.registered_biome_groups["cave"].biomes, spawn_in_nodes = true, nodes = {"air", "ignore"} }) creatura.register_mob_spawn("animalia:chicken", { chance = 3, min_group = 3, max_group = 5, biomes = chicken_biomes }) creatura.register_mob_spawn("animalia:cow", { chance = 3, min_group = 3, max_group = 4, biomes = animalia.registered_biome_groups["grassland"].biomes }) creatura.register_mob_spawn("animalia:frog", { chance = 2, min_radius = 4, max_radius = 16, min_light = 0, min_height = -32, max_height = 8, min_group = 2, max_group = 6, biomes = frog_biomes, spawn_cluster = true, spawn_in_nodes = true, nodes = {"default:water_source"}, }) creatura.register_mob_spawn("animalia:horse", { chance = 3, min_group = 4, max_group = 5, biomes = animalia.registered_biome_groups["grassland"].biomes }) creatura.register_mob_spawn("animalia:pig", { chance = 3, min_group = 2, max_group = 4, biomes = pig_biomes }) creatura.register_mob_spawn("animalia:reindeer", { chance = 4, min_group = 6, max_group = 12, biomes = animalia.registered_biome_groups["boreal"].biomes }) creatura.register_mob_spawn("animalia:sheep", { chance = 3, min_group = 3, max_group = 6, biomes = animalia.registered_biome_groups["grassland"].biomes }) creatura.register_mob_spawn("animalia:turkey", { chance = 2, min_group = 3, max_group = 4, biomes = animalia.registered_biome_groups["boreal"].biomes }) creatura.register_mob_spawn("animalia:wolf", { chance = 3, min_group = 2, max_group = 3, biomes = animalia.registered_biome_groups["boreal"].biomes }) creatura.register_mob_spawn("animalia:bird", { chance = 1, min_light = 0, min_group = 12, max_group = 16, biomes = animalia.registered_biome_groups["common"].biomes, spawn_cluster = true, nodes = {"group:leaves"} }) creatura.register_on_spawn("animalia:bird", function(self, pos) local node = minetest.get_node(pos) if node.name == "air" then minetest.set_node(pos, {name = "animalia:nest_song_bird"}) self.home_position = self:memorize("home_position", pos) self.despawn_after = self:memorize("despawn_after", nil) else local nodes = minetest.find_nodes_in_area_under_air( {x = pos.x - 3, y = pos.y - 3, z = pos.z - 3}, {x = pos.x + 3, y = pos.y + 7, z = pos.z + 3}, "group:leaves" ) if nodes[1] then pos = nodes[1] minetest.set_node({x = pos.x, y = pos.y + 1, z = pos.z}, {name = "animalia:nest_song_bird"}) self.home_position = self:memorize("home_position", {x = pos.x, y = pos.y + 1, z = pos.z}) self.despawn_after = self:memorize("despawn_after", nil) end end end) creatura.register_mob_spawn("animalia:tropical_fish", { chance = 3, min_height = -128, max_height = 256, min_group = 8, max_group = 12, spawn_cluster = true, spawn_in_nodes = true, nodes = {"default:water_source"} }) --------------------- -- Mapgen Spawning -- --------------------- local function is_value_in_table(tbl, val) for _, v in pairs(tbl) do if v == val then return true end end return false end local function get_biome_name(pos) if not pos then return end return minetest.get_biome_name(minetest.get_biome_data(pos).biome) end local function dist_to_nearest_player(pos) local dist for _, player in pairs(minetest.get_connected_players()) do local player_pos = player:get_pos() if player_pos and (not dist or dist > vector.distance(pos, player_pos)) then dist = vector.distance(pos, player_pos) end end return dist or 100 end local function get_spawnable_mobs(pos) local biome = get_biome_name(pos) if not biome then return end local spawnable = {} for k, v in pairs(creatura.registered_mob_spawns) do if (not v.biomes or is_value_in_table(v.biomes, biome)) and k:match("^animalia:") and not v.spawn_in_nodes then table.insert(spawnable, k) end end return spawnable end local mapgen_spawning = minetest.settings:get_bool("animalia_mapgen_spawning") or true animalia.chunks_since_last_spawn = 0 local chunk_spawn_add_int = tonumber(minetest.settings:get("chunk_spawn_add_int")) or 6 animalia.spawn_queue = {} local c_air = minetest.get_content_id("air") minetest.register_on_generated(function(minp, maxp) if not mapgen_spawning then return end animalia.chunks_since_last_spawn = animalia.chunks_since_last_spawn + 1 local max_y = maxp.y local min_x = minp.x local max_x = maxp.x local min_z = minp.z local max_z = maxp.z local vm, emin, emax = minetest.get_mapgen_object("voxelmanip") local area = VoxelArea:new{MinEdge=emin, MaxEdge=emax} local data = vm:get_data() local spawn_added = false for xcen = min_x + 8, max_x - 7, 8 do if spawn_added then break end for zcen = min_z + 8, max_z - 7, 8 do local surface = false -- y of above surface node for y = max_y, 2, -1 do local vi = area:index(xcen, y, zcen) local c_node = data[vi] if not c_node then break end local c_name = minetest.get_name_from_content_id(c_node) local c_def = minetest.registered_nodes[c_name] if y == max_y and c_node ~= c_air then -- if top node solid break elseif minetest.get_item_group(c_name, "leaves") > 0 then break elseif c_def.walkable then surface = y + 1 break end end if animalia.chunks_since_last_spawn > chunk_spawn_add_int and surface then local center = { x = xcen, y = surface, z = zcen, } local spawnable_mobs = get_spawnable_mobs(center) if spawnable_mobs and #spawnable_mobs > 0 then local mob = spawnable_mobs[random(#spawnable_mobs)] local spawn_def = creatura.registered_mob_spawns[mob] table.insert(animalia.spawn_queue, {pos = center, mob = mob, group = random(spawn_def.min_group, spawn_def.max_group)}) table.insert(animalia.spawn_points, center) end spawn_added = true animalia.chunks_since_last_spawn = 0 end end end end) local respawn_interval = 15 minetest.register_globalstep(function(dtime) respawn_interval = respawn_interval - dtime if respawn_interval <= 0 then if #animalia.spawn_points > 0 then for i = 1, #animalia.spawn_points do local point = animalia.spawn_points[i] if dist_to_nearest_player(point) < 48 and minetest.get_node_or_nil(point) then local spawnable_mobs = get_spawnable_mobs(point) if spawnable_mobs and #spawnable_mobs > 0 then local mob = spawnable_mobs[random(#spawnable_mobs)] local objects = minetest.get_objects_inside_radius(point, 32) local spawn = true if #objects > 0 then for _, object in ipairs(objects) do local ent = object and object:get_luaentity() if ent and ent.name:find("animalia:") then spawn = false break end end end if spawn then local spawn_def = creatura.registered_mob_spawns[mob] table.insert(animalia.spawn_queue, {pos = point, mob = mob, group = random(spawn_def.min_group, spawn_def.max_group)}) end end end end end respawn_interval = 15 end end) local chunk_spawn_queue_int = tonumber(minetest.settings:get("chunk_spawn_queue_int")) or 16 local function spawn_queued() if not mapgen_spawning then return end local queue = animalia.spawn_queue if #queue > 0 then for i = #queue, 1, -1 do if queue[i].mob then local pos = queue[i].pos if queue[i].group > 4 or creatura.registered_mob_spawns[queue[i].mob].spawn_cluster then pos = creatura.get_ground_level(pos, 4) minetest.add_node(pos, {name = "creatura:spawn_node"}) local meta = minetest.get_meta(pos) meta:set_string("mob", queue[i].mob) meta:set_string("cluster", queue[i].group) else for _ = 1, queue[i].group do pos = { x = pos.x + random(-3, 3), y = pos.y, z = pos.z + random(-3, 3) } pos = creatura.get_ground_level(pos, 4) minetest.add_node(pos, {name = "creatura:spawn_node"}) local meta = minetest.get_meta(pos) meta:set_string("mob", queue[i].mob) end end end table.remove(animalia.spawn_queue, i) end end minetest.after(chunk_spawn_queue_int, spawn_queued) end minetest.after(chunk_spawn_queue_int, spawn_queued)