mirror of
https://github.com/ElCeejo/creatura.git
synced 2025-03-15 04:11:24 +00:00
Fixed mapgen spawning, Deprecate globalstep spawning
This commit is contained in:
parent
59602c9d36
commit
725eafb6e0
5 changed files with 380 additions and 479 deletions
2
api.lua
2
api.lua
|
@ -516,7 +516,7 @@ function creatura.basic_punch_func(self, puncher, tflp, tool_caps, dir)
|
|||
if puncher:is_player() then
|
||||
tool = puncher:get_wielded_item()
|
||||
tool_name = tool:get_name()
|
||||
add_wear = not minetest.is_creative_enabled(puncher)
|
||||
add_wear = not minetest.is_creative_enabled(puncher:get_player_name())
|
||||
end
|
||||
if (self.immune_to
|
||||
and contains_val(self.immune_to, tool_name)) then
|
||||
|
|
45
doc.txt
45
doc.txt
|
@ -207,6 +207,13 @@ Environment access
|
|||
Global Mob API
|
||||
--------------
|
||||
|
||||
* `creatura.default_water_physics(self)`
|
||||
* Bouyancy and Drag physics used by default on all Mobs
|
||||
|
||||
* `creatura.default_vitals(self)`
|
||||
* Vitals used by default on all Mobs
|
||||
* Handles suffocation, drowning, fire damage, and fall damage
|
||||
|
||||
* `creatura.drop_items(self)`
|
||||
* Drops items from `self.drops`
|
||||
|
||||
|
@ -236,4 +243,40 @@ The maximum amount of time the pathfinder can spend per-step (in microseconds) c
|
|||
* Finds a path from `pos1` to `pos2`
|
||||
* Returns a path with arbitrary angles for natural looking paths at the expense of performance
|
||||
* `get_neighbors` is a function used to find valid neighbors
|
||||
* `creatura.pathfinder.get_neighbors_fly` and `creatura.pathfinder.get_neighbors_swim` are bundled by default
|
||||
* `creatura.pathfinder.get_neighbors_fly` and `creatura.pathfinder.get_neighbors_swim` are bundled by default
|
||||
|
||||
Spawning
|
||||
--------
|
||||
|
||||
NOTE: Globalstep spawning from early versions of the API likely won't recieve much/any support going forward. Use ABM Spawning instead.
|
||||
|
||||
* `creatura.register_abm_spawn(name, def)`
|
||||
* `name` of the mob to spawn
|
||||
* `def` is a table of spawn parameters
|
||||
* `chance` is the chance of a mob spawning every `interval`
|
||||
* (a `chance` of 30 and `interval` of 60 would mean a 1 in 30 chance of a mob spawning every 60 seconds)
|
||||
* `chance_on_load` same as `chance` but for LBM spawning (when a chunk is loaded for the first time)
|
||||
* `interval` is how often (in seconds) a spawn attempt will happen
|
||||
* `min_height` is the minimum height that a spawn attempt can happen at
|
||||
* a `min_height` of 0 would mean the mob cannot spawn below a y coordinate of 0
|
||||
* `max_height` is the maximum height that a spawn attempt can happen at
|
||||
* a `max_height` of 128 would mean the mob cannot spawn above a y coordinate of 128
|
||||
* `min_time` is the minimum time a mob can spawn at
|
||||
* `max_time` is the maximum time a mob can spawn at
|
||||
* set `min_time` to 19500 and `max_time` to 4500 to only spawn at night and swap the numbers to only spawn at day
|
||||
* `min_light` is the minimum light level a mob can spawn at
|
||||
* `max_light` is the maximum light level a mob can spawn at
|
||||
* `min_group` is the lowest number of mobs to spawn in a group at a time
|
||||
* value of 3 means the mob will always spawn with at least 3 mobs together
|
||||
* `max_group` is the highest number of mobs to spawn in a group at a time
|
||||
* `block_protected` will block spawning mobs in protected areas if set to true
|
||||
* `biomes` is a table of biomes the mob can spawn in
|
||||
* `nodes` is a table of nodes the mob can spawn in/on
|
||||
* `neighbors` is a table of nodes that must be adjacent to the spawn position
|
||||
* ex: set to `{"groups:tree"}` to force the mob to spawn next to a tree
|
||||
* `spawn_on_load` will spawn mobs when a chunk generates if set to true
|
||||
* `spawn_active` will spawn mobs at the rate set by `interval` if set to true
|
||||
* this option is true by default. setting it to false without setting `spawn_on_load` to true will disable spawning the mob.
|
||||
* `spawn_in_nodes` will spawn mobs inside the node rather than above if set to true
|
||||
* set this to true for mobs that spawn in water
|
||||
* `spawn_cap` is the maximum amount of the mob that can spawn within active block range
|
|
@ -133,7 +133,7 @@ local neighbor_grid_3d = {
|
|||
|
||||
-- Get Neighbors
|
||||
|
||||
local function get_neighbors(pos, width, height, open, closed, parent)
|
||||
local function get_neighbors(pos, width, height, open, closed, parent, evaluated)
|
||||
local result = {}
|
||||
local neighbor
|
||||
local can_move
|
||||
|
@ -151,7 +151,8 @@ local function get_neighbors(pos, width, height, open, closed, parent)
|
|||
end
|
||||
|
||||
if open[hashed_pos]
|
||||
or closed[hashed_pos] then
|
||||
or closed[hashed_pos]
|
||||
or evaluated[hashed_pos] then
|
||||
can_move = false
|
||||
elseif can_move then
|
||||
can_move = not is_blocked(neighbor, width, height)
|
||||
|
@ -172,6 +173,8 @@ local function get_neighbors(pos, width, height, open, closed, parent)
|
|||
if can_move then
|
||||
table.insert(result, neighbor)
|
||||
end
|
||||
|
||||
evaluated[hashed_pos] = true
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
@ -303,6 +306,7 @@ function creatura.pathfinder.find_path(self, pos1, pos2, neighbor_func)
|
|||
|
||||
local openSet = self._path_data.open or {}
|
||||
local closedSet = self._path_data.closed or {}
|
||||
local evaluated = {}
|
||||
|
||||
local start_index = minetest.hash_node_position(start)
|
||||
|
||||
|
@ -382,7 +386,8 @@ function creatura.pathfinder.find_path(self, pos1, pos2, neighbor_func)
|
|||
self.height,
|
||||
openSet,
|
||||
closedSet,
|
||||
(parent_closed and parent_closed.pos) or (parent_open and parent_open.pos)
|
||||
(parent_closed and parent_closed.pos) or (parent_open and parent_open.pos),
|
||||
evaluated
|
||||
)
|
||||
-- Fly, Swim, and Climb all return true for check_vertical to properly check if goal has been reached
|
||||
|
||||
|
@ -463,6 +468,7 @@ function creatura.pathfinder.find_path_theta(self, pos1, pos2, neighbor_func)
|
|||
|
||||
local openSet = self._path_data.open or {}
|
||||
local closedSet = self._path_data.closed or {}
|
||||
local evaluated = {}
|
||||
|
||||
local start_index = minetest.hash_node_position(start)
|
||||
|
||||
|
@ -543,7 +549,8 @@ function creatura.pathfinder.find_path_theta(self, pos1, pos2, neighbor_func)
|
|||
self.height,
|
||||
openSet,
|
||||
closedSet,
|
||||
(parent_closed and parent_closed.pos) or (parent_open and parent_open.pos)
|
||||
(parent_closed and parent_closed.pos) or (parent_open and parent_open.pos),
|
||||
evaluated
|
||||
)
|
||||
-- Fly, Swim, and Climb all return true for check_vertical to properly check if goal has been reached
|
||||
|
||||
|
|
|
@ -7,14 +7,11 @@ creatura_step_type (Step Type) enum simple simple,fancy
|
|||
# How often (in seconds) the spawn ABM is called
|
||||
creatura_spawn_interval (Spawn ABM Interval) int 10
|
||||
|
||||
# Time (in seconds) between spawn steps
|
||||
creatura_spawn_step (Spawn Step Interval) int 15
|
||||
|
||||
# Allows Mobs to spawn during chunk generation (If dependent mods use spawn_on_gen)
|
||||
creatura_mapgen_spawning (Mapgen Spawning) bool true
|
||||
|
||||
# How many chunks are generated before a Mob can spawn
|
||||
creatura_mapgen_spawn_interval (Mapgen Spawning Interval) int 5
|
||||
creatura_mapgen_spawn_interval (Mapgen Spawning Interval) int 64
|
||||
|
||||
# How many Mobs can be a in a Mapblock before ABM spawning is blocked
|
||||
creatura_mapblock_limit (Max Mobs per Mapblock) int 12
|
||||
|
|
792
spawning.lua
792
spawning.lua
|
@ -3,18 +3,7 @@
|
|||
--------------
|
||||
|
||||
creatura.registered_mob_spawns = {}
|
||||
|
||||
local walkable_nodes = {}
|
||||
|
||||
minetest.register_on_mods_loaded(function()
|
||||
for name in pairs(minetest.registered_nodes) do
|
||||
if name ~= "air" and name ~= "ignore" then
|
||||
if minetest.registered_nodes[name].walkable then
|
||||
table.insert(walkable_nodes, name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
creatura.registered_on_spawns = {}
|
||||
|
||||
-- Math --
|
||||
|
||||
|
@ -22,17 +11,11 @@ local abs = math.abs
|
|||
local ceil = math.ceil
|
||||
local pi = math.pi
|
||||
local random = math.random
|
||||
local min, max = math.min, math.max
|
||||
|
||||
local function vec_raise(v, n)
|
||||
return {x = v.x, y = v.y + n, z = v.z}
|
||||
end
|
||||
local min = math.min
|
||||
|
||||
local vec_add, vec_dist, vec_sub = vector.add, vector.distance, vector.subtract
|
||||
|
||||
-- Registration --
|
||||
|
||||
local creative = minetest.settings:get_bool("creative_mode")
|
||||
-- Utility Functions --
|
||||
|
||||
local function format_name(str)
|
||||
if str then
|
||||
|
@ -41,36 +24,56 @@ local function format_name(str)
|
|||
end
|
||||
end
|
||||
|
||||
function creatura.register_spawn_egg(name, col1, col2, inventory_image) -- deprecated
|
||||
if col1 and col2 then
|
||||
local base = "(creatura_spawning_crystal_primary.png^[multiply:#" .. col1 .. ")"
|
||||
local spots = "(creatura_spawning_crystal_secondary.png^[multiply:#" .. col2 .. ")"
|
||||
inventory_image = base .. "^" .. spots
|
||||
local function table_contains(tbl, val)
|
||||
for _, v in pairs(tbl) do
|
||||
if v == val then
|
||||
return true
|
||||
end
|
||||
end
|
||||
local mod_name = name:split(":")[1]
|
||||
local mob_name = name:split(":")[2]
|
||||
minetest.register_craftitem(mod_name .. ":spawn_" .. mob_name, {
|
||||
description = "Spawn " .. format_name(name),
|
||||
inventory_image = inventory_image,
|
||||
stack_max = 99,
|
||||
on_place = function(itemstack, _, pointed_thing)
|
||||
local mobdef = minetest.registered_entities[name]
|
||||
local spawn_offset = abs(mobdef.collisionbox[2])
|
||||
local pos = minetest.get_pointed_thing_position(pointed_thing, true)
|
||||
pos.y = (pos.y - 0.4) + spawn_offset
|
||||
local object = minetest.add_entity(pos, name)
|
||||
if object then
|
||||
object:set_yaw(random(1, 6))
|
||||
object:get_luaentity().last_yaw = object:get_yaw()
|
||||
end
|
||||
if not creative then
|
||||
itemstack:take_item()
|
||||
return itemstack
|
||||
return false
|
||||
end
|
||||
|
||||
local function pos_meets_params(pos, def)
|
||||
if not minetest.find_nodes_in_area(pos, pos, def.nodes) then return false end
|
||||
if not minetest.find_node_near(pos, 1, def.neighbors) then return false end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function can_spawn(pos, width, height)
|
||||
local pos2
|
||||
local w_iter = width / ceil(width)
|
||||
for y = 0, height, height / ceil(height) do
|
||||
for z = -width, width, w_iter do
|
||||
for x = -width, width, w_iter do
|
||||
pos2 = {x = pos.x + x, y = pos.y + y, z = pos.z + z}
|
||||
local def = creatura.get_node_def(pos2)
|
||||
if def.walkable then return false end
|
||||
end
|
||||
end
|
||||
})
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function do_on_spawn(pos, obj)
|
||||
local name = obj and obj:get_luaentity().name
|
||||
if not name then return end
|
||||
local spawn_functions = creatura.registered_on_spawns[name] or {}
|
||||
|
||||
if #spawn_functions > 0 then
|
||||
for _, func in ipairs(spawn_functions) do
|
||||
func(obj:get_luaentity(), pos)
|
||||
if not obj:get_yaw() then break end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
----------------
|
||||
-- Spawn Item --
|
||||
----------------
|
||||
|
||||
local creative = minetest.settings:get_bool("creative_mode")
|
||||
|
||||
function creatura.register_spawn_item(name, def)
|
||||
local inventory_image
|
||||
if not def.inventory_image
|
||||
|
@ -132,30 +135,6 @@ function creatura.register_spawn_item(name, def)
|
|||
minetest.register_craftitem(def.itemstring or (mod_name .. ":spawn_" .. mob_name), def)
|
||||
end
|
||||
|
||||
function creatura.register_mob_spawn(name, def)
|
||||
local spawn = {
|
||||
chance = def.chance or 5,
|
||||
min_height = def.min_height or 0,
|
||||
max_height = def.max_height or 128,
|
||||
min_time = def.min_time or 0,
|
||||
max_time = def.max_time or 24000,
|
||||
min_light = def.min_light or 6,
|
||||
max_light = def.max_light or 15,
|
||||
min_group = def.min_group or 1,
|
||||
max_group = def.max_group or 4,
|
||||
nodes = def.nodes or nil,
|
||||
biomes = def.biomes or nil,
|
||||
spawn_cluster = def.spawn_cluster or false,
|
||||
spawn_on_gen = def.spawn_on_gen or false,
|
||||
spawn_in_nodes = def.spawn_in_nodes or false,
|
||||
spawn_cap = def.spawn_cap or 5,
|
||||
send_debug = def.send_debug or false
|
||||
}
|
||||
creatura.registered_mob_spawns[name] = spawn
|
||||
end
|
||||
|
||||
creatura.registered_on_spawns = {}
|
||||
|
||||
function creatura.register_on_spawn(name, func)
|
||||
if not creatura.registered_on_spawns[name] then
|
||||
creatura.registered_on_spawns[name] = {}
|
||||
|
@ -163,336 +142,9 @@ function creatura.register_on_spawn(name, func)
|
|||
table.insert(creatura.registered_on_spawns[name], func)
|
||||
end
|
||||
|
||||
-- Utility Functions --
|
||||
|
||||
local function table_contains(tbl, val)
|
||||
for _, v in pairs(tbl) do
|
||||
if v == val then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function get_spawnable_mobs(pos, mapgen)
|
||||
local biome = minetest.get_biome_name(minetest.get_biome_data(pos).biome)
|
||||
if not biome then return end
|
||||
local mobs = {}
|
||||
for mob, data in pairs(creatura.registered_mob_spawns) do
|
||||
local biome_valid = data.biomes and table_contains(data.biomes, biome)
|
||||
if (biome_valid
|
||||
or data.nodes)
|
||||
and (not mapgen
|
||||
or data.spawn_on_gen)
|
||||
and not data.spawn_in_nodes then
|
||||
table.insert(mobs, mob)
|
||||
end
|
||||
end
|
||||
return mobs
|
||||
end
|
||||
|
||||
local function is_spawn_node(name, spawn_nodes)
|
||||
for _, val in ipairs(spawn_nodes) do
|
||||
if val:match("^group:") then
|
||||
local group = val:split(":")[2]
|
||||
if minetest.get_item_group(name, group) > 0 then
|
||||
return true
|
||||
end
|
||||
elseif val == name then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- Spawning Function --
|
||||
|
||||
local min_spawn_radius = 32
|
||||
local max_spawn_radius = 128
|
||||
|
||||
local function execute_spawns(player)
|
||||
if not player:get_pos() then return end
|
||||
local pos = player:get_pos()
|
||||
|
||||
local spawn_pos_center = {
|
||||
x = pos.x + random(-max_spawn_radius, max_spawn_radius),
|
||||
y = pos.y,
|
||||
z = pos.z + random(-max_spawn_radius, max_spawn_radius)
|
||||
}
|
||||
|
||||
local spawnable_mobs = get_spawnable_mobs(spawn_pos_center)
|
||||
if spawnable_mobs
|
||||
and #spawnable_mobs > 0 then
|
||||
local mob = spawnable_mobs[random(#spawnable_mobs)]
|
||||
local spawn = creatura.registered_mob_spawns[mob]
|
||||
if not spawn
|
||||
or random(spawn.chance or 2) > 1 then return end
|
||||
|
||||
-- Spawn cap check
|
||||
local objects = minetest.get_objects_inside_radius(pos, max_spawn_radius)
|
||||
local object_count = 0
|
||||
for _, object in ipairs(objects) do
|
||||
if creatura.is_alive(object)
|
||||
and not object:is_player()
|
||||
and object:get_luaentity().name == mob then
|
||||
object_count = object_count + 1
|
||||
end
|
||||
end
|
||||
if object_count >= spawn.spawn_cap then
|
||||
return
|
||||
end
|
||||
|
||||
local index_func
|
||||
if spawn.spawn_in_nodes then
|
||||
index_func = minetest.find_nodes_in_area
|
||||
else
|
||||
index_func = minetest.find_nodes_in_area_under_air
|
||||
end
|
||||
local spawn_on = spawn.nodes or walkable_nodes
|
||||
if type(spawn_on) == "string" then
|
||||
spawn_on = {spawn_on}
|
||||
end
|
||||
local spawn_y_array = index_func(
|
||||
vec_raise(spawn_pos_center, -max_spawn_radius),
|
||||
vec_raise(spawn_pos_center, max_spawn_radius),
|
||||
spawn_on)
|
||||
if spawn_y_array[1] then
|
||||
local spawn_pos = spawn_y_array[1]
|
||||
local dist = vec_dist(pos, spawn_pos)
|
||||
if dist < min_spawn_radius or dist > max_spawn_radius then
|
||||
return
|
||||
end
|
||||
|
||||
if spawn_pos.y > spawn.max_height
|
||||
or spawn_pos.y < spawn.min_height then
|
||||
return
|
||||
end
|
||||
|
||||
if not spawn.spawn_in_nodes then
|
||||
spawn_pos = vec_raise(spawn_pos, 1)
|
||||
end
|
||||
|
||||
local tod = (minetest.get_timeofday() or 0) * 24000
|
||||
|
||||
local min_time = spawn.min_time
|
||||
local max_time = spawn.max_time
|
||||
|
||||
local bounds_in = tod >= min_time and tod <= max_time
|
||||
local bounds_ex = tod >= max_time or tod <= min_time
|
||||
|
||||
if (max_time > min_time and not bounds_in)
|
||||
or (min_time > max_time and not bounds_ex) then
|
||||
return
|
||||
end
|
||||
|
||||
local light = minetest.get_node_light(spawn_pos) or 7
|
||||
|
||||
if light > spawn.max_light
|
||||
or light < spawn.min_light then
|
||||
return
|
||||
end
|
||||
|
||||
local mob_def = minetest.registered_entities[mob]
|
||||
local mob_width = mob_def.collisionbox[4]
|
||||
local mob_height = max(0, mob_def.collisionbox[5] - mob_def.collisionbox[2])
|
||||
|
||||
if not creatura.is_pos_moveable(spawn_pos, mob_width, mob_height) then
|
||||
return
|
||||
end
|
||||
|
||||
local group_size = random(spawn.min_group or 1, spawn.max_group or 1)
|
||||
group_size = min(spawn.spawn_cap - object_count, group_size)
|
||||
|
||||
if spawn.spawn_cluster then
|
||||
minetest.add_node(spawn_pos, {name = "creatura:spawn_node"})
|
||||
local meta = minetest.get_meta(spawn_pos)
|
||||
meta:set_string("mob", mob)
|
||||
meta:set_int("cluster", group_size)
|
||||
else
|
||||
for _ = 1, group_size do
|
||||
spawn_pos = {
|
||||
x = spawn_pos.x + random(-3, 3),
|
||||
y = spawn_pos.y,
|
||||
z = spawn_pos.z + random(-3, 3)
|
||||
}
|
||||
spawn_pos = creatura.get_ground_level(spawn_pos, 4)
|
||||
minetest.add_node(spawn_pos, {name = "creatura:spawn_node"})
|
||||
local meta = minetest.get_meta(spawn_pos)
|
||||
meta:set_string("mob", mob)
|
||||
end
|
||||
end
|
||||
if spawn.send_debug then
|
||||
minetest.chat_send_all(mob .. " spawned at " .. minetest.pos_to_string(spawn_pos))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local spawn_step = tonumber(minetest.settings:get("creatura_spawn_step")) or 15
|
||||
|
||||
local spawn_tick = 0
|
||||
|
||||
minetest.register_globalstep(function(dtime)
|
||||
if #creatura.registered_mob_spawns < 1 then return end
|
||||
spawn_tick = spawn_tick - dtime
|
||||
if spawn_tick <= 0 then
|
||||
for _, player in ipairs(minetest.get_connected_players()) do
|
||||
execute_spawns(player)
|
||||
end
|
||||
spawn_tick = spawn_step
|
||||
end
|
||||
end)
|
||||
|
||||
-- Mapgen Spawning --
|
||||
|
||||
local mapgen_spawning = minetest.settings:get_bool("creatura_mapgen_spawning", true)
|
||||
|
||||
local mapgen_spawning_int = tonumber(minetest.settings:get("creatura_mapgen_spawn_interval")) or 5
|
||||
|
||||
local spawn_cooldown = 1
|
||||
|
||||
minetest.register_on_generated(function(minp, maxp)
|
||||
if not mapgen_spawning then return end
|
||||
spawn_cooldown = spawn_cooldown - 1
|
||||
if spawn_cooldown <= 0 then
|
||||
local min_x, max_x = minp.x, maxp.x
|
||||
local min_y, max_y = minp.y, maxp.y
|
||||
local min_z, max_z = minp.z, maxp.z
|
||||
local mobs = get_spawnable_mobs({
|
||||
x = min_x + (max_x - min_x) * 0.5,
|
||||
y = min_y + (max_y - min_y) * 0.5,
|
||||
z = min_z + (max_z - min_z) * 0.5
|
||||
}, true)
|
||||
if not mobs then spawn_cooldown = mapgen_spawning_int return end
|
||||
local vm, emin, emax = minetest.get_mapgen_object("voxelmanip")
|
||||
local area = VoxelArea:new{MinEdge = emin, MaxEdge = emax}
|
||||
local data = vm:get_data()
|
||||
for z = min_z + 8, max_z - 7, 8 do
|
||||
for x = min_x + 8, max_x - 7, 8 do
|
||||
local mob = #mobs > 0 and mobs[random(#mobs)]
|
||||
if mob then
|
||||
local spawn = creatura.registered_mob_spawns[mob]
|
||||
for y = min_y, max_y do
|
||||
if y > spawn.max_height
|
||||
or y < spawn.min_height then
|
||||
break
|
||||
end
|
||||
local vi = area:index(x, y, z)
|
||||
local vi_name = minetest.get_name_from_content_id(data[vi])
|
||||
if spawn.spawn_in_nodes
|
||||
and is_spawn_node(vi_name, spawn.nodes) then
|
||||
-- Add Spawn Node to Map
|
||||
data[vi] = minetest.get_content_id("creatura:spawn_node")
|
||||
vm:set_data(data)
|
||||
vm:write_to_map()
|
||||
-- Apply Meta
|
||||
local group_size = random(spawn.min_group or 1, spawn.max_group or 1)
|
||||
local meta = minetest.get_meta({x = x, y = y, z = z})
|
||||
meta:set_string("mob", mob)
|
||||
meta:set_int("cluster", group_size)
|
||||
spawn_cooldown = mapgen_spawning_int
|
||||
return
|
||||
end
|
||||
local vi_n = area:index(x, y - 1, z)
|
||||
local vi_n_name = minetest.get_name_from_content_id(data[vi_n])
|
||||
if not creatura.get_node_def(vi_name).walkable
|
||||
and ((spawn.nodes and is_spawn_node(vi_n_name, spawn.nodes))
|
||||
or (not spawn.nodes and creatura.get_node_def(vi_n_name).walkable)) then
|
||||
-- Add Spawn Node to Map
|
||||
data[vi] = minetest.get_content_id("creatura:spawn_node")
|
||||
vm:set_data(data)
|
||||
vm:write_to_map()
|
||||
-- Apply Meta
|
||||
local group_size = random(spawn.min_group or 1, spawn.max_group or 1)
|
||||
local meta = minetest.get_meta({x = x, y = y, z = z})
|
||||
meta:set_string("mob", mob)
|
||||
meta:set_int("cluster", group_size)
|
||||
spawn_cooldown = mapgen_spawning_int
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Node --
|
||||
|
||||
minetest.register_node("creatura:spawn_node", {
|
||||
drawtype = "airlike",
|
||||
groups = {not_in_creative_inventory = 1},
|
||||
walkable = false
|
||||
})
|
||||
|
||||
local spawn_interval = tonumber(minetest.settings:get("creatura_spawn_interval")) or 10
|
||||
|
||||
minetest.register_abm({
|
||||
label = "Creatura Spawning",
|
||||
nodenames = {"creatura:spawn_node"},
|
||||
interval = spawn_interval,
|
||||
chance = 1,
|
||||
action = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local name = meta:get_string("mob") or ""
|
||||
if name == "" then minetest.remove_node(pos) return end
|
||||
local amount = meta:get_int("cluster")
|
||||
local obj
|
||||
if amount > 0 then
|
||||
for _ = 1, amount do
|
||||
obj = minetest.add_entity(pos, name)
|
||||
end
|
||||
minetest.log("action",
|
||||
"[Creatura] Spawned " .. amount .. " " .. name .. " at " .. minetest.pos_to_string(pos))
|
||||
else
|
||||
obj = minetest.add_entity(pos, name)
|
||||
minetest.log("action",
|
||||
"[Creatura] Spawned a " .. name .. " at " .. minetest.pos_to_string(pos))
|
||||
end
|
||||
minetest.remove_node(pos)
|
||||
if obj
|
||||
and creatura.registered_on_spawns[name]
|
||||
and #creatura.registered_on_spawns[name] > 0 then
|
||||
for i = 1, #creatura.registered_on_spawns[name] do
|
||||
local func = creatura.registered_on_spawns[name][i]
|
||||
func(obj:get_luaentity(), pos)
|
||||
if not obj:get_yaw() then break end
|
||||
end
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
--[[minetest.register_lbm({
|
||||
name = "creatura:spawning",
|
||||
nodenames = {"creatura:spawn_node"},
|
||||
run_at_every_load = true,
|
||||
action = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local name = meta:get_string("mob")
|
||||
local amount = meta:get_int("cluster")
|
||||
local obj
|
||||
if amount > 0 then
|
||||
for i = 1, amount do
|
||||
obj = minetest.add_entity(pos, name)
|
||||
end
|
||||
else
|
||||
obj = minetest.add_entity(pos, name)
|
||||
end
|
||||
minetest.remove_node(pos)
|
||||
if obj
|
||||
and creatura.registered_on_spawns[name]
|
||||
and #creatura.registered_on_spawns[name] > 0 then
|
||||
for i = 1, #creatura.registered_on_spawns[name] do
|
||||
local func = creatura.registered_on_spawns[name][i]
|
||||
func(obj:get_luaentity(), pos)
|
||||
end
|
||||
end
|
||||
end,
|
||||
})]]
|
||||
|
||||
------------------
|
||||
-- ABM Spawning --
|
||||
------------------
|
||||
--------------
|
||||
-- Spawning --
|
||||
--------------
|
||||
|
||||
--[[creatura.register_abm_spawn("mymod:mymob", {
|
||||
chance = 3000,
|
||||
|
@ -516,26 +168,12 @@ local max_per_block = tonumber(minetest.settings:get("creatura_mapblock_limit"))
|
|||
local max_in_abr = tonumber(minetest.settings:get("creatura_abr_limit")) or 24
|
||||
local min_abm_dist = min(abr / 2, tonumber(minetest.settings:get("creatura_min_abm_dist")) or 32)
|
||||
|
||||
local function can_spawn(pos, width, height)
|
||||
local pos2
|
||||
local w_iter = width / ceil(width)
|
||||
for y = 0, height, height / ceil(height) do
|
||||
for z = -width, width, w_iter do
|
||||
for x = -width, width, w_iter do
|
||||
pos2 = {x = pos.x + x, y = pos.y + y, z = pos.z + z}
|
||||
local def = creatura.get_node_def(pos2)
|
||||
if def.walkable then return false end
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local mobs_spawn = minetest.settings:get_bool("mobs_spawn") ~= false
|
||||
|
||||
local mapgen_mobs = {}
|
||||
|
||||
function creatura.register_abm_spawn(mob, def)
|
||||
local chance = def.chance or 3000
|
||||
local chance_on_load = def.chance_on_load or def.chance / 32
|
||||
local interval = def.interval or 30
|
||||
local min_height = def.min_height or 0
|
||||
local max_height = def.max_height or 128
|
||||
|
@ -550,11 +188,10 @@ function creatura.register_abm_spawn(mob, def)
|
|||
local nodes = def.nodes or {"group:soil", "group:stone"}
|
||||
local neighbors = def.neighbors or {"air"}
|
||||
local spawn_on_load = def.spawn_on_load or false
|
||||
local spawn_active = def.spawn_active or true
|
||||
local spawn_in_nodes = def.spawn_in_nodes or false
|
||||
local spawn_cap = def.spawn_cap or 5
|
||||
|
||||
local function spawn_func(pos, aocw, is_lbm)
|
||||
local function spawn_func(pos, aocw)
|
||||
|
||||
if not mobs_spawn then
|
||||
return
|
||||
|
@ -570,12 +207,6 @@ function creatura.register_abm_spawn(mob, def)
|
|||
return
|
||||
end
|
||||
|
||||
if is_lbm then -- Manual checks for LBMs
|
||||
if random(chance_on_load or chance) > 1 then return end
|
||||
if not minetest.find_node_near(pos, 1, neighbors) then return end
|
||||
if pos.y > max_height or pos.y < min_height then return end
|
||||
end
|
||||
|
||||
local tod = (minetest.get_timeofday() or 0) * 24000
|
||||
|
||||
local bounds_in = tod >= min_time and tod <= max_time
|
||||
|
@ -648,11 +279,14 @@ function creatura.register_abm_spawn(mob, def)
|
|||
end
|
||||
|
||||
local group_size = random(min_group or 1, max_group or 1)
|
||||
local obj
|
||||
|
||||
if group_size > 1 then
|
||||
local offset
|
||||
local spawn_pos
|
||||
for _ = 1, group_size do
|
||||
local offset = ceil(mob_width)
|
||||
local spawn_pos = creatura.get_ground_level({
|
||||
offset = ceil(mob_width)
|
||||
spawn_pos = creatura.get_ground_level({
|
||||
x = pos.x + random(-offset, offset),
|
||||
y = pos.y,
|
||||
z = pos.z + random(-offset, offset)
|
||||
|
@ -660,28 +294,12 @@ function creatura.register_abm_spawn(mob, def)
|
|||
if not can_spawn(spawn_pos, mob_width, mob_height) then
|
||||
spawn_pos = pos
|
||||
end
|
||||
local obj = minetest.add_entity(spawn_pos, mob)
|
||||
if obj
|
||||
and creatura.registered_on_spawns[mob]
|
||||
and #creatura.registered_on_spawns[mob] > 0 then
|
||||
for i = 1, #creatura.registered_on_spawns[mob] do
|
||||
local func = creatura.registered_on_spawns[mob][i]
|
||||
func(obj:get_luaentity(), pos)
|
||||
if not obj:get_yaw() then break end
|
||||
end
|
||||
end
|
||||
obj = minetest.add_entity(spawn_pos, mob)
|
||||
do_on_spawn(spawn_pos, obj)
|
||||
end
|
||||
else
|
||||
local obj = minetest.add_entity(pos, mob)
|
||||
if obj
|
||||
and creatura.registered_on_spawns[mob]
|
||||
and #creatura.registered_on_spawns[mob] > 0 then
|
||||
for i = 1, #creatura.registered_on_spawns[mob] do
|
||||
local func = creatura.registered_on_spawns[mob][i]
|
||||
func(obj:get_luaentity(), pos)
|
||||
if not obj:get_yaw() then break end
|
||||
end
|
||||
end
|
||||
obj = minetest.add_entity(pos, mob)
|
||||
do_on_spawn(pos, obj)
|
||||
end
|
||||
|
||||
minetest.log("action",
|
||||
|
@ -689,30 +307,266 @@ function creatura.register_abm_spawn(mob, def)
|
|||
|
||||
end
|
||||
|
||||
minetest.register_abm({
|
||||
label = mob .. " spawning",
|
||||
nodenames = nodes,
|
||||
neighbors = neighbors,
|
||||
interval = interval,
|
||||
chance = chance,
|
||||
min_y = min_height,
|
||||
max_y = max_height,
|
||||
catch_up = false,
|
||||
action = function(pos, _, _, aocw)
|
||||
spawn_func(pos, aocw)
|
||||
end
|
||||
})
|
||||
|
||||
if spawn_on_load then
|
||||
minetest.register_lbm({
|
||||
name = mob .. "_spawning",
|
||||
label = mob .. " spawning",
|
||||
nodenames = nodes,
|
||||
run_at_every_load = false,
|
||||
action = function(pos, _, _, aocw)
|
||||
spawn_func(pos, aocw, true)
|
||||
end
|
||||
})
|
||||
end
|
||||
if spawn_active then
|
||||
minetest.register_abm({
|
||||
label = mob .. " spawning",
|
||||
nodenames = nodes,
|
||||
neighbors = neighbors,
|
||||
interval = interval,
|
||||
chance = chance,
|
||||
min_y = min_height,
|
||||
max_y = max_height,
|
||||
catch_up = false,
|
||||
action = function(pos, _, _, aocw)
|
||||
spawn_func(pos, aocw, false)
|
||||
end
|
||||
})
|
||||
table.insert(mapgen_mobs, mob)
|
||||
end
|
||||
|
||||
creatura.registered_mob_spawns[mob] = {
|
||||
chance = def.chance or 3000,
|
||||
interval = def.interval or 30,
|
||||
min_height = def.min_height or 0,
|
||||
max_height = def.max_height or 128,
|
||||
min_time = def.min_time or 0,
|
||||
max_time = def.max_time or 24000,
|
||||
min_light = def.min_light or 1,
|
||||
max_light = def.max_light or 15,
|
||||
min_group = def.min_group or 1,
|
||||
max_group = def.max_group or 4,
|
||||
block_protected = def.block_protected_spawn or false,
|
||||
biomes = def.biomes or {},
|
||||
nodes = def.nodes or {"group:soil", "group:stone"},
|
||||
neighbors = def.neighbors or {"air"},
|
||||
spawn_on_load = def.spawn_on_load or false,
|
||||
spawn_in_nodes = def.spawn_in_nodes or false,
|
||||
spawn_cap = def.spawn_cap or 5
|
||||
}
|
||||
end
|
||||
|
||||
-- Mapgen --
|
||||
|
||||
minetest.register_node("creatura:spawn_node", {
|
||||
drawtype = "airlike",
|
||||
groups = {not_in_creative_inventory = 1},
|
||||
walkable = false
|
||||
})
|
||||
|
||||
local mapgen_spawning = minetest.settings:get_bool("creatura_mapgen_spawning", true)
|
||||
local mapgen_spawning_int = tonumber(minetest.settings:get("creatura_mapgen_spawn_interval")) or 64
|
||||
|
||||
if mapgen_spawning then
|
||||
local chunk_delay = 0
|
||||
local c_air = minetest.get_content_id("air")
|
||||
local c_spawn = minetest.get_content_id("creatura:spawn_node")
|
||||
|
||||
minetest.register_on_generated(function(minp, maxp)
|
||||
if chunk_delay > 0 then chunk_delay = chunk_delay - 1 end
|
||||
local meta_queue = {}
|
||||
|
||||
local vm, emin, emax = minetest.get_mapgen_object("voxelmanip")
|
||||
local area = VoxelArea:new{MinEdge = emin, MaxEdge = emax}
|
||||
local data = vm:get_data()
|
||||
|
||||
local min_x, max_x = minp.x, maxp.x
|
||||
local min_y, max_y = minp.y, maxp.y
|
||||
local min_z, max_z = minp.z, maxp.z
|
||||
|
||||
local def
|
||||
local center
|
||||
|
||||
local current_biome
|
||||
local spawn_biomes
|
||||
|
||||
local current_pos
|
||||
|
||||
for _, mob_name in ipairs(mapgen_mobs) do
|
||||
local mob_spawned = false
|
||||
|
||||
def = creatura.registered_mob_spawns[mob_name]
|
||||
|
||||
center = {
|
||||
x = min_x + (max_x - min_x) * 0.5,
|
||||
y = min_y + (max_y - min_y) * 0.5,
|
||||
z = min_z + (max_z - min_z) * 0.5
|
||||
}
|
||||
|
||||
current_biome = minetest.get_biome_name(minetest.get_biome_data(center).biome)
|
||||
spawn_biomes = def.biomes
|
||||
|
||||
if not mob_spawned
|
||||
and (not spawn_biomes
|
||||
or table_contains(spawn_biomes, current_biome)) then
|
||||
for z = min_z + 8, max_z - 7, 8 do
|
||||
if mob_spawned then break end
|
||||
for x = min_x + 8, max_x - 7, 8 do
|
||||
if mob_spawned then break end
|
||||
for y = min_y, max_y do
|
||||
local vi = area:index(x, y, z)
|
||||
|
||||
if data[vi] == c_air
|
||||
or data[vi] == c_spawn then
|
||||
break
|
||||
end
|
||||
|
||||
-- Check if position is outside of vertical bounds
|
||||
if y > def.max_height
|
||||
or y < def.min_height then
|
||||
break
|
||||
end
|
||||
|
||||
current_pos = vector.new(x, y, z)
|
||||
|
||||
-- Check if position has required nodes
|
||||
if not pos_meets_params(current_pos, def) then
|
||||
break
|
||||
end
|
||||
|
||||
if def.spawn_in_nodes then
|
||||
-- Add Spawn Node to Map
|
||||
data[vi] = c_spawn
|
||||
|
||||
local group_size = random(def.min_group or 1, def.max_group or 1)
|
||||
table.insert(meta_queue, {pos = current_pos, mob = mob_name, cluster = group_size})
|
||||
|
||||
mob_spawned = true
|
||||
break
|
||||
elseif data[area:index(x, y + 1, z)] == c_air then
|
||||
vi = area:index(x, y + 1, z)
|
||||
current_pos = vector.new(x, y + 1, z)
|
||||
|
||||
-- Add Spawn Node to Map
|
||||
data[vi] = c_spawn
|
||||
|
||||
local group_size = random(def.min_group or 1, def.max_group or 1)
|
||||
table.insert(meta_queue, {pos = current_pos, mob = mob_name, cluster = group_size})
|
||||
|
||||
mob_spawned = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if #meta_queue > 0 then
|
||||
vm:set_data(data)
|
||||
vm:write_to_map()
|
||||
|
||||
for _, unset_meta in ipairs(meta_queue) do
|
||||
local pos = unset_meta.pos
|
||||
local mob = unset_meta.mob
|
||||
local cluster = unset_meta.cluster
|
||||
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("mob", mob)
|
||||
meta:set_int("cluster", cluster)
|
||||
end
|
||||
|
||||
chunk_delay = mapgen_spawning_int
|
||||
end
|
||||
end)
|
||||
|
||||
local spawn_interval = tonumber(minetest.settings:get("creatura_spawn_interval")) or 10
|
||||
|
||||
minetest.register_abm({
|
||||
label = "Creatura Spawning",
|
||||
nodenames = {"creatura:spawn_node"},
|
||||
interval = spawn_interval,
|
||||
chance = 1,
|
||||
action = function(pos)
|
||||
local plyr_found = false
|
||||
local objects = minetest.get_objects_inside_radius(pos, abr)
|
||||
|
||||
for _, object in ipairs(objects) do
|
||||
if object:is_player() then
|
||||
plyr_found = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not plyr_found then return end
|
||||
|
||||
local meta = minetest.get_meta(pos)
|
||||
local name = meta:get_string("mob") or ""
|
||||
if name == "" then minetest.remove_node(pos) return end
|
||||
local amount = meta:get_int("cluster")
|
||||
local obj
|
||||
if amount > 0 then
|
||||
for _ = 1, amount do
|
||||
obj = minetest.add_entity(pos, name)
|
||||
do_on_spawn(pos, obj)
|
||||
end
|
||||
minetest.log("action",
|
||||
"[Creatura] Spawned " .. amount .. " " .. name .. " at " .. minetest.pos_to_string(pos))
|
||||
else
|
||||
obj = minetest.add_entity(pos, name)
|
||||
do_on_spawn(pos, obj)
|
||||
minetest.log("action",
|
||||
"[Creatura] Spawned a " .. name .. " at " .. minetest.pos_to_string(pos))
|
||||
end
|
||||
minetest.remove_node(pos)
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
----------------
|
||||
-- DEPRECATED --
|
||||
----------------
|
||||
|
||||
function creatura.register_mob_spawn(name, def)
|
||||
local spawn_def = {
|
||||
chance = def.chance or 5,
|
||||
min_height = def.min_height or 0,
|
||||
max_height = def.max_height or 128,
|
||||
min_time = def.min_time or 0,
|
||||
max_time = def.max_time or 24000,
|
||||
min_light = def.min_light or 6,
|
||||
max_light = def.max_light or 15,
|
||||
min_group = def.min_group or 1,
|
||||
max_group = def.max_group or 4,
|
||||
nodes = def.nodes or nil,
|
||||
biomes = def.biomes or nil,
|
||||
--spawn_cluster = def.spawn_cluster or false,
|
||||
spawn_on_load = def.spawn_on_gen or false,
|
||||
spawn_in_nodes = def.spawn_in_nodes or false,
|
||||
spawn_cap = def.spawn_cap or 5,
|
||||
--send_debug = def.send_debug or false
|
||||
}
|
||||
--creatura.registered_mob_spawns[name] = spawn_def
|
||||
|
||||
creatura.register_abm_spawn(name, spawn_def)
|
||||
end
|
||||
|
||||
function creatura.register_spawn_egg(name, col1, col2, inventory_image)
|
||||
if col1 and col2 then
|
||||
local base = "(creatura_spawning_crystal_primary.png^[multiply:#" .. col1 .. ")"
|
||||
local spots = "(creatura_spawning_crystal_secondary.png^[multiply:#" .. col2 .. ")"
|
||||
inventory_image = base .. "^" .. spots
|
||||
end
|
||||
local mod_name = name:split(":")[1]
|
||||
local mob_name = name:split(":")[2]
|
||||
minetest.register_craftitem(mod_name .. ":spawn_" .. mob_name, {
|
||||
description = "Spawn " .. format_name(name),
|
||||
inventory_image = inventory_image,
|
||||
stack_max = 99,
|
||||
on_place = function(itemstack, _, pointed_thing)
|
||||
local mobdef = minetest.registered_entities[name]
|
||||
local spawn_offset = abs(mobdef.collisionbox[2])
|
||||
local pos = minetest.get_pointed_thing_position(pointed_thing, true)
|
||||
pos.y = (pos.y - 0.4) + spawn_offset
|
||||
local object = minetest.add_entity(pos, name)
|
||||
if object then
|
||||
object:set_yaw(random(1, 6))
|
||||
object:get_luaentity().last_yaw = object:get_yaw()
|
||||
end
|
||||
if not creative then
|
||||
itemstack:take_item()
|
||||
return itemstack
|
||||
end
|
||||
end
|
||||
})
|
||||
end
|
||||
|
|
Loading…
Add table
Reference in a new issue