-------------- -- 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 wolf_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"}, send_debug = true }) 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 = 4, min_light = 0, min_group = 12, max_group = 16, biomes = animalia.registered_biome_groups["common"].biomes, spawn_cluster = true, spawn_in_nodes = true, nodes = {"air", "ignore"} }) 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 vec_raise(v, n) return {x = v.x, y = v.y + n, z = v.z} end function is_value_in_table(tbl, val) for _, v in pairs(tbl) do if v == val then return true end end return false end function get_biome_name(pos) if not pos then return end return minetest.get_biome_name(minetest.get_biome_data(pos).biome) end function get_ground_level(pos) local node = minetest.get_node(pos) local node_def = minetest.registered_nodes[node.name] local height = 0 while node_def.walkable and height < 4 do height = height + 1 node = minetest.get_node(vec_raise(pos, height)) node_def = minetest.registered_nodes[node.name] end return vec_raise(pos, height) 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 = {} 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 heightmap = minetest.get_mapgen_object("heightmap") if not heightmap then return end local pos = { x = minp.x + math.floor((maxp.x - minp.x) / 2), y = minp.y, z = minp.z + math.floor((maxp.z - minp.z) / 2) } local hm_i = (pos.x - minp.x + 1) + (((pos.z - minp.z)) * 80) pos.y = heightmap[hm_i] if animalia.chunks_since_last_spawn > chunk_spawn_add_int and pos.y > 0 then local heightmap = minetest.get_mapgen_object("heightmap") if not heightmap then return end local center = { x = math.floor(minp.x + ((maxp.x - minp.x) * 0.5) + 0.5), y = minp.y, z = math.floor(minp.z + ((maxp.z - minp.z) * 0.5) + 0.5), } local light = minetest.get_natural_light(center) while center.y < maxp.y and light < 10 do center.y = center.y + 1 light = minetest.get_natural_light(center) end local spawnable_mobs = get_spawnable_mobs(center) if spawnable_mobs then local mob = spawnable_mobs[random(#spawnable_mobs)] table.insert(animalia.spawn_queue, {pos = center, mob = mob, group = random(3, 4)}) table.insert(animalia.spawn_points, center) end animalia.chunks_since_last_spawn = 0 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 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 i = 1, #objects do local object = objects[i] if object:get_luaentity() and object:get_luaentity().name:find("animalia:") then spawn = false break end end end if spawn then table.insert(animalia.spawn_queue, {pos = point, mob = mob, group = random(3, 4)}) 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 for _ = 1, queue[i].group do pos = { x = pos.x + random(-3, 3), y = pos.y, z = pos.z + random(-3, 3) } pos = get_ground_level(pos) minetest.add_node(pos, {name = "creatura:spawn_node"}) local meta = minetest.get_meta(pos) meta:set_string("mob", queue[i].mob) 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)