2022-01-15 23:12:56 -05:00

149 lines
5.1 KiB
Lua

--localize libraries
local v_dist, v_multiply, v_add = vector.distance, vector.multiply, vector.add
local m_random, m_pi = math.random, math.pi
--varibles
local abr = minetest.get_mapgen_setting('active_block_range') or 4
local mopb = minetest.settings:get("max_objects_per_block") or 64
--[[
{
biomes - table, or leave blank for all
rarity - number between 0 and 1 for spawning (required)
y_min - min spawn height
y_max - max spawn height
light_min - min light level
light_max - max light level
light_type - (total) changes light calc from natural to natural+light sources
useful for hostile mobs where you want them to be switched on or off by torches
cluster - if present, amount of times another mob is attempted to be spawned
}
]]
--make me local
local mob_biomes = {}
local mob_spawndata = {}
--fill data cache. technically this currently doesnt account for dynamically registered mobs
minetest.register_on_mods_loaded(function()
for biome, _ in pairs(minetest.registered_biomes) do
mob_biomes[biome] = {}
end
for ename, edef in pairs(minetest.registered_entities) do
if edef._spawning and edef._spawning.biomes then
mob_spawndata[ename] = table.copy(edef._spawning)
for _, biome in pairs(edef._spawning.biomes) do
if mob_biomes[biome] then table.insert(mob_biomes[biome], ename) end
end
elseif edef._spawning then
mob_spawndata[ename] = table.copy(edef._spawning)
for biome, _ in pairs(mob_biomes) do
table.insert(mob_biomes[biome], ename)
end
end
end
end)
function find_nodes(p_min, p_max)
local vm = minetest.get_voxel_manip()
local emin, emax = vm:read_from_map(p_min, p_max)
local area = VoxelArea:new{MinEdge = emin, MaxEdge = emax}
local data = vm:get_data()
local c_air = minetest.get_content_id("air")
local pos_list = {}
for vi in area:iterp(emin, emax) do
local def = minetest.registered_items[minetest.get_name_from_content_id(data[vi])]
local pos = area:position(vi)
if def.walkable and def.groups and not def.groups.spawn_blacklist
and data[area:index(pos.x, pos.y + 1, pos.z)] == c_air then
table.insert(pos_list, vector.new(pos.x, pos.y + 1, pos.z))
end
end
return pos_list
end
local function spawn_mob(stable, cmob, ccount)
local index = m_random(#stable)
local pos = stable[index]
local light = minetest.get_natural_light(pos)
local biome = minetest.get_biome_name(minetest.get_biome_data(pos).biome)
local mob = cmob or mob_biomes[biome][m_random(#mob_biomes[biome])]
if mob_spawndata.light_type and mob_spawndata.light_type == "total" then
light = minetest.get_node_light(pos)
end
--minetest.chat_send_all(mob)
if (mob_spawndata[mob].y_min or -33000) > pos.y or (mob_spawndata[mob].y_max or 33000) < pos.y then
return
elseif (mob_spawndata[mob].light_min or 0) > light or (mob_spawndata[mob].light_max or 15) < light then
return
elseif m_random() > mob_spawndata[mob].rarity then
return
end
--minetest.chat_send_all("sucess: " .. (ccount or 0))
local ent = minetest.add_entity(pos, mob)
if minetest.registered_entities[mob]._on_spawn then
minetest.registered_entities[mob]._on_spawn(ent)
end
--use recursion for cluster spawning
table.remove(stable, index)
if not ccount and mob_spawndata[mob].cluster then
ccount = mob_spawndata[mob].cluster - 1
spawn_mob(stable, mob, ccount)
elseif ccount and ccount-1 > 0 then
ccount = ccount - 1
spawn_mob(stable, cmob, ccount)
end
end
local function spawn_step()
--slow down mobs spawning overall, save calc time
if m_random() > 0.5 then
--minetest.chat_send_all("nuked")
minetest.after(11, spawn_step)
return
end
--minetest.chat_send_all("fired")
for _, player in pairs(minetest.get_connected_players()) do
local p_pos = player:get_pos()
local m_count, e_count = 0, 0
local y_mod = p_pos.y < -4 and 0.5 or 1
local yaw = (m_random(0, 360) - 180) / 180 * m_pi
local l_dist = abr*16*y_mod*(m_random(0,0.5)+0.5)
local dist = v_multiply(minetest.yaw_to_dir(yaw),l_dist)
local s_pos = v_add(p_pos, dist)
local s_table = find_nodes(
vector.new(s_pos.x-10, s_pos.y-20, s_pos.z-10),
vector.new(s_pos.x+10, s_pos.y+20, s_pos.z+10)
)
for _, ent in pairs(minetest.luaentities) do
--check for pos to prevent crash
if ent.object:get_pos() and v_dist(ent.object:get_pos(), p_pos) <= (abr*16*2)+5 then
e_count = e_count + 1
if ent.brainfunc then
m_count = m_count + 1
end
end
end
--make sure not to many mobs/ents in area
if #s_table > 0 and mopb*.8 > e_count and (mopb*.5) > m_count then
spawn_mob(s_table)
end
end
minetest.after(11, spawn_step)
end
minetest.after(11, spawn_step)