Merge pull request 'Adopt mob spawning from mcl5' (#2120) from mcl5-spawning into master
Reviewed-on: https://git.minetest.land/MineClone2/MineClone2/pulls/2120
This commit is contained in:
commit
ea2b53b231
|
@ -3,22 +3,57 @@ local get_node = minetest.get_node
|
||||||
local get_item_group = minetest.get_item_group
|
local get_item_group = minetest.get_item_group
|
||||||
local get_node_light = minetest.get_node_light
|
local get_node_light = minetest.get_node_light
|
||||||
local find_nodes_in_area_under_air = minetest.find_nodes_in_area_under_air
|
local find_nodes_in_area_under_air = minetest.find_nodes_in_area_under_air
|
||||||
local new_vector = vector.new
|
|
||||||
local math_random = math.random
|
|
||||||
local get_biome_name = minetest.get_biome_name
|
local get_biome_name = minetest.get_biome_name
|
||||||
local max = math.max
|
|
||||||
local get_objects_inside_radius = minetest.get_objects_inside_radius
|
local get_objects_inside_radius = minetest.get_objects_inside_radius
|
||||||
local vector_distance = vector.distance
|
local get_connected_players = minetest.get_connected_players
|
||||||
|
local minetest_get_perlin = minetest.get_perlin
|
||||||
|
|
||||||
|
local math_random = math.random
|
||||||
|
local math_floor = math.floor
|
||||||
|
local math_ceil = math.ceil
|
||||||
|
local math_cos = math.cos
|
||||||
|
local math_sin = math.sin
|
||||||
|
local math_round = function(x) return (x > 0) and math_floor(x + 0.5) or math_ceil(x - 0.5) end
|
||||||
|
|
||||||
|
--local vector_distance = vector.distance
|
||||||
|
local vector_new = vector.new
|
||||||
|
local vector_floor = vector.floor
|
||||||
|
|
||||||
|
local table_copy = table.copy
|
||||||
|
local table_remove = table.remove
|
||||||
|
|
||||||
|
local pairs = pairs
|
||||||
|
|
||||||
-- range for mob count
|
-- range for mob count
|
||||||
local aoc_range = 32
|
local aoc_range = 32
|
||||||
--[[
|
|
||||||
|
|
||||||
THIS IS THE BIG LIST OF ALL BIOMES - used for programming/updating mobs
|
--do mobs spawn?
|
||||||
|
local mobs_spawn = minetest.settings:get_bool("mobs_spawn", true) ~= false
|
||||||
|
|
||||||
|
|
||||||
|
local noise_params = {
|
||||||
|
offset = 0,
|
||||||
|
scale = 3,
|
||||||
|
spread = {
|
||||||
|
x = 301,
|
||||||
|
y = 50,
|
||||||
|
z = 304,
|
||||||
|
},
|
||||||
|
seed = 100,
|
||||||
|
octaves = 3,
|
||||||
|
persistence = 0.5,
|
||||||
|
}
|
||||||
|
|
||||||
|
-- THIS IS THE BIG LIST OF ALL BIOMES - used for programming/updating mobs
|
||||||
|
-- Also used for missing parameter
|
||||||
|
-- Please update the list when adding new biomes!
|
||||||
|
|
||||||
|
local list_of_all_biomes = {
|
||||||
|
|
||||||
|
-- underground:
|
||||||
|
|
||||||
underground:
|
|
||||||
"FlowerForest_underground",
|
"FlowerForest_underground",
|
||||||
"JungleEdge_underground",local spawning_position = spawning_position_list[math.random(1,#spawning_position_list)]
|
"JungleEdge_underground",
|
||||||
"ColdTaiga_underground",
|
"ColdTaiga_underground",
|
||||||
"IcePlains_underground",
|
"IcePlains_underground",
|
||||||
"IcePlainsSpikes_underground",
|
"IcePlainsSpikes_underground",
|
||||||
|
@ -29,7 +64,8 @@ underground:
|
||||||
"ExtremeHillsM_underground",
|
"ExtremeHillsM_underground",
|
||||||
"JungleEdgeM_underground",
|
"JungleEdgeM_underground",
|
||||||
|
|
||||||
ocean:
|
-- ocean:
|
||||||
|
|
||||||
"RoofedForest_ocean",
|
"RoofedForest_ocean",
|
||||||
"JungleEdgeM_ocean",
|
"JungleEdgeM_ocean",
|
||||||
"BirchForestM_ocean",
|
"BirchForestM_ocean",
|
||||||
|
@ -91,13 +127,15 @@ ocean:
|
||||||
"Taiga_deep_ocean",
|
"Taiga_deep_ocean",
|
||||||
"JungleM_ocean",
|
"JungleM_ocean",
|
||||||
|
|
||||||
water or beach?
|
-- water or beach?
|
||||||
|
|
||||||
"MesaPlateauFM_sandlevel",
|
"MesaPlateauFM_sandlevel",
|
||||||
"MesaPlateauF_sandlevel",
|
"MesaPlateauF_sandlevel",
|
||||||
"MesaBryce_sandlevel",
|
"MesaBryce_sandlevel",
|
||||||
"Mesa_sandlevel",
|
"Mesa_sandlevel",
|
||||||
|
|
||||||
beach:
|
-- beach:
|
||||||
|
|
||||||
"FlowerForest_beach",
|
"FlowerForest_beach",
|
||||||
"Forest_beach",
|
"Forest_beach",
|
||||||
"StoneBeach",
|
"StoneBeach",
|
||||||
|
@ -112,11 +150,13 @@ beach:
|
||||||
"JungleM_shore",
|
"JungleM_shore",
|
||||||
"Jungle_shore",
|
"Jungle_shore",
|
||||||
|
|
||||||
dimension biome:
|
-- dimension biome:
|
||||||
|
|
||||||
"Nether",
|
"Nether",
|
||||||
"End",
|
"End",
|
||||||
|
|
||||||
Overworld regular:
|
-- Overworld regular:
|
||||||
|
|
||||||
"Mesa",
|
"Mesa",
|
||||||
"FlowerForest",
|
"FlowerForest",
|
||||||
"Swampland",
|
"Swampland",
|
||||||
|
@ -149,32 +189,16 @@ Overworld regular:
|
||||||
"MesaBryce",
|
"MesaBryce",
|
||||||
"JungleEdge",
|
"JungleEdge",
|
||||||
"SavannaM",
|
"SavannaM",
|
||||||
]]--
|
}
|
||||||
|
|
||||||
|
-- count how many mobs are in an area
|
||||||
|
local function count_mobs(pos)
|
||||||
|
|
||||||
local mobs_spawn = minetest.settings:get_bool("mobs_spawn", true) ~= false
|
|
||||||
-- count how many mobs of one type are inside an area
|
|
||||||
|
|
||||||
local count_mobs = function(pos,mobtype)
|
|
||||||
local num = 0
|
local num = 0
|
||||||
local objs = get_objects_inside_radius(pos, aoc_range)
|
for _,object in pairs(get_objects_inside_radius(pos, aoc_range)) do
|
||||||
for n = 1, #objs do
|
if object and object:get_luaentity() and object:get_luaentity()._cmi_is_mob then
|
||||||
local obj = objs[n]:get_luaentity()
|
|
||||||
if obj and obj.name and obj._cmi_is_mob then
|
|
||||||
-- count hostile mobs only
|
|
||||||
if mobtype == "hostile" then
|
|
||||||
if obj.spawn_class == "hostile" then
|
|
||||||
num = num + 1
|
|
||||||
end
|
|
||||||
-- count passive mobs only
|
|
||||||
else
|
|
||||||
num = num + 1
|
num = num + 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
return num
|
return num
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -215,11 +239,73 @@ WARNING: BIOME INTEGRATION NEEDED -> How to get biome through lua??
|
||||||
|
|
||||||
--this is where all of the spawning information is kept
|
--this is where all of the spawning information is kept
|
||||||
local spawn_dictionary = {}
|
local spawn_dictionary = {}
|
||||||
|
local summary_chance = 0
|
||||||
|
|
||||||
|
function mobs:spawn_setup(def)
|
||||||
|
if not mobs_spawn then return end
|
||||||
|
|
||||||
|
if not def then
|
||||||
|
minetest.log("warning", "Empty mob spawn setup definition")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local name = def.name
|
||||||
|
if not name then
|
||||||
|
minetest.log("warning", "Missing mob name")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local dimension = def.dimension or "overworld"
|
||||||
|
local type_of_spawning = def.type_of_spawning or "ground"
|
||||||
|
local biomes = def.biomes or list_of_all_biomes
|
||||||
|
local min_light = def.min_light or 0
|
||||||
|
local max_light = def.max_light or (minetest.LIGHT_MAX + 1)
|
||||||
|
local chance = def.chance or 1000
|
||||||
|
local aoc = def.aoc or aoc_range
|
||||||
|
local min_height = def.min_height or mcl_mapgen.overworld.min
|
||||||
|
local max_height = def.max_height or mcl_mapgen.overworld.max
|
||||||
|
local day_toggle = def.day_toggle
|
||||||
|
local on_spawn = def.on_spawn
|
||||||
|
local check_position = def.check_position
|
||||||
|
|
||||||
|
-- chance/spawn number override in minetest.conf for registered mob
|
||||||
|
local numbers = minetest.settings:get(name)
|
||||||
|
if numbers then
|
||||||
|
numbers = numbers:split(",")
|
||||||
|
chance = tonumber(numbers[1]) or chance
|
||||||
|
aoc = tonumber(numbers[2]) or aoc
|
||||||
|
if chance == 0 then
|
||||||
|
minetest.log("warning", string.format("[mobs] %s has spawning disabled", name))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
minetest.log("action", string.format("[mobs] Chance setting for %s changed to %s (total: %s)", name, chance, aoc))
|
||||||
|
end
|
||||||
|
|
||||||
|
if chance < 1 then
|
||||||
|
chance = 1
|
||||||
|
minetest.log("warning", "Chance shouldn't be less than 1 (mob name: " .. name ..")")
|
||||||
|
end
|
||||||
|
|
||||||
|
spawn_dictionary[#spawn_dictionary + 1] = {
|
||||||
|
name = name,
|
||||||
|
dimension = dimension,
|
||||||
|
type_of_spawning = type_of_spawning,
|
||||||
|
biomes = biomes,
|
||||||
|
min_light = min_light,
|
||||||
|
max_light = max_light,
|
||||||
|
chance = chance,
|
||||||
|
aoc = aoc,
|
||||||
|
min_height = min_height,
|
||||||
|
max_height = max_height,
|
||||||
|
day_toggle = day_toggle,
|
||||||
|
check_position = check_position,
|
||||||
|
on_spawn = on_spawn,
|
||||||
|
}
|
||||||
|
summary_chance = summary_chance + chance
|
||||||
|
end
|
||||||
|
|
||||||
function mobs:spawn_specific(name, dimension, type_of_spawning, biomes, min_light, max_light, interval, chance, aoc, min_height, max_height, day_toggle, on_spawn)
|
function mobs:spawn_specific(name, dimension, type_of_spawning, biomes, min_light, max_light, interval, chance, aoc, min_height, max_height, day_toggle, on_spawn)
|
||||||
|
|
||||||
--print(dump(biomes))
|
|
||||||
|
|
||||||
-- Do mobs spawn at all?
|
-- Do mobs spawn at all?
|
||||||
if not mobs_spawn then
|
if not mobs_spawn then
|
||||||
return
|
return
|
||||||
|
@ -238,180 +324,7 @@ function mobs:spawn_specific(name, dimension, type_of_spawning, biomes, min_ligh
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
minetest.log("action",
|
minetest.log("action", string.format("[mobs] Chance setting for %s changed to %s (total: %s)", name, chance, aoc))
|
||||||
string.format("[mobs] Chance setting for %s changed to %s (total: %s)", name, chance, aoc))
|
|
||||||
end
|
|
||||||
|
|
||||||
--[[
|
|
||||||
local spawn_action
|
|
||||||
spawn_action = function(pos, node, active_object_count, active_object_count_wider, name)
|
|
||||||
|
|
||||||
local orig_pos = table.copy(pos)
|
|
||||||
-- is mob actually registered?
|
|
||||||
if not mobs.spawning_mobs[name]
|
|
||||||
or not minetest.registered_entities[name] then
|
|
||||||
minetest.log("warning", "Mob spawn of "..name.." failed, unknown entity or mob is not registered for spawning!")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- additional custom checks for spawning mob
|
|
||||||
if mobs:spawn_abm_check(pos, node, name) == true then
|
|
||||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, ABM check rejected!")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- count nearby mobs in same spawn class
|
|
||||||
local entdef = minetest.registered_entities[name]
|
|
||||||
local spawn_class = entdef and entdef.spawn_class
|
|
||||||
if not spawn_class then
|
|
||||||
if entdef.type == "monster" then
|
|
||||||
spawn_class = "hostile"
|
|
||||||
else
|
|
||||||
spawn_class = "passive"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local in_class_cap = count_mobs(pos, "!"..spawn_class) < MOB_CAP[spawn_class]
|
|
||||||
-- do not spawn if too many of same mob in area
|
|
||||||
if active_object_count_wider >= max_per_block -- large-range mob cap
|
|
||||||
or (not in_class_cap) -- spawn class mob cap
|
|
||||||
or count_mobs(pos, name) >= aoc then -- per-mob mob cap
|
|
||||||
-- too many entities
|
|
||||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, too crowded!")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- if toggle set to nil then ignore day/night check
|
|
||||||
if day_toggle ~= nil then
|
|
||||||
|
|
||||||
local tod = (minetest.get_timeofday() or 0) * 24000
|
|
||||||
|
|
||||||
if tod > 4500 and tod < 19500 then
|
|
||||||
-- daylight, but mob wants night
|
|
||||||
if day_toggle == false then
|
|
||||||
-- mob needs night
|
|
||||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, mob needs light!")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- night time but mob wants day
|
|
||||||
if day_toggle == true then
|
|
||||||
-- mob needs day
|
|
||||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, mob needs daylight!")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- spawn above node
|
|
||||||
pos.y = pos.y + 1
|
|
||||||
|
|
||||||
-- only spawn away from player
|
|
||||||
local objs = minetest.get_objects_inside_radius(pos, 24)
|
|
||||||
|
|
||||||
for n = 1, #objs do
|
|
||||||
|
|
||||||
if objs[n]:is_player() then
|
|
||||||
-- player too close
|
|
||||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, player too close!")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- mobs cannot spawn in protected areas when enabled
|
|
||||||
if not spawn_protected
|
|
||||||
and minetest.is_protected(pos, "") then
|
|
||||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, position is protected!")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- are we spawning within height limits?
|
|
||||||
if pos.y > max_height
|
|
||||||
or pos.y < min_height then
|
|
||||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, out of height limit!")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- are light levels ok?
|
|
||||||
local light = minetest.get_node_light(pos)
|
|
||||||
if not light
|
|
||||||
or light > max_light
|
|
||||||
or light < min_light then
|
|
||||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, bad light!")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- do we have enough space to spawn mob?
|
|
||||||
local ent = minetest.registered_entities[name]
|
|
||||||
local width_x = max(1, math.ceil(ent.collisionbox[4] - ent.collisionbox[1]))
|
|
||||||
local min_x, max_x
|
|
||||||
if width_x % 2 == 0 then
|
|
||||||
max_x = math.floor(width_x/2)
|
|
||||||
min_x = -(max_x-1)
|
|
||||||
else
|
|
||||||
max_x = math.floor(width_x/2)
|
|
||||||
min_x = -max_x
|
|
||||||
end
|
|
||||||
|
|
||||||
local width_z = max(1, math.ceil(ent.collisionbox[6] - ent.collisionbox[3]))
|
|
||||||
local min_z, max_z
|
|
||||||
if width_z % 2 == 0 then
|
|
||||||
max_z = math.floor(width_z/2)
|
|
||||||
min_z = -(max_z-1)
|
|
||||||
else
|
|
||||||
max_z = math.floor(width_z/2)
|
|
||||||
min_z = -max_z
|
|
||||||
end
|
|
||||||
|
|
||||||
local max_y = max(0, math.ceil(ent.collisionbox[5] - ent.collisionbox[2]) - 1)
|
|
||||||
|
|
||||||
for y = 0, max_y do
|
|
||||||
for x = min_x, max_x do
|
|
||||||
for z = min_z, max_z do
|
|
||||||
local pos2 = {x = pos.x+x, y = pos.y+y, z = pos.z+z}
|
|
||||||
if minetest.registered_nodes[node_ok(pos2).name].walkable == true then
|
|
||||||
-- inside block
|
|
||||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, too little space!")
|
|
||||||
if ent.spawn_small_alternative ~= nil and (not minetest.registered_nodes[node_ok(pos).name].walkable) then
|
|
||||||
minetest.log("info", "Trying to spawn smaller alternative mob: "..ent.spawn_small_alternative)
|
|
||||||
spawn_action(orig_pos, node, active_object_count, active_object_count_wider, ent.spawn_small_alternative)
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- tweak X/Y/Z spawn pos
|
|
||||||
if width_x % 2 == 0 then
|
|
||||||
pos.x = pos.x + 0.5
|
|
||||||
end
|
|
||||||
if width_z % 2 == 0 then
|
|
||||||
pos.z = pos.z + 0.5
|
|
||||||
end
|
|
||||||
pos.y = pos.y - 0.5
|
|
||||||
|
|
||||||
local mob = minetest.add_entity(pos, name)
|
|
||||||
minetest.log("action", "Mob spawned: "..name.." at "..minetest.pos_to_string(pos))
|
|
||||||
|
|
||||||
if on_spawn then
|
|
||||||
|
|
||||||
local ent = mob:get_luaentity()
|
|
||||||
|
|
||||||
on_spawn(ent, pos)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function spawn_abm_action(pos, node, active_object_count, active_object_count_wider)
|
|
||||||
spawn_action(pos, node, active_object_count, active_object_count_wider, name)
|
|
||||||
end
|
|
||||||
]]--
|
|
||||||
|
|
||||||
local entdef = minetest.registered_entities[name]
|
|
||||||
local spawn_class
|
|
||||||
if entdef.type == "monster" then
|
|
||||||
spawn_class = "hostile"
|
|
||||||
else
|
|
||||||
spawn_class = "passive"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--load information into the spawn dictionary
|
--load information into the spawn dictionary
|
||||||
|
@ -423,106 +336,34 @@ function mobs:spawn_specific(name, dimension, type_of_spawning, biomes, min_ligh
|
||||||
spawn_dictionary[key]["biomes"] = biomes
|
spawn_dictionary[key]["biomes"] = biomes
|
||||||
spawn_dictionary[key]["min_light"] = min_light
|
spawn_dictionary[key]["min_light"] = min_light
|
||||||
spawn_dictionary[key]["max_light"] = max_light
|
spawn_dictionary[key]["max_light"] = max_light
|
||||||
spawn_dictionary[key]["interval"] = interval
|
|
||||||
spawn_dictionary[key]["chance"] = chance
|
spawn_dictionary[key]["chance"] = chance
|
||||||
spawn_dictionary[key]["aoc"] = aoc
|
spawn_dictionary[key]["aoc"] = aoc
|
||||||
spawn_dictionary[key]["min_height"] = min_height
|
spawn_dictionary[key]["min_height"] = min_height
|
||||||
spawn_dictionary[key]["max_height"] = max_height
|
spawn_dictionary[key]["max_height"] = max_height
|
||||||
spawn_dictionary[key]["day_toggle"] = day_toggle
|
spawn_dictionary[key]["day_toggle"] = day_toggle
|
||||||
--spawn_dictionary[key]["on_spawn"] = spawn_abm_action
|
|
||||||
spawn_dictionary[key]["spawn_class"] = spawn_class
|
|
||||||
|
|
||||||
--[[
|
summary_chance = summary_chance + chance
|
||||||
minetest.register_abm({
|
|
||||||
label = name .. " spawning",
|
|
||||||
nodenames = nodes,
|
|
||||||
neighbors = neighbors,
|
|
||||||
interval = interval,
|
|
||||||
chance = floor(max(1, chance * mobs_spawn_chance)),
|
|
||||||
catch_up = false,
|
|
||||||
action = spawn_abm_action,
|
|
||||||
})
|
|
||||||
]]--
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- compatibility with older mob registration
|
local two_pi = 2 * math.pi
|
||||||
-- we're going to forget about this for now -j4i
|
local function get_next_mob_spawn_pos(pos)
|
||||||
--[[
|
local distance = math_random(25, 32)
|
||||||
function mobs:register_spawn(name, nodes, max_light, min_light, chance, active_object_count, max_height, day_toggle)
|
local angle = math_random() * two_pi
|
||||||
|
return {
|
||||||
mobs:spawn_specific(name, nodes, {"air"}, min_light, max_light, 30,
|
x = math_round(pos.x + distance * math_cos(angle)),
|
||||||
chance, active_object_count, -31000, max_height, day_toggle)
|
y = pos.y,
|
||||||
end
|
z = math_round(pos.z + distance * math_sin(angle))
|
||||||
]]--
|
|
||||||
|
|
||||||
|
|
||||||
--Don't disable this yet-j4i
|
|
||||||
-- MarkBu's spawn function
|
|
||||||
|
|
||||||
function mobs:spawn(def)
|
|
||||||
--does nothing for now
|
|
||||||
--[[
|
|
||||||
local name = def.name
|
|
||||||
local nodes = def.nodes or {"group:soil", "group:stone"}
|
|
||||||
local neighbors = def.neighbors or {"air"}
|
|
||||||
local min_light = def.min_light or 0
|
|
||||||
local max_light = def.max_light or 15
|
|
||||||
local interval = def.interval or 30
|
|
||||||
local chance = def.chance or 5000
|
|
||||||
local active_object_count = def.active_object_count or 1
|
|
||||||
local min_height = def.min_height or -31000
|
|
||||||
local max_height = def.max_height or 31000
|
|
||||||
local day_toggle = def.day_toggle
|
|
||||||
local on_spawn = def.on_spawn
|
|
||||||
|
|
||||||
mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, interval,
|
|
||||||
chance, active_object_count, min_height, max_height, day_toggle, on_spawn)
|
|
||||||
]]--
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
local axis
|
|
||||||
--inner and outer part of square donut radius
|
|
||||||
local inner = 1
|
|
||||||
local outer = 65
|
|
||||||
local int = {-1,1}
|
|
||||||
local position_calculation = function(pos)
|
|
||||||
|
|
||||||
pos = vector.floor(pos)
|
|
||||||
|
|
||||||
--this is used to determine the axis buffer from the player
|
|
||||||
axis = math.random(0,1)
|
|
||||||
|
|
||||||
--cast towards the direction
|
|
||||||
if axis == 0 then --x
|
|
||||||
pos.x = pos.x + math.random(inner,outer)*int[math.random(1,2)]
|
|
||||||
pos.z = pos.z + math.random(-outer,outer)
|
|
||||||
else --z
|
|
||||||
pos.z = pos.z + math.random(inner,outer)*int[math.random(1,2)]
|
|
||||||
pos.x = pos.x + math.random(-outer,outer)
|
|
||||||
end
|
|
||||||
return(pos)
|
|
||||||
end
|
|
||||||
|
|
||||||
--[[
|
|
||||||
local decypher_limits_dictionary = {
|
|
||||||
["overworld"] = {mcl_vars.mg_overworld_min,mcl_vars.mg_overworld_max},
|
|
||||||
["nether"] = {mcl_vars.mg_nether_min, mcl_vars.mg_nether_max},
|
|
||||||
["end"] = {mcl_vars.mg_end_min, mcl_vars.mg_end_max}
|
|
||||||
}
|
}
|
||||||
]]--
|
end
|
||||||
|
|
||||||
local function decypher_limits(posy)
|
local function decypher_limits(posy)
|
||||||
--local min_max_table = decypher_limits_dictionary[dimension]
|
posy = math_floor(posy)
|
||||||
--return min_max_table[1],min_max_table[2]
|
|
||||||
posy = math.floor(posy)
|
|
||||||
return posy - 32, posy + 32
|
return posy - 32, posy + 32
|
||||||
end
|
end
|
||||||
|
|
||||||
--a simple helper function for mob_spawn
|
--a simple helper function for mob_spawn
|
||||||
local function biome_check(biome_list, biome_goal)
|
local function biome_check(biome_list, biome_goal)
|
||||||
for _,data in ipairs(biome_list) do
|
for _, data in pairs(biome_list) do
|
||||||
if data == biome_goal then
|
if data == biome_goal then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
@ -531,125 +372,111 @@ local function biome_check(biome_list, biome_goal)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function is_farm_animal(n)
|
||||||
--todo mob limiting
|
return n == "mobs_mc:pig" or n == "mobs_mc:cow" or n == "mobs_mc:sheep" or n == "mobs_mc:chicken" or n == "mobs_mc:horse" or n == "mobs_mc:donkey"
|
||||||
--MAIN LOOP
|
end
|
||||||
|
|
||||||
if mobs_spawn then
|
if mobs_spawn then
|
||||||
local timer = 0
|
|
||||||
minetest.register_globalstep(function(dtime)
|
|
||||||
timer = timer + dtime
|
|
||||||
if timer >= 8 then
|
|
||||||
timer = 0
|
|
||||||
for _,player in pairs(minetest.get_connected_players()) do
|
|
||||||
for i = 1,math_random(3,8) do
|
|
||||||
repeat -- after this line each "break" means "continue"
|
|
||||||
local player_pos = player:get_pos()
|
|
||||||
|
|
||||||
local _,dimension = mcl_worlds.y_to_layer(player_pos.y)
|
local perlin_noise
|
||||||
|
|
||||||
if dimension == "void" or dimension == "default" then
|
|
||||||
break -- ignore void and unloaded area
|
|
||||||
end
|
|
||||||
|
|
||||||
local min,max = decypher_limits(player_pos.y)
|
|
||||||
|
|
||||||
local goal_pos = position_calculation(player_pos)
|
|
||||||
|
|
||||||
local spawning_position_list = find_nodes_in_area_under_air(new_vector(goal_pos.x,min,goal_pos.z), vector.new(goal_pos.x,max,goal_pos.z), {"group:solid", "group:water", "group:lava"})
|
|
||||||
|
|
||||||
--couldn't find node
|
|
||||||
if #spawning_position_list <= 0 then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
|
local function spawn_a_mob(pos, dimension, y_min, y_max)
|
||||||
|
local dimension = dimension or mcl_worlds.pos_to_dimension(pos)
|
||||||
|
local goal_pos = get_next_mob_spawn_pos(pos)
|
||||||
|
local spawning_position_list = find_nodes_in_area_under_air(
|
||||||
|
{x = goal_pos.x, y = y_min, z = goal_pos.z},
|
||||||
|
{x = goal_pos.x, y = y_max, z = goal_pos.z},
|
||||||
|
{"group:solid", "group:water", "group:lava"}
|
||||||
|
)
|
||||||
|
if #spawning_position_list <= 0 then return end
|
||||||
local spawning_position = spawning_position_list[math_random(1, #spawning_position_list)]
|
local spawning_position = spawning_position_list[math_random(1, #spawning_position_list)]
|
||||||
|
|
||||||
--Prevent strange behavior/too close to player
|
--hard code mob limit in area to 5 for now
|
||||||
if not spawning_position or vector_distance(player_pos, spawning_position) < 15 then
|
if count_mobs(spawning_position) >= 5 then return end
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
local gotten_node = get_node(spawning_position)
|
|
||||||
local gotten_node_name = gotten_node.name
|
|
||||||
local gotten_node_def = minetest.registered_nodes[gotten_node_name]
|
|
||||||
|
|
||||||
if not gotten_node_name or not gotten_node_def or gotten_node_name == "air" then --skip air nodes
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
if gotten_node_def.use_texture_alpha and gotten_node_def.use_texture_alpha ~= "opaque" then
|
|
||||||
break
|
|
||||||
end --don't spawn on nonopaque nodes
|
|
||||||
|
|
||||||
local leaf = minetest.get_item_group(gotten_node_name,"leaves")
|
|
||||||
if leaf ~= 0 then
|
|
||||||
break end --don't spawn on treetops
|
|
||||||
|
|
||||||
|
local gotten_node = get_node(spawning_position).name
|
||||||
local gotten_biome = minetest.get_biome_data(spawning_position)
|
local gotten_biome = minetest.get_biome_data(spawning_position)
|
||||||
|
if not gotten_node or not gotten_biome then return end
|
||||||
if not gotten_biome then
|
|
||||||
break --skip if in unloaded area
|
|
||||||
end
|
|
||||||
|
|
||||||
gotten_biome = get_biome_name(gotten_biome.biome) --makes it easier to work with
|
gotten_biome = get_biome_name(gotten_biome.biome) --makes it easier to work with
|
||||||
|
|
||||||
--grab random mob
|
|
||||||
local mob_def = spawn_dictionary[math.random(1,#spawn_dictionary)]
|
|
||||||
|
|
||||||
if not mob_def then
|
|
||||||
break --skip if something ridiculous happens (nil mob def)
|
|
||||||
end
|
|
||||||
|
|
||||||
--skip if not correct dimension
|
|
||||||
if mob_def.dimension ~= dimension then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
--skip if not in correct biome
|
|
||||||
if not biome_check(mob_def.biomes, gotten_biome) then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
--add this so mobs don't spawn inside nodes
|
--add this so mobs don't spawn inside nodes
|
||||||
spawning_position.y = spawning_position.y + 1
|
spawning_position.y = spawning_position.y + 1
|
||||||
|
|
||||||
if spawning_position.y < mob_def.min_height or spawning_position.y > mob_def.max_height then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
--only need to poll for node light if everything else worked
|
--only need to poll for node light if everything else worked
|
||||||
local gotten_light = get_node_light(spawning_position)
|
local gotten_light = get_node_light(spawning_position)
|
||||||
|
|
||||||
--don't spawn if not in light limits
|
local is_water = get_item_group(gotten_node, "water") ~= 0
|
||||||
if gotten_light < mob_def.min_light or gotten_light > mob_def.max_light then
|
local is_lava = get_item_group(gotten_node, "lava") ~= 0
|
||||||
break
|
local is_ground = not (is_water or is_lava)
|
||||||
end
|
local is_grass = minetest.get_item_group(gotten_node,"grass_block") ~= 0
|
||||||
|
local has_bed = minetest.find_node_near(pos,25,{"group:bed"})
|
||||||
|
|
||||||
local is_water = get_item_group(gotten_node_name, "water") ~= 0
|
if not is_ground then
|
||||||
local is_lava = get_item_group(gotten_node_name, "lava") ~= 0
|
|
||||||
|
|
||||||
if mob_def.type_of_spawning == "ground" and is_water then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
if mob_def.type_of_spawning == "ground" and is_lava then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
--finally do the heavy check (for now) of mobs in area
|
|
||||||
if count_mobs(spawning_position, mob_def.spawn_class) >= mob_def.aoc then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
--adjust the position for water and lava mobs
|
|
||||||
if mob_def.type_of_spawning == "water" or mob_def.type_of_spawning == "lava" then
|
|
||||||
spawning_position.y = spawning_position.y - 1
|
spawning_position.y = spawning_position.y - 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local mob_def
|
||||||
|
|
||||||
|
--create a disconnected clone of the spawn dictionary
|
||||||
|
--prevents memory leak
|
||||||
|
local mob_library_worker_table = table_copy(spawn_dictionary)
|
||||||
|
|
||||||
|
--grab mob that fits into the spawning location
|
||||||
|
--randomly grab a mob, don't exclude any possibilities
|
||||||
|
perlin_noise = perlin_noise or minetest_get_perlin(noise_params)
|
||||||
|
local noise = perlin_noise:get_3d(spawning_position)
|
||||||
|
local current_summary_chance = summary_chance
|
||||||
|
while #mob_library_worker_table > 0 do
|
||||||
|
local mob_chance_offset = (math_round(noise * current_summary_chance + 12345) % current_summary_chance) + 1
|
||||||
|
local mob_index = 1
|
||||||
|
local mob_chance = mob_library_worker_table[mob_index].chance
|
||||||
|
local step_chance = mob_chance
|
||||||
|
while step_chance < mob_chance_offset do
|
||||||
|
mob_index = mob_index + 1
|
||||||
|
mob_chance = mob_library_worker_table[mob_index].chance
|
||||||
|
step_chance = step_chance + mob_chance
|
||||||
|
end
|
||||||
|
local mob_def = mob_library_worker_table[mob_index]
|
||||||
|
local mob_type = minetest.registered_entities[mob_def.name].type
|
||||||
|
if mob_def
|
||||||
|
and spawning_position.y >= mob_def.min_height
|
||||||
|
and spawning_position.y <= mob_def.max_height
|
||||||
|
and mob_def.dimension == dimension
|
||||||
|
and biome_check(mob_def.biomes, gotten_biome)
|
||||||
|
and gotten_light >= mob_def.min_light
|
||||||
|
and gotten_light <= mob_def.max_light
|
||||||
|
and (is_ground or mob_def.type_of_spawning ~= "ground")
|
||||||
|
and (mob_def.check_position and mob_def.check_position(spawning_position) or true)
|
||||||
|
and (not is_farm_animal(mob_def.name) or is_grass)
|
||||||
|
and (mob_type ~= "npc" or has_bed)
|
||||||
|
then
|
||||||
--everything is correct, spawn mob
|
--everything is correct, spawn mob
|
||||||
minetest.add_entity(spawning_position, mob_def.name)
|
local object = minetest.add_entity(spawning_position, mob_def.name)
|
||||||
until true --this is a safety catch
|
if object then
|
||||||
|
return mob_def.on_spawn and mob_def.on_spawn(object, pos)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
current_summary_chance = current_summary_chance - mob_chance
|
||||||
|
table_remove(mob_library_worker_table, mob_index)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--MAIN LOOP
|
||||||
|
|
||||||
|
local timer = 0
|
||||||
|
minetest.register_globalstep(function(dtime)
|
||||||
|
timer = timer + dtime
|
||||||
|
if timer < 10 then return end
|
||||||
|
timer = 0
|
||||||
|
for _, player in pairs(get_connected_players()) do
|
||||||
|
local pos = player:get_pos()
|
||||||
|
local dimension = mcl_worlds.pos_to_dimension(pos)
|
||||||
|
-- ignore void and unloaded area
|
||||||
|
if dimension ~= "void" and dimension ~= "default" then
|
||||||
|
local y_min, y_max = decypher_limits(pos.y)
|
||||||
|
for i = 1, math_random(1, 4) do
|
||||||
|
spawn_a_mob(pos, dimension, y_min, y_max)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -197,7 +197,6 @@ mobs:spawn_specific(
|
||||||
"ExtremeHills+",
|
"ExtremeHills+",
|
||||||
"Forest",
|
"Forest",
|
||||||
"Plains",
|
"Plains",
|
||||||
"Desert",
|
|
||||||
"ColdTaiga",
|
"ColdTaiga",
|
||||||
"MushroomIsland",
|
"MushroomIsland",
|
||||||
"IcePlainsSpikes",
|
"IcePlainsSpikes",
|
||||||
|
@ -290,7 +289,6 @@ mobs:spawn_specific(
|
||||||
"ExtremeHills+",
|
"ExtremeHills+",
|
||||||
"Forest",
|
"Forest",
|
||||||
"Plains",
|
"Plains",
|
||||||
"Desert",
|
|
||||||
"ColdTaiga",
|
"ColdTaiga",
|
||||||
"MushroomIsland",
|
"MushroomIsland",
|
||||||
"IcePlainsSpikes",
|
"IcePlainsSpikes",
|
||||||
|
@ -342,9 +340,6 @@ mobs:spawn_specific(
|
||||||
"ground",
|
"ground",
|
||||||
{
|
{
|
||||||
"Desert",
|
"Desert",
|
||||||
"SavannaM",
|
|
||||||
"Savanna",
|
|
||||||
"Savanna_beach",
|
|
||||||
},
|
},
|
||||||
0,
|
0,
|
||||||
7,
|
7,
|
||||||
|
@ -359,9 +354,6 @@ mobs:spawn_specific(
|
||||||
"ground",
|
"ground",
|
||||||
{
|
{
|
||||||
"Desert",
|
"Desert",
|
||||||
"SavannaM",
|
|
||||||
"Savanna",
|
|
||||||
"Savanna_beach",
|
|
||||||
},
|
},
|
||||||
0,
|
0,
|
||||||
7,
|
7,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user