403 lines
12 KiB
Lua
403 lines
12 KiB
Lua
-------------------------------------------------------------------------------
|
|
-- advanced spawning mod
|
|
--
|
|
--@license WTFP
|
|
--@copyright Sapier
|
|
--@author Sapier
|
|
--@date 2013-12-05
|
|
--
|
|
--------------------------------------------------------------------------------
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- @function [parent=#adv_spawning] seed_step
|
|
-- @param self spawner entity
|
|
-- @param dtime time since last call
|
|
--------------------------------------------------------------------------------
|
|
function adv_spawning.seed_step(self,dtime)
|
|
if not self.activated then
|
|
local starttime = adv_spawning.gettime()
|
|
adv_spawning.seed_activate(self)
|
|
adv_spawning.check_time(starttime, "Initializing spawner on_step took way too much time")
|
|
return
|
|
end
|
|
|
|
self.mydtime = self.mydtime + dtime
|
|
|
|
if (self.mydtime < 1/adv_spawning.max_spawning_frequency_hz) then
|
|
return
|
|
end
|
|
|
|
--check if we did finish initialization of our spawner list by now
|
|
if not adv_spawning.seed_scan_for_applyable_spawners(self) then
|
|
return
|
|
end
|
|
|
|
if adv_spawning.quota_enter() then
|
|
self.pending_spawners = {}
|
|
|
|
adv_spawning.seed_countdown_spawners(self,self.mydtime)
|
|
|
|
self.mydtime = 0
|
|
|
|
--check quota again
|
|
adv_spawning.quota_leave()
|
|
if not adv_spawning.quota_enter() then
|
|
return
|
|
end
|
|
|
|
local per_step_count = 0
|
|
local key = nil
|
|
|
|
local starttime = adv_spawning.gettime()
|
|
|
|
while #self.pending_spawners > 0 and
|
|
per_step_count < adv_spawning.max_spawns_per_spawner and
|
|
(not adv_spawning.time_over(10)) do
|
|
|
|
|
|
local rand_spawner = math.random(1,#self.pending_spawners)
|
|
key = self.pending_spawners[rand_spawner]
|
|
|
|
local tries = 1
|
|
|
|
if adv_spawning.spawner_definitions[key].spawns_per_interval ~= nil then
|
|
tries = adv_spawning.spawner_definitions[key].spawns_per_interval
|
|
end
|
|
|
|
while tries > 0 do
|
|
local successfull, permanent_error, reason =
|
|
adv_spawning.handlespawner(key,self.object:getpos())
|
|
|
|
if successfull then
|
|
self.spawning_data[key] =
|
|
adv_spawning.spawner_definitions[key].spawn_interval
|
|
self.spawn_fail_reasons[key] = "successfull spawned"
|
|
else
|
|
self.spawning_data[key] =
|
|
adv_spawning.spawner_definitions[key].spawn_interval/4
|
|
self.spawn_fail_reasons[key] = reason
|
|
end
|
|
|
|
--check quota again
|
|
if not adv_spawning.quota_leave() then
|
|
adv_spawning.dbg_log(2, "spawner " .. key .. " did use way too much time")
|
|
end
|
|
if not adv_spawning.quota_enter() then
|
|
return
|
|
end
|
|
|
|
tries = tries -1
|
|
end
|
|
|
|
|
|
starttime = adv_spawning.check_time(starttime, key .. " for " ..
|
|
adv_spawning.spawner_definitions[key].spawnee .. " did use way too much time")
|
|
|
|
table.remove(self.pending_spawners,rand_spawner)
|
|
per_step_count = per_step_count +1
|
|
end
|
|
|
|
-- if (#self.pending_spawners > 0) then
|
|
-- adv_spawning.dbg_log(3, "Handled " .. per_step_count .. " spawners, spawners left: " .. #self.pending_spawners)
|
|
-- end
|
|
if not adv_spawning.quota_leave() then
|
|
adv_spawning.dbg_log(2, "spawner " .. key .. " did use way too much time")
|
|
end
|
|
end
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- @function [parent=#adv_spawning] seed_activate
|
|
-- @param self spawner entity
|
|
--------------------------------------------------------------------------------
|
|
function adv_spawning.seed_activate(self)
|
|
if adv_spawning.quota_enter() then
|
|
|
|
if adv_spawning.seed_check_for_collision(self) then
|
|
adv_spawning.quota_leave()
|
|
return
|
|
end
|
|
|
|
if self.serialized_data ~= nil then
|
|
self.spawning_data = minetest.deserialize(self.serialized_data)
|
|
end
|
|
|
|
if self.spawning_data == nil then
|
|
self.spawning_data = {}
|
|
end
|
|
|
|
adv_spawning.seed_validate_spawndata(self)
|
|
|
|
self.pending_spawners = {}
|
|
self.spawn_fail_reasons = {}
|
|
self.initialized_spawners = 0
|
|
self.activated = true
|
|
|
|
-- fix unaligned own pos
|
|
local pos = self.object:getpos()
|
|
|
|
pos.x = math.floor(pos.x + 0.5)
|
|
pos.y = math.floor(pos.y + 0.5)
|
|
pos.z = math.floor(pos.z + 0.5)
|
|
|
|
self.object:setpos(pos)
|
|
|
|
if not adv_spawning.quota_leave() then
|
|
adv_spawning.dbg_log(2, "on activate " .. self.name .. " did use way too much time")
|
|
end
|
|
end
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- @function [parent=#adv_spawning] on_rightclick
|
|
-- @param self spawner entity
|
|
-- @param clicker (unused)
|
|
--------------------------------------------------------------------------------
|
|
function adv_spawning.on_rightclick(self, clicker)
|
|
if adv_spawning.debug then
|
|
print("ADV_SPAWNING: time till next spawn: " .. self.mydtime)
|
|
print("ADV_SPAWNING: pending spawners: " .. #self.pending_spawners)
|
|
print("ADV_SPAWNING: Spawner may spawn " .. adv_spawning.table_count(self.spawning_data) .. " mobs:")
|
|
local index = 1
|
|
for key,value in pairs(self.spawning_data) do
|
|
local reason = "unknown"
|
|
|
|
if self.spawn_fail_reasons[key] then
|
|
reason = self.spawn_fail_reasons[key]
|
|
end
|
|
|
|
print(string.format("%3d:",index) .. string.format("%30s ",key) .. string.format("%3d s (", value) .. reason .. ")")
|
|
index = index +1
|
|
end
|
|
end
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- @function [parent=#adv_spawning] seed_initialize
|
|
--------------------------------------------------------------------------------
|
|
function adv_spawning.seed_initialize()
|
|
|
|
local spawner_texture = "adv_spawning_invisible.png^[makealpha:128,0,0^[makealpha:128,128,0"
|
|
local spawner_collisionbox = { 0.0,0.0,0.0,0.0,0.0,0.0}
|
|
|
|
if adv_spawning.debug then
|
|
spawner_texture = "adv_spawning_spawner.png"
|
|
spawner_collisionbox = { -0.5,-0.5,-0.5,0.5,0.5,0.5 }
|
|
end
|
|
|
|
minetest.register_entity("adv_spawning:spawn_seed",
|
|
{
|
|
collisionbox = spawner_collisionbox,
|
|
visual = "sprite",
|
|
textures = { spawner_texture },
|
|
physical = false,
|
|
groups = { "immortal" },
|
|
on_activate = function(self,staticdata,dtime_s)
|
|
self.activated = false
|
|
self.mydtime = dtime_s
|
|
self.serialized_data = staticdata
|
|
self.object:set_armor_groups({ immortal=100 })
|
|
adv_spawning.seed_activate(self)
|
|
end,
|
|
on_step = adv_spawning.seed_step,
|
|
get_staticdata = function(self)
|
|
return minetest.serialize(self.spawning_data)
|
|
end,
|
|
on_rightclick = adv_spawning.on_rightclick
|
|
}
|
|
)
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- @function [parent=#adv_spawning] seed_validate_spawndata
|
|
-- @param self spawner entity
|
|
--------------------------------------------------------------------------------
|
|
function adv_spawning.seed_validate_spawndata(self)
|
|
for key,value in pairs(self.spawning_data) do
|
|
if adv_spawning.spawner_definitions[key] == nil then
|
|
self.spawning_data[key] = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- @function [parent=#adv_spawning] seed_countdown_spawners
|
|
-- @param self spawner entity
|
|
-- @param dtime time to decrement spawners
|
|
--------------------------------------------------------------------------------
|
|
function adv_spawning.seed_countdown_spawners(self,dtime)
|
|
|
|
for key,value in pairs(self.spawning_data) do
|
|
self.spawning_data[key] = self.spawning_data[key] - dtime
|
|
|
|
if self.spawning_data[key] < 0 then
|
|
table.insert(self.pending_spawners,key)
|
|
end
|
|
end
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- @function [parent=#adv_spawning] seed_check_for_collision
|
|
-- @param self spawner entity
|
|
-- @return true/false
|
|
--------------------------------------------------------------------------------
|
|
function adv_spawning.seed_check_for_collision(self)
|
|
assert(self ~= nil)
|
|
local pos = self.object:getpos()
|
|
local objects = minetest.get_objects_inside_radius(pos, 0.5)
|
|
|
|
if objects == nil then
|
|
return false
|
|
end
|
|
|
|
-- check if any of those found objects is a spawning seed
|
|
for k,v in ipairs(objects) do
|
|
local entity = v:get_luaentity()
|
|
|
|
if entity ~= nil then
|
|
if entity.name == "adv_spawning:spawn_seed" and
|
|
entity.object ~= self.object then
|
|
self.object:remove()
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
|
|
function adv_spawning.init_spawner(self, pos, name, spawnerdef)
|
|
local starttime = adv_spawning.gettime()
|
|
|
|
if self.spawner_init_state ~= nil then
|
|
self.spawner_init_state = "initial"
|
|
end
|
|
|
|
local starttime = adv_spawning.gettime()
|
|
if self.spawner_init_state == "initial" then
|
|
|
|
--check if cyclic spawning is enabled
|
|
if spawnerdef.cyclic_spawning ~= nil and
|
|
spawnerdef.cyclic_spawning == false then
|
|
self.spawning_data[name] = nil
|
|
return true
|
|
end
|
|
self.spawner_init_state = "abs_height"
|
|
end
|
|
|
|
starttime = adv_spawning.check_time(starttime, name .. "cyclic check")
|
|
if self.spawner_init_state == "abs_height" then
|
|
--if spawner is far away from spawn area don't even try to spawn
|
|
if spawnerdef.absolute_height ~= nil then
|
|
if spawnerdef.absolute_height.min ~= nil and
|
|
spawnerdef.absolute_height.min
|
|
> pos.y + (adv_spawning.spawner_distance/2) then
|
|
self.spawning_data[name] = nil
|
|
return true
|
|
end
|
|
|
|
if spawnerdef.absolute_height.max ~= nil
|
|
and spawnerdef.absolute_height.max
|
|
< pos.y - (adv_spawning.spawner_distance/2) then
|
|
self.spawning_data[name] = nil
|
|
return true
|
|
end
|
|
end
|
|
self.spawner_init_state = "environment"
|
|
end
|
|
|
|
starttime = adv_spawning.check_time(starttime, name .. "height check")
|
|
if self.spawner_init_state == "environment" then
|
|
|
|
local runidx = 1
|
|
local radius = adv_spawning.spawner_distance / 2
|
|
|
|
if self.spawnerinit_env_radius ~= nil then
|
|
runidx = self.spawnerinit_env_radius
|
|
end
|
|
|
|
local found = false
|
|
|
|
for i = runidx , radius, 1 do
|
|
adv_spawning.quota_leave()
|
|
|
|
if not adv_spawning.quota_enter() then
|
|
self.spawnerinit_env_radius = runidx
|
|
return false
|
|
end
|
|
|
|
if spawnerdef.spawn_inside == nil then
|
|
print(name .. " tries to spawn within nil")
|
|
assert(false)
|
|
end
|
|
|
|
local resultpos = adv_spawning.find_nodes_in(pos, runidx, runidx, spawnerdef.spawn_inside)
|
|
|
|
if (resultpos ~= nil) then
|
|
local node = minetest.get_node_or_nil(resultpos)
|
|
found = true
|
|
break
|
|
end
|
|
end
|
|
|
|
starttime = adv_spawning.check_time(starttime, name ..
|
|
" at environment check radius was: " .. radius ..
|
|
" env: " .. dump(spawnerdef.spawn_inside))
|
|
|
|
if not found then
|
|
self.spawning_data[name] = nil
|
|
end
|
|
end
|
|
|
|
self.spawner_init_state = "initial"
|
|
self.spawning_data[name] = spawnerdef.spawn_interval * math.random()
|
|
return true
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- @function [parent=#adv_spawning] seed_scan_for_applyable_spawners
|
|
-- @param self spawner entity
|
|
-- @return true/false
|
|
--------------------------------------------------------------------------------
|
|
function adv_spawning.seed_scan_for_applyable_spawners(self)
|
|
|
|
if self.initialized_spawners >=
|
|
adv_spawning.table_count(adv_spawning.spawner_definitions) then
|
|
return true
|
|
end
|
|
|
|
local runindex = 0
|
|
|
|
if self.spawner_init_idx ~= nil then
|
|
runindex = self.spawner_init_idx
|
|
end
|
|
|
|
local pos = self.object:getpos()
|
|
for key,value in pairs(adv_spawning.spawner_definitions) do
|
|
if not adv_spawning.quota_enter() then
|
|
return false
|
|
end
|
|
|
|
local continue = false
|
|
|
|
if runindex >= self.initialized_spawners then
|
|
self.initialized_spawners = self.initialized_spawners + 1
|
|
else
|
|
continue = true
|
|
end
|
|
|
|
|
|
if not continue then
|
|
runindex = runindex + 1
|
|
if not adv_spawning.init_spawner(self, pos, key, value) then
|
|
return false
|
|
end
|
|
end
|
|
|
|
adv_spawning.quota_leave()
|
|
end
|
|
|
|
return self.initialized_spawners == #adv_spawning.spawner_definitions
|
|
end |