mirror of
https://github.com/ElCeejo/creatura.git
synced 2025-03-15 12:21:24 +00:00
363 lines
No EOL
10 KiB
Lua
363 lines
No EOL
10 KiB
Lua
--------------
|
|
-- Spawning --
|
|
--------------
|
|
|
|
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)
|
|
|
|
-- Math --
|
|
|
|
local abs = math.abs
|
|
local pi = math.pi
|
|
local random = math.random
|
|
|
|
local function vec_raise(v, n)
|
|
return {x = v.x, y = v.y + n, z = v.z}
|
|
end
|
|
|
|
local vec_sub = vector.subtract
|
|
local vec_add = vector.add
|
|
|
|
|
|
-- Registration --
|
|
|
|
local creative = minetest.settings:get_bool("creative_mode")
|
|
|
|
local function format_name(str)
|
|
if str then
|
|
if str:match(":") then str = str:split(":")[2] end
|
|
return (string.gsub(" " .. str, "%W%l", string.upper):sub(2):gsub("_", " "))
|
|
end
|
|
end
|
|
|
|
function creatura.register_spawn_egg(name, col1, col2, inventory_image) -- deprecated
|
|
if col1 and col2 then
|
|
local base = "(creatura_spawning_crystal.png^[multiply:#" .. col1 .. ")"
|
|
local spots = "(creatura_spawning_crystal_overlay.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
|
|
|
|
function creatura.register_spawn_item(name, def)
|
|
local inventory_image
|
|
if not def.inventory_image
|
|
and def.col1 and def.col2 then
|
|
local base = "(creatura_spawning_crystal.png^[multiply:#" .. def.col1 .. ")"
|
|
local spots = "(creatura_spawning_crystal_overlay.png^[multiply:#" .. def.col2 .. ")"
|
|
inventory_image = base .. "^" .. spots
|
|
end
|
|
local mod_name = name:split(":")[1]
|
|
local mob_name = name:split(":")[2]
|
|
def.description = def.description or "Spawn " .. format_name(name)
|
|
def.inventory_image = def.inventory_image or inventory_image
|
|
def.on_place = function(itemstack, player, pointed_thing)
|
|
local pos = minetest.get_pointed_thing_position(pointed_thing, true)
|
|
if minetest.is_protected(pos, player and player:get_player_name() or "") then return end
|
|
local mobdef = minetest.registered_entities[name]
|
|
local spawn_offset = abs(mobdef.collisionbox[2])
|
|
pos.y = (pos.y - 0.49) + spawn_offset
|
|
if def.antispam then
|
|
local objs = minetest.get_objects_in_area(vec_sub(pos, 0.51), vec_add(pos, 0.51))
|
|
for _, obj in ipairs(objs) do
|
|
if obj
|
|
and obj:get_luaentity()
|
|
and obj:get_luaentity().name == name then
|
|
return
|
|
end
|
|
end
|
|
end
|
|
local object = minetest.add_entity(pos, name)
|
|
if object then
|
|
object:set_yaw(random(0, pi * 2))
|
|
object:get_luaentity().last_yaw = object:get_yaw()
|
|
end
|
|
if not minetest.is_creative_enabled(player:get_player_name())
|
|
or def.consume_in_creative then
|
|
itemstack:take_item()
|
|
return itemstack
|
|
end
|
|
end
|
|
minetest.register_craftitem(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_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_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] = {}
|
|
end
|
|
table.insert(creatura.registered_on_spawns[name], func)
|
|
end
|
|
|
|
|
|
-- Utility Functions --
|
|
|
|
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 get_spawnable_mobs(pos)
|
|
local biome = get_biome_name(pos)
|
|
if not biome then biome = "_nil" 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) then
|
|
table.insert(spawnable, k)
|
|
end
|
|
end
|
|
return spawnable
|
|
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 = vector.distance(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 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 = math.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)
|
|
|
|
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)
|
|
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)
|
|
|
|
-- 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
|
|
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,
|
|
})
|
|
|
|
--[[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,
|
|
})]] |