[minetest-mod-advspawning] 01/02: Imported Upstream version 0.0.13
Julien Puydt
julien.puydt at laposte.net
Sun Jul 10 14:54:35 UTC 2016
This is an automated email from the git hooks/post-receive script.
jpuydt-guest pushed a commit to branch master
in repository minetest-mod-advspawning.
commit d190204ca5540fb0f78a8ed62f595f7906e40a6f
Author: Julien Puydt <julien.puydt at laposte.net>
Date: Sat Jul 9 15:38:16 2016 +0200
Imported Upstream version 0.0.13
---
README.txt | 220 +++++
api.lua | 53 ++
init.lua | 34 +
internal.lua | 1691 +++++++++++++++++++++++++++++++++++
spawn_seed.lua | 403 +++++++++
spawndef_checks.lua | 73 ++
testmod/depends.txt | 1 +
testmod/init.lua | 154 ++++
testmod/textures/testmod_num1.png | Bin 0 -> 327 bytes
testmod/textures/testmod_num2.png | Bin 0 -> 701 bytes
testmod/textures/testmod_num3.png | Bin 0 -> 790 bytes
testmod/textures/testmod_num4.png | Bin 0 -> 543 bytes
textures/adv_spawning_invisible.png | Bin 0 -> 180 bytes
textures/adv_spawning_spawner.png | Bin 0 -> 1543 bytes
14 files changed, 2629 insertions(+)
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..5b14caa
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,220 @@
+********************************************************************************
+* *
+* Advanced spawning mod (adv_spawning) 0.0.6 *
+* *
+* URL: http://github.com/sapier/adv_spawning *
+* Author: sapier *
+* *
+********************************************************************************
+
+
+Description:
+--------------------
+Advances spawning mod is designed to provide a feature rich yet easy to use
+spawner for entites. It's purpose is to support spawning within large numbers
+of different environments. While adv_spawning supports a wide configurable range
+of spawning situations, it's performance impact is clamped at minimal level.
+To achieve this performance goal adv_spawning is intended for low frequency
+entity spawning only. Typical spawn rate will be a low single digit count of
+entities per second throughout whole world.
+
+
+API:
+--------------------
+adv_spawning.register(spawner_name,spawning_def) --> successfull true/false
+^ register a spawn to adv_spawning mechanisms
+^ spawner_name a unique spawner name
+^ spawning_def is configuration of spawner
+
+adv_spawning.statistics() --> statistics data about spawning
+
+Spawning definition:
+--------------------
+{
+ spawnee = "some_mod:entity_name", -- name of entity to spawn OR function to be called e.g. func(pos)
+ absolute_height = -- absolute y value to check
+ {
+ min = 1, -- minimum height to spawn at
+ max = 5 -- maximum height to spawn at
+ }
+
+ relative_height = --relative y value to next non environment node
+ {
+ min = 1, -- minimum height above non environment node
+ max = 5 -- maximum height above non environment node
+ }
+
+ spawn_inside, -- [MANDATORY] list of nodes to to spawn within (see spawn inside example)
+ surfaces, -- list of nodes to spawn uppon (same format as spawn_inside)
+
+ entities_around = -- list of surrounding entity definitions
+ {
+ entity_def_1,
+ entity_def_2,
+ ...
+ },
+
+ nodes_around = -- list of surrounding node definitions
+ {
+ node_def_1,
+ node_def_2,
+ ...
+ },
+
+ light_around = -- list of light around definitions
+ {
+ light_around_def_1,
+ light_around_def_2,
+ ...
+ },
+
+ humidity_around = -- list of humidity around definitions
+ {
+ humidity_around_def_1,
+ humidity_around_def_2,
+ ...
+ },
+
+ temperature_around = -- list of temperature around definitions
+ {
+ temperature_around_def_1,
+ temperature_around_def_2,
+ ...
+ },
+
+ mapgen = -- configuration for initial mapgen spawning
+ {
+ enabled = true, -- mapgen spawning enabled or not
+ retries = 5, -- number of tries to spawn a entity prior giving up
+ spawntotal = 3, -- number of entities to try on mapgen
+ },
+
+
+ flat_area = -- check for amount of flat area around,
+ -- (only usefull for ground bound mobs)
+ {
+ range = 3, -- range to be checked for flattness
+ deviation = 2, -- maximum number of nodes not matching flat check
+ },
+
+ daytimes = -- do only spawn within these daytimes
+ {
+ daytime_def_1,
+ daytime_def_2,
+ ...
+ }
+
+ collisionbox = {}, -- collisionbox of entity to spawn (usually same as used for entiy itself)
+ spawn_interval = 200, -- [MANDATORY] interval to try to spawn a entity
+ spawns_per_interval = 1, -- try to spawn multiple mobs (if time available)
+ custom_check = fct(pos,spawndef), -- a custom check to be called return true for pass, false for not pass
+ cyclic_spawning = true -- spawn per spawner step (defaults to true)
+}
+
+Light around definition:
+{
+ type = "TIMED_MIN", -- type of light around check
+ -- TIMED_MIN/TIMED_MAX at least this light level at specified time within whole distance
+ -- OVERALL_MAX,OVERALL_MIN at least this light level at any time
+ -- CURRENT_MIN,CURRENT_MAX at least this light level now
+ distance = 2, -- distance to check (be carefull high distances may cause lag)
+ -- WARNING: light check is a very very heavy operation don't use large distances
+ threashold = 2, -- value to match at max/at least to pass this check
+ time = 6000 -- time to do check at (TIMED_MIN/TIMED_MAX only)
+}
+
+Surrounding node definition:
+{
+ type = "MIN", -- type of surround check valid types are MIN and MAX
+ name = { "default:tree" },-- name(s) of node(s) to check
+ distance = 7, -- distance to look for node
+ threshold = 1 -- number to match at max/at least to pass this check
+}
+
+Surrounding entity definition:
+{
+ type = "MIN", -- type of surround check valid types are MIN and MAX
+ entityname = "mod:entity", -- name of entity to check (nil to match all)
+ distance = 3, -- distance to look for this entity
+ threshold = 2 -- number to match at max/at least to pass this check
+}
+
+Surrounding temperature definition:
+{
+ type = "MIN", -- type of surround check valid types are MIN and MAX
+ distance = 3, -- distance to look for this temperature
+ threshold = 2 -- number to match at max/at least to pass this check
+}
+
+Surrounding humidity definition:
+{
+ type = "MIN", -- type of surround check valid types are MIN and MAX
+ distance = 3, -- distance to look for this humidity
+ threshold = 2 -- number to match at max/at least to pass this check
+}
+
+spawn_inside definition (list of nodenames):
+{
+ "air",
+ "default:water_source",
+ "default:water_flowing"
+}
+
+Daytime definition:
+{
+ begin = 0.0, --minimum daytime
+ stop = 0.25, --maximum daytime
+}
+
+Statistics:
+{
+ session =
+ {
+ spawners_created = 0, -- number of spawners created this session
+ entities_created = 0, -- number of spawns done
+ steps = 0, -- number of steps
+ },
+ step =
+ {
+ min = 0, -- minimum time required for a single step
+ max = 0, -- maximum time required for a single step
+ last = 0, -- last steps time
+ },
+ load =
+ {
+ min = 0, -- minimum load caused
+ max = 0, -- maximum load caused
+ cur = 0, -- load caused in last step
+ avg = 0 -- average load caused
+ }
+}
+
+Settings:
+ adv_spawning_validate_spawners = false
+ ^ make advanced_spawning check area around active players for lost spawner seeds
+
+Changelog:
+
+0.0.9
+ -Fix broken spawner initialization
+ -Add support for regenerating spawner seeds (only around active players)
+ set >>adv_spawning_validate_spawners<< to true if you want advanced spawning
+ to do this.
+ Note: this might need some additional cpu time
+
+0.0.8
+ -Fix large steps caused by uninterruptable spawn seed initialization within activation
+
+0.0.7
+ -handle time steps backward without assertion
+
+0.0.6
+ -add configuration option adv_spawing.debug to show or hide spawner entities
+ -fix quota overflow calculation
+ -add rightclick function to show debug info
+
+0.0.5
+ -fix MIN/MAX to always return a number value
+ -fix default activity range to use a initial value if not manually configured
+ -don't calculate statistics with zero dtime
+ -fix invalid debug log using old variable name
\ No newline at end of file
diff --git a/api.lua b/api.lua
new file mode 100644
index 0000000..298d742
--- /dev/null
+++ b/api.lua
@@ -0,0 +1,53 @@
+-------------------------------------------------------------------------------
+-- advanced spawning mod
+--
+-- at license WTFP
+-- at copyright Sapier
+-- at author Sapier
+-- at date 2013-12-05
+--
+-------------------------------------------------------------------------------
+
+--------------------------------------------------------------------------------
+-- @function [parent=#adv_spawning] register
+-- @param spawn_definition a definition to use for spawning
+--------------------------------------------------------------------------------
+function adv_spawning.register(spawner_name,spawning_def)
+ if adv_spawning.spawner_definitions[spawner_name] == nil then
+
+
+ if not adv_spawning.verify_check_entities_around(spawning_def.entities_around) then
+ return false
+ end
+
+ if not adv_spawning.verify_check_nodes_around(spawning_def.nodes_around) then
+ return false
+ end
+
+ adv_spawning.spawner_definitions[spawner_name] = spawning_def
+ adv_spawning.dbg_log(0, "registering spawner \"" .. spawner_name .. "\"")
+ adv_spawning.dbg_log(0, "now handling: " ..
+ adv_spawning.table_count(adv_spawning.spawner_definitions) ..
+ " spawner definitions")
+ return true
+ else
+ return false
+ end
+end
+
+--------------------------------------------------------------------------------
+-- @function [parent=#adv_spawning] get_statistics
+-- @return get snapshot of statistics
+--------------------------------------------------------------------------------
+function adv_spawning.get_statistics()
+ return minetest.deserialize(minetest.serialize(adv_spawning.statistics))
+end
+
+
+--------------------------------------------------------------------------------
+-- @function [parent=#adv_spawning] get_spawner_density
+-- @return get snapshot of statistics
+--------------------------------------------------------------------------------
+function adv_spawning.get_spawner_density()
+ return adv_spawning.spawner_distance,adv_spawning.spawner_y_offset
+end
\ No newline at end of file
diff --git a/init.lua b/init.lua
new file mode 100644
index 0000000..411fc9d
--- /dev/null
+++ b/init.lua
@@ -0,0 +1,34 @@
+-------------------------------------------------------------------------------
+-- advanced spawning mod
+--
+-- at license WTFP
+-- at copyright Sapier
+-- at author Sapier
+-- at date 2013-12-05
+--
+-------------------------------------------------------------------------------
+
+local version = "0.0.13"
+
+if adv_spawning ~= nil then
+ core.log("error", "MOD: adv_spawning requires adv_spawning variable to be available")
+end
+
+--------------------------------------------------------------------------------
+-- @type adv_spawning base element for usage of adv_spawning
+-- -----------------------------------------------------------------------------
+adv_spawning = {}
+
+adv_spawning.debug = core.setting_get("adv_spawning.debug")
+
+local adv_modpath = core.get_modpath("adv_spawning")
+
+dofile (adv_modpath .. "/internal.lua")
+dofile (adv_modpath .. "/spawndef_checks.lua")
+dofile (adv_modpath .. "/api.lua")
+dofile (adv_modpath .. "/spawn_seed.lua")
+
+
+adv_spawning.initialize()
+
+core.log("action", "Advanced spawning mod version " .. version .. " loaded")
diff --git a/internal.lua b/internal.lua
new file mode 100644
index 0000000..caa595e
--- /dev/null
+++ b/internal.lua
@@ -0,0 +1,1691 @@
+-------------------------------------------------------------------------------
+-- advanced spawning mod
+--
+-- at license WTFP
+-- at copyright Sapier
+-- at author Sapier
+-- at date 2013-12-05
+--
+--------------------------------------------------------------------------------
+
+--------------------------------------------------------------------------------
+-- @function [parent=#adv_spawning] MAX
+-- @param a first value to compare
+-- @param b second value to compare
+-- @return maximum of a and b
+--------------------------------------------------------------------------------
+function adv_spawning.MAX(a,b)
+ if a == nil then
+ return (b or 0)
+ end
+ if b == nil then
+ return (a or 0)
+ end
+ if a > b then
+ return (a or 0)
+ else
+ return (b or 0)
+ end
+end
+
+--------------------------------------------------------------------------------
+-- @function [parent=#adv_spawning] MIN
+-- @param a first value to compare
+-- @param b second value to compare
+-- @return minimum of a and b
+--------------------------------------------------------------------------------
+function adv_spawning.MIN(a,b)
+ if a == nil then
+ return (b or 0)
+ end
+ if b == nil then
+ return (a or 0)
+ end
+ if a > b then
+ return (b or 0)
+ else
+ return (a or 0)
+ end
+end
+
+
+--------------------------------------------------------------------------------
+-- @function [parent=#adv_spawning] initialize
+--------------------------------------------------------------------------------
+function adv_spawning.initialize()
+
+ --initialize data
+ adv_spawning.quota_starttime = nil
+ adv_spawning.quota_reload = 100
+ adv_spawning.quota_left = adv_spawning.quota_reload
+ adv_spawning.max_spawns_per_spawner = 2
+ adv_spawning.spawner_distance = 70
+ adv_spawning.spawner_y_offset = 20
+ adv_spawning.max_spawning_frequency_hz = 5
+ adv_spawning.max_mapgen_tries_per_step = 3
+ adv_spawning.spawner_warned = {}
+ adv_spawning.loglevel = 0
+ adv_spawning.spawner_validation_delta = 0
+ adv_spawning.spawner_validation_interval = 30
+
+ adv_spawning.active_range = minetest.setting_get("active_block_range")
+
+ if (adv_spawning.active_range == nil) then
+ adv_spawning.log("info", "No \"active_block_range\" set, defaulting to 5")
+ adv_spawning.active_range = 5
+ else
+ adv_spawning.active_range = adv_spawning.active_range * 16
+ end
+
+ adv_spawning.spawner_definitions = {}
+ adv_spawning.mapgen_jobqueue = {}
+ adv_spawning.statistics =
+ {
+ session =
+ {
+ spawners_created = 0,
+ entities_created = 0,
+ steps = 0,
+ },
+ step =
+ {
+ min = 0,
+ max = 0,
+ last = 0,
+ },
+ load =
+ {
+ min = 0,
+ max = 0,
+ cur = 0,
+ avg = 0
+ }
+ }
+
+ adv_spawning.gettime = function() return os.clock() * 1000 end
+
+ if type(minetest.get_us_time) == "function" then
+ adv_spawning.log("action", "Using minetest.get_us_time() for quota calc")
+ adv_spawning.gettime = function()
+ return minetest.get_us_time() / 1000
+ end
+ else
+ if socket == nil then
+ local status, module = pcall(require, 'socket')
+
+ if status and type(module.gettime) == "function" then
+ adv_spawning.log("action", "Using socket.gettime() for quota calc")
+ adv_spawning.gettime = function()
+ return socket.gettime()*1000
+ end
+ end
+ end
+ end
+
+ --register global onstep
+ minetest.register_globalstep(adv_spawning.global_onstep)
+
+ --register seed spawner entity
+ adv_spawning.seed_initialize()
+
+ --register mapgen hook
+ minetest.register_on_generated(adv_spawning.mapgen_hook)
+end
+
+
+--------------------------------------------------------------------------------
+-- @function [parent=#adv_spawning] mapgen_hook
+-- @param minp minimal position of block
+-- @param maxp maximal position of block
+-- @param blockseed seed for this block
+--------------------------------------------------------------------------------
+function adv_spawning.mapgen_hook(minp,maxp,blockseed)
+ if adv_spawning.quota_enter(true) then
+ --find positions within current block to place a spawner seed
+ local start_x =
+ math.floor(minp.x/adv_spawning.spawner_distance)
+ * adv_spawning.spawner_distance
+ local start_y =
+ (math.floor(minp.y/adv_spawning.spawner_distance)
+ * adv_spawning.spawner_distance)
+ + adv_spawning.spawner_y_offset
+ local start_z =
+ math.floor(minp.z/adv_spawning.spawner_distance)
+ * adv_spawning.spawner_distance
+
+ for x=start_x,maxp.x,adv_spawning.spawner_distance do
+ for y=start_y,maxp.y,adv_spawning.spawner_distance do
+ for z=start_z,maxp.z,adv_spawning.spawner_distance do
+
+ if x > minp.x and
+ y > minp.y and
+ z > minp.z then
+ if not adv_spawning.quota_leave() then
+ adv_spawning.dbg_log(2, "mapgen_hook did use way too much time 1")
+ end
+ minetest.add_entity({x=x,y=y,z=z},"adv_spawning:spawn_seed")
+ adv_spawning.quota_enter(true)
+ adv_spawning.log("info", "adv_spawning: adding spawner entity at "
+ .. minetest.pos_to_string({x=x,y=y,z=z}))
+ adv_spawning.statistics.session.spawners_created =
+ adv_spawning.statistics.session.spawners_created +1
+ end
+ end
+ end
+ end
+
+ adv_spawning.queue_mapgen_jobs(minp,maxp)
+ if not adv_spawning.quota_leave() then
+ adv_spawning.dbg_log(2, "mapgen_hook did use way too much time 2")
+ end
+ else
+ assert("Mapgen hook could not be executed" == nil)
+ end
+end
+
+--------------------------------------------------------------------------------
+-- @function [parent=#adv_spawning] global_onstep
+-- @param dtime time since last call
+--------------------------------------------------------------------------------
+function adv_spawning.global_onstep(dtime)
+
+ if dtime == 0 then
+ -- don't try to calc differences for no time
+ return
+ end
+
+ adv_spawning.statistics.step.last =
+ math.floor(adv_spawning.quota_reload - adv_spawning.quota_left + 0.5)
+
+ adv_spawning.statistics.step.max = adv_spawning.MAX(adv_spawning.statistics.step.last,
+ adv_spawning.statistics.step.max)
+
+ adv_spawning.statistics.step.min = adv_spawning.MIN(adv_spawning.statistics.step.last,
+ adv_spawning.statistics.step.min)
+
+ adv_spawning.statistics.session.steps =
+ adv_spawning.statistics.session.steps + 1
+
+ adv_spawning.statistics.load.cur =
+ adv_spawning.statistics.step.last/(dtime*1000)
+
+ adv_spawning.statistics.load.max = adv_spawning.MAX(adv_spawning.statistics.load.cur,
+ adv_spawning.statistics.load.max)
+
+ adv_spawning.statistics.load.min = adv_spawning.MIN(adv_spawning.statistics.load.cur,
+ adv_spawning.statistics.load.min)
+
+ adv_spawning.statistics.load.avg =
+ ( (adv_spawning.statistics.load.avg *
+ (adv_spawning.statistics.session.steps-1)) +
+ adv_spawning.statistics.load.cur) /
+ adv_spawning.statistics.session.steps
+
+ if core.is_yes(
+ core.setting_get("adv_spawning_validate_spawners")) then
+
+ adv_spawning.spawner_validation_delta =
+ adv_spawning.spawner_validation_delta + dtime
+
+ if adv_spawning.spawner_validation_delta >
+ adv_spawning.spawner_validation_interval then
+
+ if adv_spawning.quota_enter() then
+ local playerlist = core.get_connected_players()
+
+ for k,v in ipairs(playerlist) do
+ if not adv_spawning.time_over(10) then
+ adv_spawning.refresh_spawners(v:getpos())
+ else
+ break
+ end
+ end
+
+ adv_spawning.spawner_validation_delta = 0
+
+ end
+ end
+ end
+
+ --reduce following quota by overtime from last step
+ if adv_spawning.quota_left < 0 then
+ adv_spawning.quota_left =
+ adv_spawning.MAX(0,adv_spawning.quota_left + adv_spawning.quota_reload)
+ else
+ adv_spawning.quota_left = adv_spawning.quota_reload
+ end
+
+ if adv_spawning.quota_enter() then
+ adv_spawning.handle_mapgen_spawning()
+ if not adv_spawning.quota_leave() then
+ adv_spawning.dbg_log(2, "globalstep took to long")
+ end
+ end
+end
+
+--------------------------------------------------------------------------------
+-- @function [parent=#adv_spawning] quota_enter
+-- @param force ignore quota but start calculation
+-- @return true/false
+--------------------------------------------------------------------------------
+function adv_spawning.quota_enter(force)
+ --ONLY enable this one if you're quite sure there aren't bugs in
+ --assert(adv_spawning.quota_starttime == nil)
+ local retval = false
+
+ if adv_spawning.quota_left <= 0 then
+
+ if force == true then
+ if adv_spawning.quota_left < -10 then
+ adv_spawning.dbg_log(1, "Quota: task is too important to skip do it anyway," ..
+ " quota already passed by: " ..
+ string.format("%.2f ms",adv_spawning.quota_left))
+ end
+ retval = true
+ else
+ if adv_spawning.quota_left * -2 > adv_spawning.quota_reload then
+ adv_spawning.dbg_log(1, "Quota: no time left: " ..
+ string.format("%.2f ms",adv_spawning.quota_left))
+ end
+ end
+ else
+ retval = true
+ end
+-- print("+++++++++++++++++Quota enter+++++++++++++++++++++")
+-- print(debug.traceback())
+-- print("+++++++++++++++++++++++++++++++++++++++++++++++++")
+ if retval then
+ adv_spawning.quota_starttime = adv_spawning.gettime()
+ end
+
+ return retval
+end
+
+--------------------------------------------------------------------------------
+-- @function [parent=#adv_spawning] time_over
+-- @param minimum minimal value required to be left
+-- @return true/false
+--------------------------------------------------------------------------------
+function adv_spawning.time_over(minimum)
+ assert(adv_spawning.quota_starttime ~= nil)
+-- if adv_spawning.quota_starttime == nil then
+-- return
+-- end
+
+ if minimum == nil then
+ minimum = 0
+ end
+
+ local now = adv_spawning.gettime()
+
+ local time_passed = now - adv_spawning.quota_starttime
+
+ if (time_passed < 0) then
+ if adv_spawning.timebackwardwarning ~= true then
+ core.log("error", "ADV_SPAWNING: Error either there's a bug in time"
+ .." calculation\n or your time just went backwards: old timestamp: "
+ .. adv_spawning.quota_starttime .. " current_time: " .. now .. "\n")
+ adv_spawning.timebackwardwarning = true
+
+ end
+ return true
+ end
+
+ return (adv_spawning.quota_left - time_passed) < minimum
+end
+
+--------------------------------------------------------------------------------
+-- @function [parent=#adv_spawning] quota_leave
+--------------------------------------------------------------------------------
+function adv_spawning.quota_leave()
+ --assert(adv_spawning.quota_starttime ~= nil)
+ if adv_spawning.quota_starttime == nil then
+ return
+ end
+
+ local now = adv_spawning.gettime()
+
+ local time_passed = now - adv_spawning.quota_starttime
+
+ if (time_passed < 0) then
+ if adv_spawning.timebackwardwarning ~= true then
+ core.log("error", "ADV_SPAWNING: Error either there's a bug in time"
+ .." calculation\n or your time just went backwards: old timestamp: "
+ .. adv_spawning.quota_starttime .. " current_time: " .. now .. "\n")
+ adv_spawning.timebackwardwarning = true
+ end
+ else
+ adv_spawning.quota_left = adv_spawning.quota_left - time_passed
+ end
+
+ if (adv_spawning.quota_left < -adv_spawning.quota_reload) then
+ adv_spawning.dbg_log(1, "excessive overtime, quota remaining: " .. adv_spawning.quota_left)
+ return false
+ end
+
+ adv_spawning.quota_starttime = nil
+ --print("-----------------Quota leave----------------------")
+ return true
+end
+
+--------------------------------------------------------------------------------
+-- @function [parent=#adv_spawning] handlespawner
+-- @param spawnername unique name of spawner
+-- @param spawnerpos position of spawner
+-- @param minp (OPTIONAL) override spawner defaults
+-- @param maxp (OPTIONAL) override spawner defaults
+-- @param ignore_active_area set to true for mapgen spawning
+-- @return successfull true/false, permanent_error true,false, reason_string
+--------------------------------------------------------------------------------
+function adv_spawning.handlespawner(spawnername,spawnerpos,minp,maxp,ignore_active_area)
+
+ local permanent_error = false
+ local spawndef = adv_spawning.spawner_definitions[spawnername]
+
+ if not adv_spawning.check_daytime(spawndef.daytimes) then
+ adv_spawning.log("info","didn't meet daytime check")
+ return false,nil, "daytimecheck failed"
+ end
+
+ local max_x = spawnerpos.x + adv_spawning.spawner_distance/2
+ local min_x = spawnerpos.x - adv_spawning.spawner_distance/2
+
+ local max_z = spawnerpos.z + adv_spawning.spawner_distance/2
+ local min_z = spawnerpos.z - adv_spawning.spawner_distance/2
+
+ local upper_y = spawnerpos.y + adv_spawning.spawner_distance/2
+ local lower_y = spawnerpos.y - adv_spawning.spawner_distance/2
+
+ if minp ~= nil then
+ min_x = minp.x
+ min_z = minp.z
+ lower_y = minp.y
+ end
+
+ if maxp ~= nil then
+ max_x = maxp.x
+ max_z = maxp.z
+ upper_y = maxp.y
+ end
+
+ --get random pos
+ local new_pos = {}
+
+ new_pos.x = math.random(min_x,max_x)
+ new_pos.z = math.random(min_z,max_z)
+
+ local yreason = "ukn"
+
+ --check if entity is configured to spawn at surface
+ if spawndef.relative_height == nil or
+ (spawndef.relative_height.max ~= nil and
+ spawndef.relative_height.max <= 1) then
+ new_pos.y, yreason = adv_spawning.get_surface(lower_y,upper_y,new_pos,
+ spawndef.spawn_inside)
+ else
+ if spawndef.spawn_inside == nil then
+ print("ERROR: " .. spawnername .. " tries to spawn within nil")
+ assert(false)
+ end
+ new_pos.y, yreason = adv_spawning.get_relative_pos(lower_y,upper_y,new_pos,
+ spawndef.spawn_inside,
+ spawndef.relative_height,
+ spawndef.absolute_height)
+ end
+
+ --check if we did found a position within relative range
+ if new_pos.y == nil then
+ new_pos.y="?"
+ adv_spawning.log("info",
+ minetest.pos_to_string(new_pos) .. " didn't find a suitable y pos "
+ .. lower_y .. "<-->" .. upper_y )
+ return false, nil, "didn't find a valid ypos at " .. minetest.pos_to_string(new_pos)
+ .. " " .. lower_y .. "<-->" .. upper_y .. " rsn: " .. yreason
+ end
+
+ --check absolute height
+ local abs_height_retval, abs_height_rsn = adv_spawning.check_absolute_height(new_pos,spawndef.absolute_height)
+ if not abs_height_retval then
+ adv_spawning.log("info",
+ minetest.pos_to_string(new_pos) .. " didn't meet absolute height check")
+ return false, true, "absolute height check failed rsn: " .. abs_height_rsn
+ end
+
+ --check active area
+ if not ignore_active_area and not adv_spawning.check_active_block(new_pos) then
+ adv_spawning.log("info",
+ minetest.pos_to_string(new_pos) .. " didn't meet active area check")
+ return false, nil , "area check failed"
+ end
+
+ --check surface
+ --NOTE needs to be done before collision box check as y pos may be modified there
+ if not adv_spawning.check_surface(new_pos,
+ spawndef.surfaces,
+ spawndef.relative_height,
+ spawndef.spawn_inside) then
+ adv_spawning.log("info",
+ minetest.pos_to_string(new_pos) ..
+ " didn't meet surface check, is: " ..
+ minetest.get_node({x=new_pos.x,z=new_pos.z,y=new_pos.y-1}).name)
+ return false, nil, "surface check failed"
+ end
+
+ --flat area check
+ --NOTE needs to be done before collision box check as y pos may be modified there
+ if not adv_spawning.check_flat_area(new_pos,
+ spawndef.flat_area,
+ spawndef.spawn_inside,
+ spawndef.surfaces) then
+ adv_spawning.log("info",
+ minetest.pos_to_string(new_pos) .. " didn't meet flat area check")
+ return false, nil, "flat area check failed"
+ end
+
+ --check collisionbox
+ local checkresult,y_pos =
+ adv_spawning.check_collisionbox(new_pos,
+ spawndef.collisionbox,spawndef.spawn_inside)
+
+ if checkresult and y_pos ~= nil then
+ new_pos.y = y_pos
+ end
+
+ if not checkresult then
+ adv_spawning.log("info",
+ minetest.pos_to_string(new_pos) .. " didn't meet collisionbox check")
+ return false, nil, "collision box check failed"
+ end
+
+ --check entities around
+ if not adv_spawning.check_entities_around(new_pos,spawndef.entities_around) then
+ adv_spawning.log("info",
+ minetest.pos_to_string(new_pos) .. " didn't meet entities check")
+ return false, nil, "entitie around check failed"
+ end
+
+ --check nodes around
+ if not adv_spawning.check_nodes_around(new_pos,spawndef.nodes_around) then
+ adv_spawning.log("info",
+ minetest.pos_to_string(new_pos) .. " didn't meet nodes check")
+ return false, nil, "nodes around check failed"
+ end
+
+ --check light around
+ if not adv_spawning.check_light_around(new_pos,spawndef.light_around) then
+ adv_spawning.log("info",
+ minetest.pos_to_string(new_pos) .. " didn't meet light check")
+ return false, nil, "light check failed"
+ end
+
+-- ONLY use this if you have luajit
+-- --check light around
+-- if not adv_spawning.check_light_around_voxel(new_pos,spawndef.light_around) then
+-- adv_spawning.log("info",
+-- minetest.pos_to_string(new_pos) .. " didn't meet light check")
+-- return false, nil, "luajit light check failed"
+-- end
+
+ --check humidity
+ if not adv_spawning.check_humidity_around(new_pos,spawndef.humidity_around) then
+ adv_spawning.log("info",
+ minetest.pos_to_string(new_pos) .. " didn't meet humidity check")
+ return false, nil, "humidity check failed"
+ end
+
+ --check temperature
+ if not adv_spawning.check_temperature_around(new_pos,spawndef.temperature_around) then
+ adv_spawning.log("info",
+ minetest.pos_to_string(new_pos) .. " didn't meet temperature check")
+ return false, nil, "temperature check failed"
+ end
+
+ --custom check
+ if (spawndef.custom_check ~= nil and
+ type(spawndef.custom_check) == "function") then
+
+ local retval, reason = spawndef.custom_check(new_pos,spawndef)
+
+ if not reason then
+ reason = "custom check failed"
+ end
+
+ if not retval then
+ adv_spawning.log("info",
+ minetest.pos_to_string(new_pos) .. " didn't meet custom check")
+ return false, nil, reason
+ end
+ end
+
+ --do spawn
+ --print("Now spawning: " .. spawndef.spawnee .. " at " ..
+ -- minetest.pos_to_string(new_pos))
+
+ if type(spawndef.spawnee) == "function" then
+ spawndef.spawnee(new_pos)
+ else
+ minetest.add_entity(new_pos,spawndef.spawnee)
+ end
+
+ adv_spawning.statistics.session.entities_created =
+ adv_spawning.statistics.session.entities_created +1
+ return true
+end
+
+--------------------------------------------------------------------------------
+-- @function [parent=#adv_spawning] get_surface
+-- @param y_min minumum relevant y pos
+-- @param y_max maximum relevant y pos
+-- @param new_pos position to spawn at
+-- @param spawn_inside nodes to spawn at
+-- @return y-value of last spawnable node
+--------------------------------------------------------------------------------
+function adv_spawning.get_surface(y_min,y_max,new_pos,spawn_inside)
+
+ local top_pos = { x=new_pos.x, z=new_pos.z, y=y_max}
+ local bottom_pos = { x=new_pos.x, z=new_pos.z, y=y_min}
+
+ -- get list of all nodes within our y-range we could spawn within
+ local spawnable_nodes =
+ minetest.find_nodes_in_area(bottom_pos, top_pos, spawn_inside)
+
+ -- if there ain't a single node to spawn within get out of here
+ if #spawnable_nodes == 0 then
+ return nil, "no spawnable nodes at all"
+ end
+
+ local spawnable_node_passed = false
+
+ -- loop from topmost position to bottom
+ for i=y_max, y_min, -1 do
+ -- get current position
+ local pos = { x=new_pos.x,z=new_pos.z,y=i}
+
+ -- if the node at current position ain't one of those we can spawn within
+ if not adv_spawning.contains_pos(spawnable_nodes,pos) then
+
+ -- get more information about this node
+ local node = minetest.get_node(pos)
+
+ local text = "false"
+
+ if spawnable_node_passed then
+ text = "true"
+ end
+
+ -- if node ain't unloaded and we did already see a spawnable node above
+ -- return position above as pos to spawn
+ if node.name ~= "ignore" and
+ spawnable_node_passed then
+ return i+1, "pos found"
+ end
+ else
+ -- set marker about having seen a spawnable node above
+ spawnable_node_passed = true
+ end
+ end
+
+ return nil, "no matching node, nodecnt: " .. #spawnable_nodes
+end
+
+--------------------------------------------------------------------------------
+-- @function [parent=#adv_spawning] get_relative_pos
+-- @param y_min minumum relevant y pos
+-- @param y_max maximum relevant y pos
+-- @param new_pos position to spawn at
+-- @param spawn_inside nodes to spawn at
+-- @param relative_height
+-- @param absolute_height
+-- @return y-value of last spawnable node
+--------------------------------------------------------------------------------
+function adv_spawning.get_relative_pos(y_min,y_max,new_pos,spawn_inside,relative_height,absolute_height)
+ local y_val = adv_spawning.get_surface(y_min,y_max,new_pos,spawn_inside)
+
+ if y_val == nil then
+ if (relative_height.min ~= nil or
+ relative_height.max ~= nil) then
+ return nil, "y_pos not witing range of "
+ .. relative_height.min .. "<-->" .. relative_height.max
+ else
+ y_val = y_min
+ end
+ end
+
+ local top_pos = { x=new_pos.x, z=new_pos.z, y=y_max}
+ local bottom_pos = { x=new_pos.x, z=new_pos.z, y=y_val}
+
+ if relative_height ~= nil then
+ if relative_height.min ~= nil then
+ bottom_pos.y = y_val + relative_height.min
+ end
+
+ if relative_height.max ~= nil then
+ top_pos.y = y_val + relative_height.max
+ end
+ end
+
+ top_pos.y = adv_spawning.MIN(absolute_height.max,top_pos.y)
+ bottom_pos.y = adv_spawning.MAX(absolute_height.min,bottom_pos.y)
+
+ if top_pos.y < bottom_pos.y then
+ --print("Invalid interval: " .. bottom_pos.y .. "<-->" .. top_pos.y)
+ return nil, "invalid interval: " .. bottom_pos.y .. "<-->" .. top_pos.y
+ end
+
+ local spawnable_nodes =
+ minetest.find_nodes_in_area(bottom_pos, top_pos, spawn_inside)
+
+ if #spawnable_nodes > 0 then
+ return spawnable_nodes[math.random(1,#spawnable_nodes)].y, "rpos found"
+ else
+ --print("no suitable nodes" .. bottom_pos.y .. "<-->" .. top_pos.y)
+ return nil, "no spawnable nodes found around"
+ end
+end
+
+--------------------------------------------------------------------------------
+-- @function [parent=#adv_spawning] contains_pos
+-- @param pos_list table containing positions
+-- @param pos a position to search
+-- @param remove if this is set to true a position is removed on match
+-- @return true/false
+--------------------------------------------------------------------------------
+function adv_spawning.contains_pos(pos_list,pos,remove)
+
+ for i=1,#pos_list,1 do
+ if pos_list[i].x == pos.x and
+ pos_list[i].z == pos.z and
+ pos_list[i].y == pos.y then
+
+ if remove then
+ table.remove(pos_list,i)
+ end
+ return true
+ end
+ end
+ return false
+end
+
+--------------------------------------------------------------------------------
+-- @function [parent=#adv_spawning] check_absolute_height
+-- @param pos to verify
+-- @param absolute_height configuration for absolute height check
+-- @return true/false
+--------------------------------------------------------------------------------
+function adv_spawning.check_absolute_height(pos,absolute_height)
+ if absolute_height == nil then
+ return true, "no height limit"
+ end
+
+ if absolute_height.min ~= nil and
+ pos.y < absolute_height.min then
+ return false, pos.y .. " < " .. absolute_height.min
+ end
+
+ if absolute_height.max ~= nil and
+ pos.y > absolute_height.max then
+ return false, pos.y .. " > " .. absolute_height.max
+ end
+
+ return true, "height ok"
+end
+
+--------------------------------------------------------------------------------
+-- @function [parent=#adv_spawning] check_surface
+-- @param pos to verify
+-- @param surface configuration
+-- @param relative_height required to check for non ground bound spawning
+-- @param spawn_inside nodes to spawn inside
+-- @return true/false
+--------------------------------------------------------------------------------
+function adv_spawning.check_surface(pos,surfaces,relative_height,spawn_inside)
+
+ if surfaces == nil then
+ return true
+ end
+
+ if relative_height == nil or (
+ relative_height.max ~= nil and
+ relative_height.max <= 1) then
+
+ local lower_pos = {x=pos.x, y= pos.y-1, z=pos.z}
+
+ local node_below = minetest.get_node(lower_pos)
+
+ return adv_spawning.contains(surfaces,node_below.name)
+ else
+ local ymin = pos.y-relative_height.max-1
+ local ymax = pos.y+relative_height.max
+ local surface = adv_spawning.get_surface(ymin, ymax, pos, spawn_inside)
+ if surface == nil then
+ return false
+ else
+ local lower_pos = {x=pos.x, y= surface-1, z=pos.z}
+
+ local node_below = minetest.get_node(lower_pos)
+
+ return adv_spawning.contains(surfaces,node_below.name)
+ end
+ end
+end
+
+--------------------------------------------------------------------------------
+-- @function [parent=#adv_spawning] contains
+-- @param table_to_check
+-- @param value
+-- @return true/false
+--------------------------------------------------------------------------------
+function adv_spawning.contains(table_to_check,value)
+ for i=1,#table_to_check,1 do
+ if table_to_check[i] == value then
+ return true
+ end
+ end
+ return false
+end
+
+--------------------------------------------------------------------------------
+-- @function [parent=#adv_spawning] check_daytimes
+-- @param table_to_check
+-- @return true/false
+--------------------------------------------------------------------------------
+function adv_spawning.check_daytime(daytimedefs)
+ if daytimedefs == nil then
+ return true
+ end
+
+ local current_time = minetest.get_timeofday()
+ local match = false
+
+ for i=1,#daytimedefs,1 do
+ if daytimedefs[i].begin ~= nil and
+ daytimedefs[i].stop ~= nil then
+
+ if current_time < daytimedefs[i].stop and
+ current_time > daytimedefs[i].begin then
+ match = true
+ end
+ break
+ end
+
+ if daytimedefs[i].begin ~= nil and
+ current_time > daytimedefs[i].begin then
+ match = true
+ break
+ end
+
+ if daytimedefs[i].stop ~= nil and
+ current_time < daytimedefs[i].stop then
+ match = true
+ break
+ end
+ end
+
+ return match
+end
+
+--------------------------------------------------------------------------------
+-- @function [parent=#adv_spawning] check_nodes_around
+-- @param pos position to validate
+-- @param nodes_around node around definitions
+-- @return true/false
+--------------------------------------------------------------------------------
+function adv_spawning.check_nodes_around(pos,nodes_around)
+
+ if nodes_around == nil then
+ return true
+ end
+
+ for i=1,#nodes_around,1 do
+ --first handle special cases 0 and 1 in a quick way
+ if (nodes_around[i].threshold == 1 and nodes_around[i].type == "MIN") or
+ (nodes_around[i].threshold == 0 and nodes_around[i].type == "MAX")then
+
+ local found =
+ minetest.find_node_near(pos,nodes_around[i].distance,
+ nodes_around[i].name)
+
+ if nodes_around[i].type == "MIN" then
+ if found == nil then
+ --print("not enough: " .. dump(nodes_around[i].name) .. " around")
+ return false
+ end
+ else
+ if found ~= nil then
+ --print("to many: " .. dump(nodes_around[i].name) .. " around " .. dump(found))
+ return false
+ end
+ end
+ else
+ --need to do the full blown check
+ local found_nodes = minetest.find_nodes_in_area(
+ { x=pos.x-nodes_around[i].distance,
+ y=pos.y-nodes_around[i].distance,
+ z=pos.z-nodes_around[i].distance},
+ { x=pos.x+nodes_around[i].distance,
+ y=pos.y+nodes_around[i].distance,
+ z=pos.z+nodes_around[i].distance},
+ nodes_around[i].name)
+
+ if nodes_around[i].type == "MIN" and
+ #found_nodes < nodes_around[i].threshold then
+ --print("Found MIN: " .. dump(nodes_around[i].name) ..
+ -- "\n at locations: " .. dump(found_nodes))
+ --print ("Only " .. #found_nodes .. "/" .. nodes_around[i].threshold)
+ return false
+ end
+
+ if nodes_around[i].type == "MAX" and
+ #found_nodes > nodes_around[i].threshold then
+ --print("Found MAX: " .. dump(nodes_around[i].name) ..
+ -- "\n at locations: " .. dump(found_nodes))
+ return false
+ end
+ end
+ end
+
+ return true
+end
+
+--------------------------------------------------------------------------------
+-- @function [parent=#adv_spawning] check_entities_around
+-- @param pos position to validate
+-- @param entities_around entity around definitions
+-- @return true/false
+--------------------------------------------------------------------------------
+function adv_spawning.check_entities_around(pos,entities_around)
+ if entities_around == nil then
+ return true
+ end
+
+ for i=1,#entities_around,1 do
+ local entity_in_range =
+ minetest.get_objects_inside_radius(pos, entities_around[i].distance)
+
+ if entities_around[i].entityname == nil then
+ if entities_around[i].type == "MIN" and
+ #entity_in_range < entities_around[i].threshold then
+ return false
+ end
+
+ if entities_around[i].type == "MAX" and
+ #entity_in_range > entities_around[i].threshold then
+ return false
+ end
+ end
+
+ local count = 0
+
+ for j=1,#entity_in_range,1 do
+ local entity = entity_in_range[j]:get_luaentity()
+
+ if entity ~= nil then
+ if entity.name == entities_around[i].entityname then
+ count = count +1
+ end
+
+ if count > entities_around[i].threshold then
+ break
+ end
+ end
+ end
+
+ if entities_around[i].type == "MIN" and
+ count < entities_around[i].threshold then
+ return false
+ end
+
+ if entities_around[i].type == "MAX" and
+ count > entities_around[i].threshold then
+ return false
+ end
+ end
+
+ return true
+end
+
+--------------------------------------------------------------------------------
+-- @function [parent=#adv_spawning] check_light_around
+-- @param pos position to validate
+-- @param light_around light around definitions
+-- @return true/false
+--------------------------------------------------------------------------------
+function adv_spawning.check_light_around(pos,light_around)
+ if light_around == nil then
+ return true
+ end
+
+ for i=1,#light_around,1 do
+
+ for x=pos.x-light_around[i].distance,pos.x+light_around[i].distance,1 do
+ for y=pos.y-light_around[i].distance,pos.y+light_around[i].distance,1 do
+ for x=pos.z-light_around[i].distance,pos.z+light_around[i].distance,1 do
+ local checkpos = { x=x,y=y,z=z}
+ local time = minetest.get_timeofday()
+ if light_around[i].type == "TIMED_MIN" or
+ light_around[i].type == "TIMED_MAX" then
+ time = light_around[i].time
+ end
+
+ if light_around[i].type == "OVERALL_MIN" or
+ light_around[i].type == "OVERALL_MAX" then
+
+ for j=0,24000,1000 do
+ local light_level = minetest.get_node_light(checkpos, j)
+
+ if light_level ~= nil then
+ if light_around[i].type == "OVERALL_MAX" and
+ light_level > light_around[i].threshold then
+ return false
+ end
+
+ if light_around[i].type == "OVERALL_MIN" and
+ light_level < light_around[i].threshold then
+ return false
+ end
+ end
+ end
+
+ else
+ local light_level = minetest.get_node_light(checkpos, time)
+
+ if light_level ~= nil then
+ if (light_around[i].type == "TIMED_MIN" or
+ light_around[i].type == "CURRENT_MIN") and
+ light_level < light_around[i].threshold then
+ return false
+ end
+
+ if (light_around[i].type == "TIMED_MAX" or
+ light_around[i].type == "CURRENT_MAX") and
+ light_level > light_around[i].threshold then
+ return false
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+
+ return true
+end
+
+--------------------------------------------------------------------------------
+-- @function [parent=#adv_spawning] check_light_around_voxel
+-- @param pos position to validate
+-- @param light_around light around definitions
+-- @return true/false
+--------------------------------------------------------------------------------
+function adv_spawning.check_light_around_voxel(pos,light_around)
+ if light_around == nil then
+ return true
+ end
+
+ local maxdistance = 0
+
+ for i=1,#light_around,1 do
+ maxdistance = adv_spawning.MAX(maxdistance,light_around[i].distance)
+ end
+
+ -- voxelmanip is of no use for low counts of nodes
+ if maxdistance <=10 then
+ return adv_spawning.check_light_around(pos,light_around)
+ end
+
+ local minp = { x=math.floor(pos.x - maxdistance),
+ y=math.floor(pos.y - maxdistance),
+ z=math.floor(pos.z - maxdistance)}
+ local maxp = { x=math.ceil(pos.x + maxdistance),
+ y=math.ceil(pos.y + maxdistance),
+ z=math.ceil(pos.z + maxdistance)}
+
+ local voxeldata = minetest.get_voxel_manip()
+ local got_minp,got_maxp = voxeldata:read_from_map(minp,maxp)
+
+ local voxel_light_data = voxeldata:get_light_data()
+ local node_data = voxeldata:get_data()
+ local voxelhelper = VoxelArea:new({MinEdge=got_minp,MaxEdge=got_maxp})
+
+
+ for i=1,#light_around,1 do
+
+ for x=pos.x-light_around[i].distance,pos.x+light_around[i].distance,1 do
+ for y=pos.y-light_around[i].distance,pos.y+light_around[i].distance,1 do
+ for z=pos.z-light_around[i].distance,pos.z+light_around[i].distance,1 do
+ local checkpos = { x=x,y=y,z=z}
+ local time = minetest.get_timeofday()
+ if light_around[i].type == "TIMED_MIN" or
+ light_around[i].type == "TIMED_MAX" then
+ time = light_around[i].time
+ end
+
+ if light_around[i].type == "OVERALL_MIN" or
+ light_around[i].type == "OVERALL_MAX" then
+
+ for j=0,24000,1000 do
+ local light_level =
+ adv_spawning.voxelmaniplight(node_data,
+ voxel_light_data,
+ voxelhelper,
+ checkpos,j)
+
+ if light_level ~= nil then
+ if light_around[i].type == "OVERALL_MAX" and
+ light_level > light_around[i].threshold then
+ return false
+ end
+
+ if light_around[i].type == "OVERALL_MIN" and
+ light_level < light_around[i].threshold then
+ return false
+ end
+ end
+ end
+
+ else
+ local light_level =
+ adv_spawning.voxelmaniplight(node_data,
+ voxel_light_data,
+ voxelhelper,
+ checkpos,time)
+
+ if light_level ~= nil then
+ if (light_around[i].type == "TIMED_MIN" or
+ light_around[i].type == "CURRENT_MIN") and
+ light_level < light_around[i].threshold then
+ return false
+ end
+
+ if (light_around[i].type == "TIMED_MAX" or
+ light_around[i].type == "CURRENT_MAX") and
+ light_level > light_around[i].threshold then
+ return false
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+
+ return true
+end
+
+local light_lookup = {
+ {4250+125, 150},
+ {4500+125, 150},
+ {4750+125, 250},
+ {5000+125, 350},
+ {5250+125, 500},
+ {5500+125, 675},
+ {5750+125, 875},
+ {6000+125, 1000},
+ {6250+125, 1000}
+ }
+
+function adv_spawning.day_night_ratio(time)
+
+ --make sure time is between 0 and 240000
+ if time < 0 then
+ time = time - (((time*-1)/24000)*24000)
+ end
+ if time > 24000 then
+ time = time + ((time/24000)*24000)
+ end
+
+ --invert time for sunset
+ if time > 12000 then
+ time = 24000 - time
+ end
+
+ local dnr = 1000
+
+ for i=1,#light_lookup,1 do
+ if time < light_lookup[i][1] then
+ dnr = light_lookup[i][2]
+ break
+ end
+ end
+
+ return dnr
+end
+
+function adv_spawning.voxelmaniplight(node_data,light_data,area,pos,time)
+
+ if not area:containsp(pos) then
+ return minetest.get_node_light(pos, time)
+ end
+
+ pos = vector.round(pos)
+ local index = area:indexp(pos)
+
+ local raw_light_value = light_data[index]
+
+ if raw_light_value == nil then
+ return nil
+ end
+
+ local light_day = nil
+ local light_night = nil
+
+ --read node information
+ local content_id = node_data[index]
+ local nodename = minetest.get_name_from_content_id(content_id)
+ local nodedef = minetest.registered_nodes[nodename]
+
+ -- check for solid node
+ if nodedef.paramtype ~= "light" then
+ light_day = 0
+ light_night = 0
+ else
+ light_day = raw_light_value % 16
+ light_night = (raw_light_value - light_day)/16
+ end
+
+ --check lightsource
+ if nodedef.light_source ~= nil then
+ light_day = adv_spawning.MAX(nodedef.light_source,light_day)
+ light_night = adv_spawning.MAX(nodedef.light_source,light_night)
+ end
+
+ time = time *24000
+ time = time %24000
+
+ local dnr = adv_spawning.day_night_ratio(time)
+
+ local c = 1000
+ local current_light = ((dnr * light_day + (c-dnr) * light_night))/c
+ if(current_light > LIGHT_MAX+1) then
+ current_light = LIGHT_MAX+1
+ end
+
+ return math.floor(current_light)
+end
+
+--------------------------------------------------------------------------------
+-- @function [parent=#adv_spawning] check_temperature_around
+-- @param pos position to validate
+-- @param temperature_around temperature around definitions
+-- @return true/false
+--------------------------------------------------------------------------------
+function adv_spawning.check_temperature_around(pos,temperature_around)
+ if temperature_around == nil then
+ return true
+ end
+
+ --TODO
+ return true
+end
+
+--------------------------------------------------------------------------------
+-- @function [parent=#adv_spawning] check_humidity_around
+-- @param pos position to validate
+-- @param humidity_around humidity around definitions
+-- @return true/false
+--------------------------------------------------------------------------------
+function adv_spawning.check_humidity_around(pos,humidity_around)
+ if humidity_around == nil then
+ return true
+ end
+ --TODO
+ return true
+end
+--------------------------------------------------------------------------------
+-- @function [parent=#adv_spawning] check_flat_area
+-- @param pos position to validate
+-- @param range to check for same height
+-- @param deviation maximum nmber of nodes not matching flat check
+-- @param spawn_inside nodes to spawn inside
+-- @return true/false
+--------------------------------------------------------------------------------
+function adv_spawning.check_flat_area(new_pos,flat_area,spawn_inside,surfaces)
+
+ if flat_area == nil then
+ return true
+ end
+
+ local range = flat_area.range
+
+ local back_left = {x=new_pos.x-range,y=new_pos.y-1,z=new_pos.z-range}
+ local front_right = { x=new_pos.x+range,y=new_pos.y-1,z=new_pos.z+range}
+
+ local current_deviation = 0
+
+ if flat_area.deviation ~= nil then
+ current_deviation = flat_area.deviation
+ end
+
+ local required_nodes = (range*2+1)*(range*2+1) - current_deviation
+
+ if surfaces == nil then
+ local ground_nodes =
+ minetest.find_nodes_in_area(back_left, front_right, spawn_inside)
+
+ if #ground_nodes > current_deviation then
+ adv_spawning.log("info","check_flat_area: " .. range .. " "
+ ..dump(current_deviation).. " " .. #ground_nodes )
+ --adv_spawning.dump_area({x=back_left.x,y=new_pos.y-1,z=back_left.z},
+ -- front_right)
+ return false
+ end
+ else
+ local ground_nodes =
+ minetest.find_nodes_in_area(back_left, front_right, surfaces)
+
+ if #ground_nodes < required_nodes then
+ adv_spawning.log("info","check_flat_area: " .. range .. " " ..
+ dump(current_deviation).. " " .. #ground_nodes )
+ --adv_spawning.dump_area({x=back_left.x,y=new_pos.y-1,z=back_left.z},
+ -- front_right)
+ return false
+ end
+ end
+
+ back_left.y = new_pos.y
+ front_right.y = new_pos.y
+
+ local inside_nodes =
+ minetest.find_nodes_in_area(back_left, front_right, spawn_inside)
+
+ if #inside_nodes < required_nodes then
+ adv_spawning.log("info","check_flat_area: " .. range .. " " ..
+ dump(current_deviation) .. " "
+ .. #inside_nodes .. "/" .. required_nodes)
+ --adv_spawning.dump_area({x=back_left.x,y=new_pos.y-1,z=back_left.z},
+ -- front_right)
+ return false
+ end
+
+ return true
+end
+
+--------------------------------------------------------------------------------
+-- @function [parent=#adv_spawning] log
+-- @param level
+-- @param text
+--------------------------------------------------------------------------------
+function adv_spawning.log(level,text)
+ local is_debug = false
+
+ if not is_debug then
+ return
+ end
+ print("ADV_SPAWNING:" .. text)
+ minetest.log(level,text)
+end
+
+--------------------------------------------------------------------------------
+-- @function [parent=#adv_spawning] check_collisionbox
+-- @param pos position to check
+-- @param collisionbox collisionbox to use
+-- @param spawn_inside nodes to spawn inside
+--------------------------------------------------------------------------------
+function adv_spawning.check_collisionbox(pos,collisionbox,spawn_inside)
+ if collisionbox == nil then
+ return true,nil
+ end
+
+ --skip for collisionboxes smaller then a single node
+ if collisionbox[1] >= -0.5 and collisionbox[2] >= -0.5 and collisionbox[3] >= -0.5 and
+ collisionbox[4] <= 0.5 and collisionbox[5] <= 0.5 and collisionbox[6] <= 0.5 then
+ return true,nil
+ end
+
+ --lets do the more complex checks
+ --first check if we need to move up
+ if collisionbox[2] < -0.5 then
+ pos.y = pos.y + (collisionbox[2]*-1) - 0.45
+ end
+
+ local minp = {
+ x=pos.x+collisionbox[1],
+ y=pos.y+collisionbox[2],
+ z=pos.z+collisionbox[3]
+ }
+ local maxp = {
+ x=pos.x+collisionbox[4],
+ y=pos.y+collisionbox[5],
+ z=pos.z+collisionbox[6]
+ }
+
+ local lastpos = nil
+
+ for y=minp.y,maxp.y,1 do
+ for z=minp.z,maxp.z,1 do
+ for x=minp.x,maxp.x,1 do
+ local checkpos = {x=x,y=y,z=z}
+ if not adv_spawning.is_same_pos(checkpos,lastpos) then
+ local node = minetest.get_node(checkpos)
+
+ if not adv_spawning.contains(spawn_inside,node.name) then
+ adv_spawning.log("info","Failed collision box check: " ..
+ minetest.pos_to_string(pos) .. " "
+ .. dump(node.name) .. " at ".. minetest.pos_to_string(checkpos))
+ --adv_spawning.dump_area()
+ return false,nil
+ end
+
+ lastpos = checkpos
+ end
+ end
+ end
+ end
+
+ return true,pos.y
+end
+
+--------------------------------------------------------------------------------
+-- @function [parent=#adv_spawning] check_active_block
+-- @param pos position to check
+-- @param collisionbox collisionbox to use
+-- @param spawn_inside nodes to spawn inside
+--------------------------------------------------------------------------------
+function adv_spawning.check_active_block(pos)
+ local players = minetest.get_connected_players()
+
+ for i=1,#players,1 do
+ if vector.distance(pos,players[i]:getpos()) < adv_spawning.active_range then
+ return true
+ end
+ end
+
+ return false
+end
+
+--------------------------------------------------------------------------------
+-- @function [parent=#adv_spawning] is_same_pos
+-- @param pos1 first for comparison
+-- @param pos2 second position for comparison
+--------------------------------------------------------------------------------
+function adv_spawning.is_same_pos(pos1,pos2)
+ if pos1 == nil or pos2 == nil then
+ return false
+ end
+
+ if pos1.x ~= pos2.x or
+ pos1.y ~= pos2.y or
+ pos1.z ~= pos2.z then
+ return false
+ end
+
+ return true
+end
+
+--------------------------------------------------------------------------------
+-- @function [parent=#adv_spawning] handle_mapgen_spawning
+--------------------------------------------------------------------------------
+function adv_spawning.handle_mapgen_spawning()
+ local continue = false
+ while(not continue and #adv_spawning.mapgen_jobqueue > 0) do
+ local toprocess = adv_spawning.mapgen_jobqueue[1]
+ table.remove(adv_spawning.mapgen_jobqueue,1)
+
+ --print("Processing job: " .. dump(toprocess) .. " no_quota: " ..
+ -- dump(adv_spawning.time_over(10)))
+
+ local tries = 0
+
+ while ( toprocess.retries > 0 and
+ toprocess.spawntotal > 0 and
+ tries < adv_spawning.max_mapgen_tries_per_step and
+ (not adv_spawning.time_over(10)) )do
+
+ local single_spawn_check = adv_spawning.gettime()
+
+ local retval,permanent_error = adv_spawning.handlespawner(toprocess.spawner,
+ {x=0,y=0,z=0},
+ toprocess.minp,
+ toprocess.maxp,
+ true)
+
+ local delta = adv_spawning.gettime() - adv_spawning.quota_starttime
+
+ if retval then
+ toprocess.spawntotal = toprocess.spawntotal -1
+ end
+
+ if permanent_error then
+ toprocess.retries = 0
+ end
+
+ toprocess.retries = toprocess.retries -1
+ tries = tries +1
+ end
+
+ if toprocess.retries > 0 then
+ if toprocess.spawntotal > 0 then
+ table.insert(adv_spawning.mapgen_jobqueue,toprocess)
+ end
+ continue = true
+ end
+ end
+end
+
+--------------------------------------------------------------------------------
+-- @function [parent=#adv_spawning] queue_mapgen_jobs
+-- @param minp
+-- @param maxp
+--------------------------------------------------------------------------------
+function adv_spawning.queue_mapgen_jobs(minp,maxp)
+ for key,value in pairs(adv_spawning.spawner_definitions) do
+ local continue = false
+
+ --check if cyclic spawning is enabled
+ if not continue and
+ (value.mapgen == nil or
+ value.mapgen.enabled == false) then
+ continue = true
+ end
+
+
+ if not continue then
+ table.insert(adv_spawning.mapgen_jobqueue,
+ {
+ minp = minp,
+ maxp = maxp,
+ spawner = key,
+ retries = value.mapgen.retries,
+ spawntotal = value.mapgen.spawntotal
+ })
+ end
+ end
+end
+
+--------------------------------------------------------------------------------
+-- @function [parent=#adv_spawning] dump_area
+-- @param minp
+-- @param maxp
+--------------------------------------------------------------------------------
+function adv_spawning.dump_area(minp,maxp)
+ print("Dumping: " .. dump(minp) .. "<-->" .. dump(maxp))
+ for y=minp.y,maxp.y,1 do
+ print("--- ypos: " .. y .. " --------------------------------------------------------------")
+ for z=minp.z,maxp.z,1 do
+ local line = ""
+ for x=minp.x,maxp.x,1 do
+ local node = minetest.get_node({x=x,y=y,z=z})
+
+ local toprint = node.name
+
+ if toprint:find(":") ~= nil then
+ toprint = toprint:sub(toprint:find(":")+1)
+ end
+
+ line = line .. string.format(" %15s |",toprint)
+ end
+ print(line)
+ end
+ print("")
+ end
+end
+
+--------------------------------------------------------------------------------
+-- @function [parent=#adv_spawning] check_time
+-- @param starttime time since when to check
+-- @param checkid name of this check
+--
+-- @return current time for next check
+--------------------------------------------------------------------------------
+function adv_spawning.check_time(starttime, checkid)
+ local currenttime = adv_spawning.gettime()
+ local delta = currenttime - starttime
+
+ if (delta > adv_spawning.quota_reload) then
+ if adv_spawning.spawner_warned[checkid] ~= true then
+ adv_spawning.dbg_log(1, "spawner " .. checkid ..
+ "\n\texceeded more then full reload time on init (" .. delta .. " ms)." ..
+ "\n\tFix it as it will cause major lag on mapgen!")
+ adv_spawning.spawner_warned[checkid] = true
+ end
+ end
+
+ return currenttime
+end
+
+--------------------------------------------------------------------------------
+-- @function [parent=#adv_spawning] dbg_log
+-- @param loglevel level print it
+-- @param message message to print
+--------------------------------------------------------------------------------
+function adv_spawning.dbg_log(loglevel, message)
+ if (adv_spawning.loglevel >= loglevel ) then
+ core.log("action", "ADV_SPAWNING: " .. message)
+ end
+end
+
+
+--------------------------------------------------------------------------------
+-- @function [parent=#adv_spawning] refresh_spawners
+-- @param pos to refresh spawners around
+--------------------------------------------------------------------------------
+function adv_spawning.refresh_spawners(pos)
+
+ local min = {x=pos.x-32, y=pos.y-32, z=pos.z-32}
+ local max = {x=pos.x+32, y=pos.y+32, z=pos.z+32}
+
+ local start_x =
+ math.floor(min.x/adv_spawning.spawner_distance)
+ * adv_spawning.spawner_distance
+ local start_y =
+ (math.floor(min.y/adv_spawning.spawner_distance)
+ * adv_spawning.spawner_distance)
+ + adv_spawning.spawner_y_offset
+ local start_z =
+ math.floor(min.z/adv_spawning.spawner_distance)
+ * adv_spawning.spawner_distance
+
+ for x=start_x,max.x,adv_spawning.spawner_distance do
+ for y=start_y,max.y,adv_spawning.spawner_distance do
+ for z=start_z,max.z,adv_spawning.spawner_distance do
+ if x > min.x and
+ y > min.y and
+ z > min.z then
+ if not adv_spawning.quota_leave() then
+ adv_spawning.dbg_log(2,
+ "adv_spawning: refresh_spawners did use way too much time 1")
+ end
+ minetest.add_entity({x=x,y=y,z=z},"adv_spawning:spawn_seed")
+ adv_spawning.quota_enter(true)
+ adv_spawning.log("info", "adv_spawning: adding spawner entity at "
+ .. core.pos_to_string({x=x,y=y,z=z}))
+ adv_spawning.statistics.session.spawners_created =
+ adv_spawning.statistics.session.spawners_created +1
+ end
+ end
+ end
+ end
+end
+
+--------------------------------------------------------------------------------
+-- @function [parent=#adv_spawning] table_count
+-- @param tocount table to get number of elements from
+--------------------------------------------------------------------------------
+function adv_spawning.table_count(tocount)
+ local retval = 0
+ for k,v in pairs(tocount) do
+ retval = retval +1
+ end
+
+ return retval
+end
+
+function adv_spawning.build_shell(pos, d)
+ local retval = {}
+
+ -- build top face
+ for x = -d , d , 1 do
+ for z = -d, d, 1 do
+ retval[#retval+1] = { x = pos.x + x, y = pos.y + d, z = pos.z + z}
+ end
+ end
+
+ -- build bottom face
+ for x = -d , d , 1 do
+ for z = -d, d, 1 do
+ retval[#retval+1] = { x = pos.x + x, y = pos.y -d, z = pos.z + z}
+ end
+ end
+
+ -- build x- face
+ for z = -d , d , 1 do
+ for y = - (d -1) , (d -1), 1 do
+ retval[#retval+1] = { x = pos.x -d, y = pos.y + y, z = pos.z + z}
+ end
+ end
+
+ -- build x+ face
+ for z = -d , d , 1 do
+ for y = - (d -1) , (d -1), 1 do
+ retval[#retval+1] = { x = pos.x + d, y = pos.y + y, z = pos.z + z}
+ end
+ end
+
+ -- build z- face
+ for x = - (d -1) , (d -1) , 1 do
+ for y = - (d -1) , (d -1), 1 do
+ retval[#retval+1] = { x = pos.x + x, y = pos.y + y, z = pos.z - d}
+ end
+ end
+
+ -- build z+ face
+ for x = -(d -1) , (d -1) , 1 do
+ for y = - (d -1) , (d -1), 1 do
+ retval[#retval+1] = { x = pos.x + x, y = pos.y + y, z = pos.z + d}
+ end
+ end
+
+ return retval;
+end
+
+--------------------------------------------------------------------------------
+-- @function [parent=#adv_spawning] table_count
+-- @param tocount table to get number of elements from
+--------------------------------------------------------------------------------
+function adv_spawning.find_nodes_in(pos, min_range, max_range, nodetypes)
+
+ local nodetypes_to_use = nodetypes
+
+ if type(nodetypes) == "string" then
+ nodetypes_to_use = { }
+ table.insert(nodetypes_to_use, nodetypes)
+ end
+
+ for i = min_range, max_range, 1 do
+ local positions = adv_spawning.build_shell(pos, i)
+
+ for i = 1, #positions, 1 do
+ local node = minetest.get_node_or_nil(positions[i])
+
+ if node ~= nil then
+ for i = 1, #nodetypes_to_use, 1 do
+ if node.name == nodetypes_to_use[i] then
+ return positions[i]
+ end
+ end
+ end
+ end
+ end
+
+ return nil
+end
diff --git a/spawn_seed.lua b/spawn_seed.lua
new file mode 100644
index 0000000..fd4d6ce
--- /dev/null
+++ b/spawn_seed.lua
@@ -0,0 +1,403 @@
+-------------------------------------------------------------------------------
+-- advanced spawning mod
+--
+-- at license WTFP
+-- at copyright Sapier
+-- at author Sapier
+-- at 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
\ No newline at end of file
diff --git a/spawndef_checks.lua b/spawndef_checks.lua
new file mode 100644
index 0000000..9070f71
--- /dev/null
+++ b/spawndef_checks.lua
@@ -0,0 +1,73 @@
+-------------------------------------------------------------------------------
+-- advanced spawning mod
+--
+-- at license WTFP
+-- at copyright Sapier
+-- at author Sapier
+-- at date 2013-12-05
+--
+-------------------------------------------------------------------------------
+
+--------------------------------------------------------------------------------
+-- @function [parent=#adv_spawning] verify_check_entities_around
+-- @param entities_around a spawndef entities_around config
+-- @return true/false
+--------------------------------------------------------------------------------
+function adv_spawning.verify_check_entities_around(entities_around)
+ if entities_around ~= nil then
+
+ for i=1,#entities_around,1 do
+
+ if type(entities_around[i].distance) ~= "number" then
+ adv_spawning.dbg_log(0, "missing distance in entities_around definition")
+ return false
+ end
+
+ if entities_around[i].type ~= "MIN" and
+ entities_around[i].type ~= "MAX" then
+ adv_spawning.dbg_log(0, "invalid type \"" ..
+ dump(entities_around[i].type) ..
+ "\" in entities_around definition")
+ return false
+ end
+ end
+ end
+
+ return true
+end
+
+--------------------------------------------------------------------------------
+-- @function [parent=#adv_spawning] verify_check_nodes_around
+-- @param nodes_around a spawndef entities_around config
+-- @return true/false
+--------------------------------------------------------------------------------
+function adv_spawning.verify_check_nodes_around(nodes_around)
+ if nodes_around ~= nil then
+ for i=1,#nodes_around,1 do
+
+ if type(nodes_around[i].distance) ~= "number" then
+ adv_spawning.dbg_log(0, "missing distance in entities_around definition")
+ return false
+ end
+
+ if nodes_around[i].type ~= "MIN" and
+ nodes_around[i].type ~= "MAX" then
+ adv_spawning.dbg_log(0, "invalid type \"" ..
+ dump(nodes_around[i].type) ..
+ "\" in entities_around definition")
+ return false
+ end
+
+ if nodes_around[i].name == nil or
+ type(nodes_around[i].name) ~= "table" then
+ adv_spawning.dbg_log(0, "invalid type of name \"" ..
+ type(nodes_around[i].name) .. "\"" .. " Data: " ..
+ dump(nodes_around[i].name) ..
+ " in nodes_around definition")
+ return false
+ end
+ end
+ end
+
+ return true
+end
\ No newline at end of file
diff --git a/testmod/depends.txt b/testmod/depends.txt
new file mode 100644
index 0000000..5902e98
--- /dev/null
+++ b/testmod/depends.txt
@@ -0,0 +1 @@
+adv_spawning
\ No newline at end of file
diff --git a/testmod/init.lua b/testmod/init.lua
new file mode 100644
index 0000000..bd4aea5
--- /dev/null
+++ b/testmod/init.lua
@@ -0,0 +1,154 @@
+
+
+minetest.register_entity("testmod:bogus_1",
+ {
+ collisionbox = { -0.5,-0.5,-0.5,0.5,0.5,0.5 },
+ visual = "sprite",
+ textures = { "testmod_num1.png" },
+ physical = false,
+ groups = { "immortal" },
+ }
+ )
+
+minetest.register_entity("testmod:bogus_2",
+ {
+ collisionbox = { -0.5,-0.5,-0.5,0.5,0.5,0.5 },
+ visual = "sprite",
+ textures = { "testmod_num2.png" },
+ physical = false,
+ groups = { "immortal" },
+ }
+ )
+
+minetest.register_entity("testmod:bogus_3",
+ {
+ collisionbox = { -0.5,-0.5,-0.5,0.5,0.5,0.5 },
+ visual = "sprite",
+ textures = { "testmod_num3.png" },
+ physical = false,
+ groups = { "immortal" },
+ }
+ )
+
+minetest.register_entity("testmod:bogus_4",
+ {
+ collisionbox = { -0.5,-0.5,-0.5,0.5,0.5,0.5 },
+ visual = "sprite",
+ textures = { "testmod_num4.png" },
+ physical = false,
+ groups = { "immortal" },
+ }
+ )
+
+--adv_spawning.register("some_bogus_entity_1",
+-- {
+-- spawnee = "testmod:bogus_1",
+-- spawn_interval = 10,
+--
+-- spawn_inside =
+-- {
+-- "air"
+-- },
+--
+-- entities_around =
+-- {
+-- { type="MAX",entityname = "testmod:bogus_1",distance=20,threshold=1 }
+-- },
+--
+-- relative_height =
+-- {
+-- max = 1
+-- }
+-- })
+
+--adv_spawning.register("some_bogus_entity_2",
+-- {
+-- spawnee = "testmod:bogus_2",
+-- spawn_interval = 5,
+-- spawn_inside =
+-- {
+-- "air"
+-- },
+--
+-- entities_around =
+-- {
+-- { type="MAX",distance=20,threshold=1 }
+-- },
+--
+-- relative_height =
+-- {
+-- max = 1
+-- },
+--
+-- surfaces =
+-- {
+-- "default:dirt_with_grass"
+-- }
+-- })
+
+--adv_spawning.register("some_bogus_entity_3",
+-- {
+-- spawnee = "testmod:bogus_3",
+-- spawn_interval = 3,
+-- spawn_inside =
+-- {
+-- "air"
+-- },
+--
+-- entities_around =
+-- {
+-- { type="MAX",entityname = "testmod:bogus_4",distance=20,threshold=1 }
+-- },
+--
+-- relative_height =
+-- {
+-- max = 4,
+-- min = 4,
+-- },
+-- })
+
+adv_spawning.register("some_bogus_entity_4",
+ {
+ spawnee = "testmod:bogus_4",
+ spawn_interval = 3,
+ spawn_inside =
+ {
+ "air"
+ },
+
+ entities_around =
+ {
+ { type="MAX",distance=30,threshold=1 }
+ },
+
+ relative_height =
+ {
+ max = 4,
+ min = 4,
+ },
+ surfaces =
+ {
+ "default:leaves"
+ }
+ })
+
+
+minetest.register_chatcommand("adv_stats",
+ {
+ params = "",
+ description = "print advanced spawning satistics to logfile" ,
+ func = function()
+ local stats = adv_spawning.get_statistics()
+
+ adv_spawning.dbg_log(0, "Adv. Spawning stats:")
+ adv_spawning.dbg_log(0, "----------------------------------------")
+ adv_spawning.dbg_log(0, "Spawners added: " .. stats.session.spawners_created)
+ adv_spawning.dbg_log(0, "Spawnees added: " .. stats.session.entities_created)
+ adv_spawning.dbg_log(0, "")
+ adv_spawning.dbg_log(0, "Longest step: " .. stats.step.max)
+ adv_spawning.dbg_log(0, "")
+ adv_spawning.dbg_log(0, "Current load: " .. stats.load.cur)
+ adv_spawning.dbg_log(0, "Average load: " .. stats.load.avg)
+ adv_spawning.dbg_log(0, "Maximum load: " .. stats.load.max)
+ end
+ })
\ No newline at end of file
diff --git a/testmod/textures/testmod_num1.png b/testmod/textures/testmod_num1.png
new file mode 100644
index 0000000..2913413
Binary files /dev/null and b/testmod/textures/testmod_num1.png differ
diff --git a/testmod/textures/testmod_num2.png b/testmod/textures/testmod_num2.png
new file mode 100644
index 0000000..29f27f0
Binary files /dev/null and b/testmod/textures/testmod_num2.png differ
diff --git a/testmod/textures/testmod_num3.png b/testmod/textures/testmod_num3.png
new file mode 100644
index 0000000..e3744aa
Binary files /dev/null and b/testmod/textures/testmod_num3.png differ
diff --git a/testmod/textures/testmod_num4.png b/testmod/textures/testmod_num4.png
new file mode 100644
index 0000000..862ed9c
Binary files /dev/null and b/testmod/textures/testmod_num4.png differ
diff --git a/textures/adv_spawning_invisible.png b/textures/adv_spawning_invisible.png
new file mode 100644
index 0000000..8cb82be
Binary files /dev/null and b/textures/adv_spawning_invisible.png differ
diff --git a/textures/adv_spawning_spawner.png b/textures/adv_spawning_spawner.png
new file mode 100644
index 0000000..5ba21c1
Binary files /dev/null and b/textures/adv_spawning_spawner.png differ
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-games/minetest-mod-advspawning.git
More information about the Pkg-games-commits
mailing list