------------------------------------------------------------------------------- -- Mob Framework Mod by Sapier -- -- You may copy, use, modify or do nearly anything except removing this -- copyright notice. -- And of course you are NOT allowed to pretend you have written it. -- --! @file generic_functions.lua --! @brief generic functions used in many different places --! @copyright Sapier --! @author Sapier --! @date 2012-08-09 --! -- Contact sapier a t gmx net ------------------------------------------------------------------------------- --! @defgroup gen_func Generic functions --! @brief functions for various tasks --! @ingroup framework_int --! @{ ------------------------------------------------------------------------------- -- name: mobf_get_time_ms() -- --! @brief get current time in ms -- --! @return current time in ms ------------------------------------------------------------------------------- function mobf_get_time_ms() --this fct is overwritten on init with best timesource available atm return os.clock() * 1000 end ------------------------------------------------------------------------------- -- name: mobf_contains(cur_table,element) -- --! @brief check if element is in table -- --! @param cur_table table to look in --! @param element element to look for --! @return true/false ------------------------------------------------------------------------------- function mobf_contains(cur_table,element) if cur_table == nil then return false end for i,v in ipairs(cur_table) do if v == element then return true end end return false end ------------------------------------------------------------------------------- -- name: MIN(a,b) -- --! @brief minimum of two numbers -- --! @param a number 1 --! @param b number 2 --! @return minimum ------------------------------------------------------------------------------- function MIN(a,b) mobf_assert_backtrace(type(a) == "number") mobf_assert_backtrace(type(b) == "number") if a > b then return b else return a end end ------------------------------------------------------------------------------- -- name: MAX(a,b) -- --! @brief maximum of two numbers -- --! @param a number 1 --! @param b number 2 --! @return maximum ------------------------------------------------------------------------------- function MAX(a,b) mobf_assert_backtrace(type(a) == "number") mobf_assert_backtrace(type(b) == "number") if a > b then return a else return b end end ------------------------------------------------------------------------------- -- name: DELTA(a,b) -- --! @brief delta of two numbers -- --! @param a number 1 --! @param b number 2 --! @return delta ------------------------------------------------------------------------------- function DELTA(a,b) return math.abs(a-b) end ------------------------------------------------------------------------------- -- name: mobf_is_walkable(node) -- --! @brief check if walkable flag is set for a node -- --! @param node to check --! @return true/false ------------------------------------------------------------------------------- function mobf_is_walkable(node) return (node and node.name and minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].walkable == false) end ------------------------------------------------------------------------------- -- name: mobf_get_current_time() -- --! @brief alias to get current time -- --! @return current time in seconds ------------------------------------------------------------------------------- function mobf_get_current_time() if type(minetest.get_time) == "function" then return minetest.get_time() else return os.time(os.date('*t')) end end ------------------------------------------------------------------------------- -- name: mobf_round_pos(pos) -- --! @brief calculate integer position -- --! @param pos position to be rounded --! @return rounded position ------------------------------------------------------------------------------- function mobf_round_pos(pos) if pos == nil then return pos end return { x=math.floor(pos.x + 0.5), y=math.floor(pos.y + 0.5), z=math.floor(pos.z + 0.5) } end ------------------------------------------------------------------------------- -- name: mobf_find_entity(newobject) DEPRECATED -- --! @brief find entity by object reference -- --! @param newobject r object reference --! @return entity object reference points at or nil on error ------------------------------------------------------------------------------- function mobf_find_entity(newobject) return newobject:get_luaentity() end ------------------------------------------------------------------------------- -- name: mobf_max_light_around(pos,range,daytime) -- --! @brief get maximum light level around specified position -- --! @param pos center of area to search --! @param distance radius of area --! @param daytime time of day to check --! @return highest detected light level ------------------------------------------------------------------------------- function mobf_max_light_around(pos,distance,daytime) mobf_assert_validpos(pos) local max_light = 0 for y_run=pos.y-distance,pos.y+distance,1 do for z_run=pos.z-distance,pos.z+distance,1 do for x_run=pos.x-distance,pos.x+distance,1 do local current_pos = {x=x_run,y=y_run,z=z_run } local node = minetest.get_node(current_pos) if node.name == "air" then local current_light = minetest.get_node_light(current_pos,daytime) if current_light > max_light then max_light = current_light end end end end end return max_light end ------------------------------------------------------------------------------- -- name: mobf_min_light_around(pos,range,daytime) -- --! @brief get minimum light level around specified position -- --! @param pos center of area to search --! @param distance radius of area --! @param daytime time of day to check --! @return highest detected light level ------------------------------------------------------------------------------- function mobf_min_light_around(pos,distance,daytime) mobf_assert_validpos(pos) local min_light = LIGHT_MAX+1 for y_run=pos.y-distance,pos.y+distance,1 do for z_run=pos.z-distance,pos.z+distance,1 do for x_run=pos.x-distance,pos.x+distance,1 do local current_pos = {x=x_run,y=y_run,z=z_run } local node = minetest.get_node(current_pos) if node.name == "air" then local current_light = minetest.get_node_light(current_pos,daytime) if current_light < min_light then min_light = current_light end end end end end return min_light end ------------------------------------------------------------------------------- -- name: mobf_objects_around(pos,range,ignorelist) -- --! @brief get number of objects within a certain range -- --! @param pos position to look around --! @param range range to check --! @param ignorelist list of entitynames to ignore --! @return count of objects ------------------------------------------------------------------------------- function mobf_objects_around(pos,range,ignorelist) local objectlist = minetest.get_objects_inside_radius(pos,range) local cleaned_objectcount = 0 for i=1,#objectlist,1 do local luaentity = objectlist[i]:get_luaentity() if luaentity ~= nil then if not luaentity.mobf_spawner and not mobf_contains(ignorelist,luaentity.name) then cleaned_objectcount = cleaned_objectcount + 1 end else cleaned_objectcount = cleaned_objectcount + 1 end end return cleaned_objectcount end ------------------------------------------------------------------------------- -- name: mobf_mob_around(mob_name,mob_transform_name,pos,range,) -- --! @brief get number of mobs of specified type within range of pos -- --! @param mob_name basic name of mob --! @param mob_transform secondary name of mob --! @param pos position to check --! @param range range to check --! @param ignore_playerspawned ignore mob spawned by players for check --! @return number of mob found ------------------------------------------------------------------------------- function mobf_mob_around(mob_name,mob_transform,pos,range,ignore_playerspawned) local count = 0 local objectcount = 0 mobf_assert_backtrace(range ~= nil) mobf_assert_backtrace(pos ~= nil) local objectlist = minetest.get_objects_inside_radius(pos,range) if mob_transform == nil then mob_transform = "" end local objcount = 0 if objectlist ~= nil then objcount = #objectlist end dbg_mobf.generic_lvl1("MOBF: entity at "..printpos(pos).. " looking for: "..mob_name .. " or " .. mob_transform .. " within " .. objcount .. " objects" ) for index,value in pairs(objectlist) do local entity = mobf_find_entity(value) --any mob is required to have a name so we may use this to decide --if an entity is an mob or not if entity ~= nil and entity.data ~= nil and entity.dynamic_data ~= nil then if entity.removed == false then if entity.data.modname..":"..entity.data.name == mob_name or entity.data.modname..":"..entity.data.name == mob_transform then -- oops we don't yet know if this is playerspawned, -- for performance reasons assume it isn't if entity.dynamic_data.spawning == nil then count = count + 1 else if (ignore_playerspawned and entity.dynamic_data.spawning.player_spawned) or ignore_playerspawned ~= false then dbg_mobf.generic_lvl1("MOBF: Found "..mob_name.. " or " ..mob_transform .. " within specified range of "..range) count = count + 1 end end end end end objectcount = objectcount +1 end dbg_mobf.generic_lvl2("MOBF: found " .. objectcount .. " within range " .. count .. " of them are relevant mobs ") return count end ------------------------------------------------------------------------------- -- name: mobf_spawner_around(mob_name,pos,range) -- --! @brief get number of mobs of specified type within range of pos -- --! @param mob_name basic name of mob --! @param pos position to check --! @param range range to check --! @return number of mob found ------------------------------------------------------------------------------- function mobf_spawner_around(mob_name,pos,range) mobf_assert_validpos(pos) dbg_mobf.generic_lvl2("MOBF: mobf_spawner_around param: ".. dump(mob_name) .. " "..dump(pos).. " " .. dump(range)) local count = 0 local objectcount = 0 local objectlist = minetest.get_objects_inside_radius(pos,range) for index,value in pairs(objectlist) do local entity = value:get_luaentity() dbg_mobf.generic_lvl3("MOBF: entity at: "..dump(value:getpos()).. " looking for: "..mob_name .. " " .. dump(value) .. " " .. dump(entity)) --any mob is required to have a name so we may use this to decide --if an entity is an mob or not if entity ~= nil and entity.spawner_mob_name ~= nil then if entity.spawner_mob_name == mob_name then dbg_mobf.generic_lvl2("MOBF: Found "..mob_name .. " within specified range of "..range) count = count + 1 end end objectcount = objectcount +1 end dbg_mobf.generic_lvl2("MOBF: found " .. objectcount .. " within range " .. count .. " of them are relevant spawners ") return count end ------------------------------------------------------------------------------- -- name: mobf_line_of_sight(pos1,pos2) -- --! @brief is there a line of sight between two specified positions -- --! @param pos1 start position of los check --! @param pos2 end position of los check --! @return: true/false ------------------------------------------------------------------------------- function mobf_line_of_sight(pos1,pos2) --print("Checking line of sight between "..printpos(pos1).." and "..printpos(pos2)) local distance = mobf_calc_distance(pos1,pos2) local normalized_vector = { x=(pos2.x-pos1.x)/distance, y=(pos2.y-pos1.y)/distance, z=(pos2.z-pos1.z)/distance} local line_of_sight = true for i=1,distance, 1 do local tocheck = { x=pos1.x + (normalized_vector.x * i), y=pos1.y + (normalized_vector.y *i), z=pos1.z + (normalized_vector.z *i)} local node = minetest.env:get_node(tocheck) if minetest.registered_nodes[node.name] == nil or minetest.registered_nodes[node.name].sunlight_propagates ~= true then line_of_sight = false break end end return line_of_sight end function mobf_line_of_sightX(pos1,pos2) return minetest.line_of_sight(pos1,pos2) end ------------------------------------------------------------------------------- -- name: mobf_pos_is_zero(pos) -- --! @brief check if position is (0,0,0) -- --! @param pos position to check --! @return true/false ------------------------------------------------------------------------------- function mobf_pos_is_zero(pos) if pos.x ~= 0 then return false end if pos.y ~= 0 then return false end if pos.z ~= 0 then return false end return true end ------------------------------------------------------------------------------- -- name: mobf_air_above(pos,height) -- --! @brief check if theres at least height air abov pos -- --! @param pos position to check --! @param height min number of air to check --! @return true/false ------------------------------------------------------------------------------- function mobf_air_above(pos,height) mobf_assert_validpos(pos) for i=0, height, 1 do local pos_above = { x = pos.x, y = pos.y + 1, z = pos.z } local node_above = minetest.get_node(pos_above) if node_above.name ~= "air" then return false end end return true end ------------------------------------------------------------------------------- -- name: mobf_ground_distance(pos,media) -- --! @brief get number of blocks above solid ground -- --! @param pos_raw position to check --! @param media table of blocks not considered to be ground --! @param max_check_height abort looking for ground after this number of nodes -- --! @return number of blocks to ground ------------------------------------------------------------------------------- function mobf_ground_distance(pos_raw,media,max_check_height) local pos = { x=pos_raw.x, y=math.floor(pos_raw.y + 0.5), z=pos_raw.z } local node_to_check = minetest.get_node(pos) local count = 0 if max_check_height == nil then max_check_height = 32 end while node_to_check ~= nil and mobf_contains(media,node_to_check.name) and count < max_check_height do count = count +1 pos = {x=pos.x,y=pos.y-1,z=pos.z}; node_to_check = minetest.get_node(pos) end return count end ------------------------------------------------------------------------------- -- name: mobf_surface_distance(pos) -- --! @brief get number of blocks above surface (solid or fluid!) -- --! @param pos position to check --! @return number of blocks to ground ------------------------------------------------------------------------------- function mobf_surface_distance(pos) local node_to_check = minetest.get_node(pos) local count = 0 while node_to_check ~= nil and node_to_check.name == "air" and count < 32 do count = count +1 pos = {x=pos.x,y=pos.y-1,z=pos.z}; node_to_check = minetest.get_node(pos) end return count end ------------------------------------------------------------------------------- -- name: mobf_air_distance(pos) -- --! @brief get number of blocks below waterline -- --! @param pos position to check --! @return number of blocks to air ------------------------------------------------------------------------------- function mobf_air_distance(pos) mobf_assert_validpos(pos) local node_to_check = minetest.get_node(pos) local count = 0 while node_to_check ~= nil and ( node_to_check.name == "default:water_source" or node_to_check.name == "default:water_flowing") do count = count +1 pos = {x=pos.x,y=pos.y+1,z=pos.z}; node_to_check = minetest.get_node(pos) end if node_to_check.name == "air" then return count else return -1 end end ------------------------------------------------------------------------------- -- name: mobf_above_water(pos) -- --! @brief check if next non-air block below mob is a water block -- --! @param pos position to check --! @return true/false ------------------------------------------------------------------------------- function mobf_above_water(pos) local node_to_check = minetest.get_node(pos) while node_to_check ~= nil and node_to_check.name == "air" do pos = {x=pos.x,y=pos.y-1,z=pos.z}; node_to_check = minetest.get_node(pos) end if node_to_check.name == "default:water_source" or node_to_check.name == "default:water_flowing" then return true end return false end ------------------------------------------------------------------------------- -- name: get_sunlight_surface(x,z, min_y, max_y) -- --! @brief get surface for x/z coordinates -- --! @param x x-coordinate --! @param z z-coordinate --! @param min_y minimum y-coordinate to consider --! @param max_y maximum y-coordinate to consider --! @return y value of surface or nil ------------------------------------------------------------------------------- function mobf_get_sunlight_surface(x,z, min_y, max_y) for runy = min_y, max_y,1 do local pos = { x=x,y=runy, z=z } local node_to_check = minetest.get_node(pos) if node_to_check.name == "default:dirt_with_grass" then return pos.y end end return nil end ------------------------------------------------------------------------------- -- name: get_surface(x,z, min_y, max_y) -- --! @brief get surface for x/z coordinates -- --! @param x x-coordinate --! @param z z-coordinate --! @param min_y minimum y-coordinate to consider --! @param max_y maximum y-coordinate to consider --! @return y value of surface (first air node) or nil ------------------------------------------------------------------------------- function mobf_get_surface(x,z, min_y, max_y) mobf_assert_backtrace(min_y ~= nil) mobf_assert_backtrace(max_y ~= nil) mobf_assert_backtrace(x ~= nil) mobf_assert_backtrace(z ~= nil) if type(minetest.get_surface) == "function" then local basepos = {x=x,y=min_y,z=z} local offset = max_y-min_y local retval = minetest.get_surface(basepos,offset) return retval end local last_node = minetest.get_node({ x=x,y=min_y, z=z }) for runy = min_y+1, max_y,1 do local pos = { x=x,y=runy, z=z } local node_to_check = minetest.get_node(pos) if node_to_check.name == "air" and last_node.name ~= "air" and last_node.mame ~= "ignore" then return pos.y end last_node = node_to_check end return nil end ------------------------------------------------------------------------------- -- name: entity_at_loaded_pos(entity.mobname) -- --! @brief check if entity is activated at already loaded pos -- --! @param pos to check --! @param mobname name of mob --! @return true/false ------------------------------------------------------------------------------- function entity_at_loaded_pos(pos,mobname) local current_node = minetest.get_node(pos) if mobname == nil then mobname = "" end if current_node ~= nil then if current_node.name == "ignore" then minetest.log(LOGLEVEL_WARNING,"MOBF: " ..mobname .. " spawned at unloaded pos! : " .. dump(pos) .. " node: " .. dump(current_node)) return false else return true end end minetest.log(LOGLEVEL_WARNING,"MOBF: spawned at invalid pos!") return false end ------------------------------------------------------------------------------- -- name: mobf_random_direction() -- --! @brief get a random (blocked) 3d direction -- --! @return 3d dir value ------------------------------------------------------------------------------- function mobf_random_direction() local retval = {} retval.x=math.random(-1,1) retval.y=math.random(-1,1) retval.z=math.random(-1,1) return retval end ------------------------------------------------------------------------------- -- name: mobf_pos_is_same(pos1,pos2) -- --! @brief check if two positions are equal -- --! @param pos1 --! @param pos2 -- --! @return true/false ------------------------------------------------------------------------------- function mobf_pos_is_same(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 or pos1.x == nil or pos1.y == nil or pos1.z == nil or pos2.x == nil or pos2.y == nil or pos2.z == nil then return false end return true end ------------------------------------------------------------------------------- -- name: mobf_is_pos(value) -- --! @brief check if a given value is a position -- --! @param value to check -- --! @return true/false ------------------------------------------------------------------------------- function mobf_is_pos(value) if value == nil or type(value) ~= "table" then return false end if value.x == nil or tonumber(value.x) == nil then return false end if value.y == nil or tonumber(value.y) == nil then return false end if value.z == nil or tonumber(value.z) == nil then return false end return true end ------------------------------------------------------------------------------- -- name: mobf_hash_to_pos(hash) -- --! @brief restore a position from a pos hash value -- --! @param hash to restore pos from -- --! @return posistion reconstructed from hash ------------------------------------------------------------------------------- function mobf_hash_to_pos(hash) local retval = {} local raw_x = (hash % 65536) local raw_y = ((hash - raw_x) % (65536*65536)) / 65536 local raw_z = ((hash - raw_x - raw_y) / 65536) / 65536 local mobpos = {} retval.x = raw_x - 32768 retval.y = raw_y - 32768 retval.z = math.floor(raw_z - 32768) return retval end --!@}