2013-01-10 03:33:37 +01:00
-- Plantlife library mod by Vanessa Ezekowitz
2013-01-20 07:38:43 +01:00
-- 2013-01-20
2012-12-05 01:29:23 +01:00
--
2013-01-10 03:33:37 +01:00
-- License: WTFPL
2013-01-12 06:47:50 +01:00
--
-- I got the temperature map idea from "hmmmm", values used for it came from
2013-01-19 06:45:46 +01:00
-- Splizard's snow mod.
2013-01-12 06:47:50 +01:00
--
2012-12-05 01:29:23 +01:00
-- Various settings - most of these probably won't need to be changed
2013-01-19 06:45:46 +01:00
plantslib = { }
2013-01-05 00:56:09 +01:00
2013-01-20 19:51:09 +01:00
local DEBUG = false --... except if you want to spam the console with debugging info :-)
2013-01-19 06:45:46 +01:00
plantslib.plantlife_seed_diff = 329 -- needs to be global so other mods can see it
2012-12-05 01:29:23 +01:00
local perlin_octaves = 3
2013-01-05 00:56:09 +01:00
local perlin_persistence = 0.6
local perlin_scale = 100
2012-12-05 01:29:23 +01:00
2013-01-12 06:47:50 +01:00
local temperature_seeddiff = 112
local temperature_octaves = 3
local temperature_persistence = 0.5
local temperature_scale = 150
2013-01-20 19:51:09 +01:00
local plantlife_limit = 0.1
2012-12-05 01:29:23 +01:00
-- Local functions
math.randomseed ( os.time ( ) )
2013-01-19 06:45:46 +01:00
function plantslib : is_node_loaded ( node_pos )
2012-12-05 01:29:23 +01:00
n = minetest.env : get_node_or_nil ( node_pos )
if ( n == nil ) or ( n.name == " ignore " ) then
return false
end
return true
end
2013-01-20 19:51:09 +01:00
function dbg ( msg )
if DEBUG then minetest.log ( " verbose " , msg ) end
end
2013-01-19 06:45:46 +01:00
-- Spawn plants using the map generator
2013-01-20 05:01:20 +01:00
function plantslib : register_generate_plant ( biome , funct_or_model )
minetest.register_on_generated ( plantslib : search_for_surfaces ( minp , maxp , biome , funct_or_model ) )
2013-01-19 06:45:46 +01:00
end
2013-01-20 05:01:20 +01:00
function plantslib : search_for_surfaces ( minp , maxp , biome , funct_or_model )
2013-01-19 06:45:46 +01:00
return function ( minp , maxp , blockseed )
if biome.seed_diff == nil then biome.seed_diff = 0 end
if biome.neighbors == nil then biome.neighbors = biome.surface end
if biome.min_elevation == nil then biome.min_elevation = - 31000 end
if biome.max_elevation == nil then biome.max_elevation = 31000 end
if biome.near_nodes_size == nil then biome.near_nodes_size = 0 end
if biome.near_nodes_count == nil then biome.near_nodes_count = 1 end
2013-01-20 05:01:20 +01:00
if biome.temp_min == nil then biome.temp_min = 1 end
if biome.temp_max == nil then biome.temp_max = - 1 end
2013-01-20 18:02:59 +01:00
if biome.rarity == nil then biome.rarity = 50 end
2013-01-19 06:45:46 +01:00
2013-01-20 19:56:13 +01:00
dbg ( " Started checking generated mapblock volume... " )
2013-01-19 08:10:26 +01:00
local searchnodes = minetest.env : find_nodes_in_area ( minp , maxp , biome.surface )
local in_biome_nodes = { }
local num_in_biome_nodes = 0
for i in ipairs ( searchnodes ) do
local pos = searchnodes [ i ]
local p_top = { x = pos.x , y = pos.y + 1 , z = pos.z }
2013-01-19 06:45:46 +01:00
local perlin1 = minetest.env : get_perlin ( biome.seed_diff , perlin_octaves , perlin_persistence , perlin_scale )
local perlin2 = minetest.env : get_perlin ( temperature_seeddiff , temperature_octaves , temperature_persistence , temperature_scale )
local noise1 = perlin1 : get2d ( { x = p_top.x , y = p_top.z } )
local noise2 = perlin2 : get2d ( { x = p_top.x , y = p_top.z } )
2013-01-19 08:10:26 +01:00
if ( biome.depth == nil or minetest.env : get_node ( { x = pos.x , y = pos.y - biome.depth - 1 , z = pos.z } ) . name ~= biome.surface )
and minetest.env : get_node ( p_top ) . name == " air "
and pos.y >= biome.min_elevation
and pos.y <= biome.max_elevation
2013-01-19 06:45:46 +01:00
and noise1 > plantlife_limit
2013-01-20 05:01:20 +01:00
and noise2 <= biome.temp_min
and noise2 >= biome.temp_max
2013-01-19 20:24:15 +01:00
and ( biome.ncount == nil or table.getn ( minetest.env : find_nodes_in_area ( { x = pos.x - 1 , y = pos.y , z = pos.z - 1 } , { x = pos.x + 1 , y = pos.y , z = pos.z + 1 } , biome.neighbors ) ) > biome.ncount )
and ( biome.near_nodes == nil or table.getn ( minetest.env : find_nodes_in_area ( { x = pos.x - biome.near_nodes_size , y = pos.y - 1 , z = pos.z - biome.near_nodes_size } , { x = pos.x + biome.near_nodes_size , y = pos.y + 1 , z = pos.z + biome.near_nodes_size } , biome.near_nodes ) ) >= biome.near_nodes_count )
2013-01-20 18:01:07 +01:00
and math.random ( 1 , 100 ) > biome.rarity
2013-01-19 08:10:26 +01:00
then
table.insert ( in_biome_nodes , pos )
num_in_biome_nodes = num_in_biome_nodes + 1
end
end
2013-01-20 19:56:13 +01:00
dbg ( " Found " .. num_in_biome_nodes .. " surface nodes of type " .. biome.surface .. " in 5x5x5 mapblock volume at { " .. dump ( minp ) .. " : " .. dump ( maxp ) .. " } to check. " )
2013-01-19 08:10:26 +01:00
for i in ipairs ( in_biome_nodes ) do
local pos = in_biome_nodes [ i ]
local p_top = { x = pos.x , y = pos.y + 1 , z = pos.z }
2013-01-20 05:01:20 +01:00
if minetest.env : find_node_near ( p_top , biome.avoid_radius + math.random ( - 1.5 , 1.5 ) , biome.avoid_nodes ) == nil then
if type ( funct_or_model ) == " table " then
2013-01-20 19:56:13 +01:00
dbg ( " Spawn tree at { " .. dump ( pos ) .. " } " )
2013-01-20 05:01:20 +01:00
minetest.env : spawn_tree ( pos , funct_or_model )
else
2013-01-20 19:56:13 +01:00
dbg ( " Call function: " .. funct_or_model .. " ( " .. dump ( pos ) .. " ) " )
2013-01-20 19:51:09 +01:00
dbg ( " Call function: " .. funct_or_model .. " ( " .. dump ( pos ) .. " ) " )
2013-01-20 05:01:20 +01:00
assert ( loadstring ( funct_or_model .. " ( " .. dump ( pos ) .. " ) " ) ) ( )
end
2013-01-19 06:45:46 +01:00
end
end
end
end
2012-12-05 01:29:23 +01:00
-- The spawning ABM
2013-01-19 06:45:46 +01:00
function plantslib : spawn_on_surfaces (
sdelay ,
splant ,
sradius ,
schance ,
ssurface ,
savoid ,
seed_diff ,
lightmin ,
lightmax ,
nneighbors ,
ocount ,
facedir ,
depthmax ,
altmin ,
altmax ,
sbiome ,
sbiomesize ,
sbiomecount ,
airsize ,
aircount ,
tempmin ,
tempmax )
2012-12-05 01:29:23 +01:00
if seed_diff == nil then seed_diff = 0 end
if lightmin == nil then lightmin = 0 end
if lightmax == nil then lightmax = LIGHT_MAX end
if nneighbors == nil then nneighbors = ssurface end
2013-01-10 03:33:37 +01:00
if ocount == nil then ocount = - 1 end
2012-12-05 01:29:23 +01:00
if depthmax == nil then depthmax = 1 end
2012-12-29 23:24:47 +01:00
if altmin == nil then altmin = - 31000 end
if altmax == nil then altmax = 31000 end
2013-01-10 03:33:37 +01:00
if sbiome == nil then sbiome = " " end
if sbiomesize == nil then sbiomesize = 0 end
if sbiomecount == nil then sbiomecount = 1 end
2013-01-11 17:52:34 +01:00
if airsize == nil then airsize = 0 end
if aircount == nil then aircount = 1 end
2013-01-12 06:47:50 +01:00
if tempmin == nil then tempmin = - 2 end
if tempmax == nil then tempmax = 2 end
2012-12-05 01:29:23 +01:00
minetest.register_abm ( {
nodenames = { ssurface } ,
interval = sdelay ,
chance = schance ,
neighbors = nneighbors ,
action = function ( pos , node , active_object_count , active_object_count_wider )
local p_top = { x = pos.x , y = pos.y + 1 , z = pos.z }
local n_top = minetest.env : get_node ( p_top )
2013-01-12 06:47:50 +01:00
local perlin1 = minetest.env : get_perlin ( seed_diff , perlin_octaves , perlin_persistence , perlin_scale )
local perlin2 = minetest.env : get_perlin ( temperature_seeddiff , temperature_octaves , temperature_persistence , temperature_scale )
local noise1 = perlin1 : get2d ( { x = p_top.x , y = p_top.z } )
local noise2 = perlin2 : get2d ( { x = p_top.x , y = p_top.z } )
if noise1 > plantlife_limit
and noise2 >= tempmin
and noise2 <= tempmax
2013-01-19 06:45:46 +01:00
and plantslib : is_node_loaded ( p_top ) then
2012-12-05 01:29:23 +01:00
local n_light = minetest.env : get_node_light ( p_top , nil )
2012-12-29 23:24:47 +01:00
if minetest.env : find_node_near ( p_top , sradius + math.random ( - 1.5 , 2 ) , savoid ) == nil
and n_light >= lightmin
and n_light <= lightmax
2013-01-11 17:52:34 +01:00
and ( table.getn ( minetest.env : find_nodes_in_area ( { x = pos.x - 1 , y = pos.y , z = pos.z - 1 } , { x = pos.x + 1 , y = pos.y , z = pos.z + 1 } , nneighbors ) ) > ocount
2013-01-10 03:33:37 +01:00
or ocount == - 1 )
2013-01-11 17:52:34 +01:00
and ( table.getn ( minetest.env : find_nodes_in_area ( { x = pos.x - sbiomesize , y = pos.y - 1 , z = pos.z - sbiomesize } , { x = pos.x + sbiomesize , y = pos.y + 1 , z = pos.z + sbiomesize } , sbiome ) ) >= sbiomecount
2013-01-10 03:33:37 +01:00
or sbiome == " " )
2013-01-11 17:52:34 +01:00
and table.getn ( minetest.env : find_nodes_in_area ( { x = p_top.x - airsize , y = p_top.y , z = p_top.z - airsize } , { x = p_top.x + airsize , y = p_top.y , z = p_top.z + airsize } , " air " ) ) >= aircount
2012-12-29 23:24:47 +01:00
and pos.y >= altmin
and pos.y <= altmax
then
2013-01-19 06:45:46 +01:00
local walldir = plantslib : plant_valid_wall ( p_top )
2012-12-29 23:24:47 +01:00
if splant == " poisonivy:seedling " and walldir ~= nil then
2013-01-20 19:51:09 +01:00
dbg ( " Spawn: poisonivy:climbing at " .. dump ( p_top ) .. " on " .. ssurface )
2012-12-29 23:24:47 +01:00
minetest.env : add_node ( p_top , { name = " poisonivy:climbing " , param2 = walldir } )
else
local deepnode = minetest.env : get_node ( { x = pos.x , y = pos.y - depthmax - 1 , z = pos.z } ) . name
if ( ssurface ~= " default:water_source " )
or ( ssurface == " default:water_source "
and deepnode ~= " default:water_source " ) then
2013-01-20 19:51:09 +01:00
dbg ( " Spawn: " .. splant .. " at " .. dump ( p_top ) .. " on " .. ssurface )
2012-12-29 23:24:47 +01:00
minetest.env : add_node ( p_top , { name = splant , param2 = facedir } )
end
2012-12-05 01:29:23 +01:00
end
end
end
end
} )
end
-- The growing ABM
2013-01-19 06:45:46 +01:00
function plantslib : grow_plants (
gdelay ,
gchance ,
gplant ,
gresult ,
dry_early_node ,
grow_nodes ,
facedir ,
need_wall ,
grow_vertically ,
height_limit ,
ground_nodes ,
grow_function ,
seed_diff )
2012-12-10 04:02:30 +01:00
if need_wall ~= true then need_wall = false end
if grow_vertically ~= true then grow_vertically = false end
if height_limit == nil then height_limit = 62000 end
if ground_node == nil then ground_nodes = { " default:dirt_with_grass " } end
2012-12-05 01:29:23 +01:00
minetest.register_abm ( {
nodenames = { gplant } ,
interval = gdelay ,
chance = gchance ,
action = function ( pos , node , active_object_count , active_object_count_wider )
local p_top = { x = pos.x , y = pos.y + 1 , z = pos.z }
local p_bot = { x = pos.x , y = pos.y - 1 , z = pos.z }
local n_top = minetest.env : get_node ( p_top )
local n_bot = minetest.env : get_node ( p_bot )
2012-12-10 04:02:30 +01:00
local groundnode = minetest.env : get_node ( { x = pos.x , y = pos.y - height_limit , z = pos.z } )
2013-01-10 03:33:37 +01:00
if grow_function == nil then
if string.find ( dump ( grow_nodes ) , n_bot.name ) ~= nil and n_top.name == " air " then
if grow_vertically then
2013-01-19 06:45:46 +01:00
if plantslib : find_first_node ( pos , height_limit , ground_nodes ) ~= nil then
2013-01-10 03:33:37 +01:00
if need_wall then
2013-01-19 06:45:46 +01:00
local walldir = plantslib : plant_valid_wall ( p_top )
2013-01-10 03:33:37 +01:00
if walldir ~= nil then
2013-01-20 19:51:09 +01:00
dbg ( " Grow: " .. gplant .. " upwards to ( " .. dump ( p_top ) .. " ) on wall " .. walldir )
2013-01-10 03:33:37 +01:00
minetest.env : add_node ( p_top , { name = gplant , param2 = walldir } )
end
else
2013-01-20 19:51:09 +01:00
dbg ( " Grow: " .. gplant .. " upwards to ( " .. dump ( p_top ) .. " ) " )
2013-01-10 03:33:37 +01:00
minetest.env : add_node ( p_top , { name = gplant } )
2012-12-10 04:02:30 +01:00
end
end
2012-12-05 01:29:23 +01:00
2013-01-10 03:33:37 +01:00
-- corner case for changing short junglegrass to dry shrub in desert
elseif n_bot.name == dry_early_node and gplant == " junglegrass:short " then
2013-01-20 19:51:09 +01:00
dbg ( " Die: " .. gplant .. " becomes default:dry_shrub at ( " .. dump ( pos ) .. " ) " )
2013-01-10 03:33:37 +01:00
minetest.env : add_node ( pos , { name = " default:dry_shrub " } )
elseif gresult == nil then
2013-01-20 19:51:09 +01:00
dbg ( " Die: " .. gplant .. " at ( " .. dump ( pos ) .. " ) " )
2013-01-10 03:33:37 +01:00
minetest.env : remove_node ( pos )
elseif gresult ~= nil then
2013-01-20 19:51:09 +01:00
dbg ( " Grow: " .. gplant .. " becomes " .. gresult .. " at ( " .. dump ( pos ) .. " ) " )
2013-01-10 03:33:37 +01:00
if facedir == nil then
minetest.env : add_node ( pos , { name = gresult } )
else
minetest.env : add_node ( pos , { name = gresult , param2 = facedir } )
end
2012-12-05 01:29:23 +01:00
end
end
2013-01-10 03:33:37 +01:00
else
if seed_diff == nil then seed_diff = 0 end
2013-01-12 06:47:50 +01:00
local perlin1 = minetest.env : get_perlin ( seed_diff , perlin_octaves , perlin_persistence , perlin_scale )
local perlin2 = minetest.env : get_perlin ( temperature_seeddiff , temperature_octaves , temperature_persistence , temperature_scale )
local noise1 = perlin1 : get2d ( { x = p_top.x , y = p_top.z } )
local noise2 = perlin2 : get2d ( { x = p_top.x , y = p_top.z } )
2013-01-20 19:37:58 +01:00
if type ( grow_function ) == " table " then
2013-01-20 19:51:09 +01:00
dbg ( " Grow sapling into tree at " .. dump ( pos ) )
2013-01-20 19:37:58 +01:00
minetest.env : remove_node ( pos )
minetest.env : spawn_tree ( pos , grow_function )
else
2013-01-20 19:51:09 +01:00
dbg ( " Call function: " .. grow_function .. " ( " .. dump ( pos ) .. " , " .. noise1 .. " , " .. noise2 .. " ) " )
2013-01-20 19:37:58 +01:00
assert ( loadstring ( grow_function .. " ( " .. dump ( pos ) .. " , " .. noise1 .. " , " .. noise2 .. " ) " ) ) ( )
end
2012-12-05 01:29:23 +01:00
end
end
} )
end
-- function to decide if a node has a wall that's in verticals_list{}
-- returns wall direction of valid node, or nil if invalid.
2013-01-19 06:45:46 +01:00
function plantslib : plant_valid_wall ( wallpos )
2012-12-05 01:29:23 +01:00
local walldir = nil
local verts = dump ( verticals_list )
local testpos = { x = wallpos.x - 1 , y = wallpos.y , z = wallpos.z }
if string.find ( verts , minetest.env : get_node ( testpos ) . name ) ~= nil then walldir = 3 end
local testpos = { x = wallpos.x + 1 , y = wallpos.y , z = wallpos.z }
if string.find ( verts , minetest.env : get_node ( testpos ) . name ) ~= nil then walldir = 2 end
local testpos = { x = wallpos.x , y = wallpos.y , z = wallpos.z - 1 }
if string.find ( verts , minetest.env : get_node ( testpos ) . name ) ~= nil then walldir = 5 end
local testpos = { x = wallpos.x , y = wallpos.y , z = wallpos.z + 1 }
if string.find ( verts , minetest.env : get_node ( testpos ) . name ) ~= nil then walldir = 4 end
return walldir
end
2012-12-10 04:02:30 +01:00
-- Function to search straight down from (pos) to find first node in match list.
2013-01-19 06:45:46 +01:00
function plantslib : find_first_node ( pos , height_limit , nodelist )
2012-12-10 04:02:30 +01:00
for i = 1 , height_limit do
n = minetest.env : get_node ( { x = pos.x , y = pos.y - i , z = pos.z } )
if string.find ( dump ( nodelist ) , n.name ) ~= nil then
return n.name
end
end
return nil
end
2012-12-17 22:01:16 +01:00
print ( " [Plantlife] Loaded! " )
2012-12-10 04:02:30 +01:00