1 -- Plantlife library mod by Vanessa Ezekowitz
2 -- last revision, 2013-01-24
6 -- I got the temperature map idea from "hmmmm", values used for it came from
7 -- Splizard's snow mod.
11 -- Various settings - most of these probably won't need to be changed
17 plantslib
.modpath
= minetest
.get_modpath("plants_lib")
18 plantslib
.intllib_modpath
= minetest
.get_modpath("intllib")
22 if plantslib
.intllib_modpath
then
23 dofile(plantslib
.intllib_modpath
.."/intllib.lua")
24 S
= intllib
.Getter(minetest
.get_current_modname())
26 S
= function ( s
) return s
end
30 local DEBUG
= false --... except if you want to spam the console with debugging info :-)
33 function plantslib
:dbg(msg
)
35 minetest
.log("verbose", "[Plantlife] "..msg
)
40 plantslib
.plantlife_seed_diff
= 329 -- needs to be global so other mods can see it
44 local spawn_plants
= {}
46 local perlin_octaves
= 3
47 local perlin_persistence
= 0.6
48 local perlin_scale
= 100
51 local temperature_seeddiff
= 112
52 local temperature_octaves
= 3
53 local temperature_persistence
= 0.5
54 local temperature_scale
= 150
57 local humidity_seeddiff
= 9130
58 local humidity_octaves
= 3
59 local humidity_persistence
= 0.5
60 local humidity_scale
= 250
64 local time_speed
= tonumber(minetest
.settings
:get("time_speed"))
67 if time_speed
and time_speed
> 0 then
68 time_scale
= 72 / time_speed
72 plantslib
:dbg("time_speed = "..dump(time_speed
))
73 plantslib
:dbg("time_scale = 72 / time_speed = "..dump(time_scale
))
76 --PerlinNoise(seed, octaves, persistence, scale)
79 plantslib
.perlin_temperature
= PerlinNoise(temperature_seeddiff
, temperature_octaves
, temperature_persistence
, temperature_scale
)
80 plantslib
.perlin_humidity
= PerlinNoise(humidity_seeddiff
, humidity_octaves
, humidity_persistence
, humidity_scale
)
86 local function dump_pos(pos
)
87 return "{x="..pos
.x
..",y="..pos
.y
..",z="..pos
.z
.."}"
91 function plantslib
:is_node_loaded(node_pos
)
92 local n
= minetest
.get_node_or_nil(node_pos
)
93 if (not n
) or (n
.name
== "ignore") then
100 function plantslib
:clone_node(name
)
102 node
=minetest
.registered_nodes
[name
]
103 for k
,v
in pairs(node
) do
110 function plantslib
:set_defaults(biome
)
111 biome
.seed_diff
= biome
.seed_diff
or 0
112 biome
.min_elevation
= biome
.min_elevation
or -31000
113 biome
.max_elevation
= biome
.max_elevation
or 31000
114 biome
.temp_min
= biome
.temp_min
or 1
115 biome
.temp_max
= biome
.temp_max
or -1
116 biome
.humidity_min
= biome
.humidity_min
or 1
117 biome
.humidity_max
= biome
.humidity_max
or -1
118 biome
.plantlife_limit
= biome
.plantlife_limit
or 0.1
119 biome
.near_nodes_vertical
= biome
.near_nodes_vertical
or 1
122 -- specific to on-generate
125 biome
.neighbors
= biome
.neighbors
or biome
.surface
126 biome
.near_nodes_size
= biome
.near_nodes_size
or 0
127 biome
.near_nodes_count
= biome
.near_nodes_count
or 1
128 biome
.rarity
= biome
.rarity
or 50
129 biome
.max_count
= biome
.max_count
or 5
130 if biome
.check_air
~= false then biome
.check_air
= true end
133 -- specific to abm spawner
134 biome
.seed_diff
= biome
.seed_diff
or 0
135 biome
.light_min
= biome
.light_min
or 0
136 biome
.light_max
= biome
.light_max
or 15
137 biome
.depth_max
= biome
.depth_max
or 1
138 biome
.facedir
= biome
.facedir
or 0
142 -- Spawn plants using the map generator
145 function plantslib
:register_generate_plant(biomedef
, node_or_function_or_model
)
146 plantslib
:dbg("Registered mapgen spawner:")
147 plantslib
:dbg(dump(biomedef
))
150 minetest
.register_on_generated(plantslib
:search_for_surfaces(minp
, maxp
, biomedef
, node_or_function_or_model
))
154 function plantslib
:search_for_surfaces(minp
, maxp
, biomedef
, node_or_function_or_model
)
155 return function(minp
, maxp
, blockseed
)
159 local biome
= biomedef
160 plantslib
:set_defaults(biome
)
163 plantslib
:dbg("Started checking generated mapblock volume...")
164 local searchnodes
= minetest
.find_nodes_in_area(minp
, maxp
, biome
.surface
)
165 local in_biome_nodes
= {}
166 local num_in_biome_nodes
= 0
167 for i
in ipairs(searchnodes
) do
168 local pos
= searchnodes
[i
]
169 local p_top
= { x
= pos
.x
, y
= pos
.y
+ 1, z
= pos
.z
}
170 local perlin1
= minetest
.get_perlin(biome
.seed_diff
, perlin_octaves
, perlin_persistence
, perlin_scale
)
171 local noise1
= perlin1
:get_2d({x
=p_top
.x
, y
=p_top
.z
})
172 local noise2
= plantslib
.perlin_temperature
:get_2d({x
=p_top
.x
, y
=p_top
.z
})
173 local noise3
= plantslib
.perlin_humidity
:get_2d({x
=p_top
.x
+150, y
=p_top
.z
+50})
174 if (not biome
.depth
or minetest
.get_node({ x
= pos
.x
, y
= pos
.y
-biome
.depth
-1, z
= pos
.z
}).name
~= biome
.surface
)
175 and (not biome
.check_air
or (biome
.check_air
and minetest
.get_node(p_top
).name
== "air"))
176 and pos
.y
>= biome
.min_elevation
177 and pos
.y
<= biome
.max_elevation
178 -- and noise1 > biome.plantlife_limit
179 -- and noise2 <= biome.temp_min
180 -- and noise2 >= biome.temp_max
181 -- and noise3 <= biome.humidity_min
182 -- and noise3 >= biome.humidity_max
183 and (not biome
.ncount
or table.getn(minetest
.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
)
184 and (not biome
.near_nodes
or table.getn(minetest
.find_nodes_in_area({x
=pos
.x
-biome
.near_nodes_size
, y
=pos
.y
-biome
.near_nodes_vertical
, z
=pos
.z
-biome
.near_nodes_size
}, {x
=pos
.x
+biome
.near_nodes_size
, y
=pos
.y
+biome
.near_nodes_vertical
, z
=pos
.z
+biome
.near_nodes_size
}, biome
.near_nodes
)) >= biome
.near_nodes_count
)
185 and math
.random(1,100) > biome
.rarity
186 and (not biome
.below_nodes
or string.find(dump(biome
.below_nodes
), minetest
.get_node({x
=pos
.x
, y
=pos
.y
-1, z
=pos
.z
}).name
) )
188 table.insert(in_biome_nodes
, pos
)
189 num_in_biome_nodes
= num_in_biome_nodes
+ 1
194 plantslib
:dbg("Found "..num_in_biome_nodes
.." surface nodes of type(s) "..dump(biome
.surface
).." in 5x5x5 mapblock volume at {"..dump(minp
)..":"..dump(maxp
).."} to check.")
197 if num_in_biome_nodes
> 0 then
198 plantslib
:dbg("Calculated maximum of "..math
.min(biome
.max_count
*3, num_in_biome_nodes
).." nodes to be checked in that list.")
199 for i
= 1, math
.min(biome
.max_count
, num_in_biome_nodes
) do
201 local spawned
= false
202 while tries
< 2 and not spawned
do
203 local pos
= in_biome_nodes
[math
.random(1, num_in_biome_nodes
)]
204 if biome
.spawn_replace_node
then
207 local p_top
= { x
= pos
.x
, y
= pos
.y
+ 1, z
= pos
.z
}
210 if not (biome
.avoid_nodes
and biome
.avoid_radius
and minetest
.find_node_near(p_top
, biome
.avoid_radius
+ math
.random(-1.5,2), biome
.avoid_nodes
)) then
211 if biome
.delete_above
then
212 minetest
.remove_node(p_top
)
213 minetest
.remove_node({x
=p_top
.x
, y
=p_top
.y
+1, z
=p_top
.z
})
217 if biome
.delete_above_surround
then
218 minetest
.remove_node({x
=p_top
.x
-1, y
=p_top
.y
, z
=p_top
.z
})
219 minetest
.remove_node({x
=p_top
.x
+1, y
=p_top
.y
, z
=p_top
.z
})
220 minetest
.remove_node({x
=p_top
.x
, y
=p_top
.y
, z
=p_top
.z
-1})
221 minetest
.remove_node({x
=p_top
.x
, y
=p_top
.y
, z
=p_top
.z
+1})
224 minetest
.remove_node({x
=p_top
.x
-1, y
=p_top
.y
+1, z
=p_top
.z
})
225 minetest
.remove_node({x
=p_top
.x
+1, y
=p_top
.y
+1, z
=p_top
.z
})
226 minetest
.remove_node({x
=p_top
.x
, y
=p_top
.y
+1, z
=p_top
.z
-1})
227 minetest
.remove_node({x
=p_top
.x
, y
=p_top
.y
+1, z
=p_top
.z
+1})
231 if biome
.spawn_replace_node
then
232 minetest
.remove_node(pos
)
236 if type(node_or_function_or_model
) == "table" then
237 plantslib
:dbg("Spawn tree at {"..dump(pos
).."}")
238 plantslib
:generate_tree(pos
, node_or_function_or_model
)
241 elseif type(node_or_function_or_model
) == "string" then
242 if not minetest
.registered_nodes
[node_or_function_or_model
] then
243 plantslib
:dbg("Call function: "..node_or_function_or_model
.."("..dump_pos(pos
)..")")
245 assert(loadstring(node_or_function_or_model
.."("..dump_pos(pos
)..")"))()
246 plantslib
:dbg("Executed that function in ".. (os
.clock()-t2
)*1000 .."ms")
248 plantslib
:dbg("Add node: "..node_or_function_or_model
.." at ("..dump(p_top
)..")")
249 minetest
.add_node(p_top
, { name
= node_or_function_or_model
})
255 plantslib
:dbg("No room to spawn object at {"..dump(pos
).."} -- trying again elsewhere")
259 plantslib
:dbg("Unable to spawn that object. Giving up on it.")
263 plantslib
:dbg("Evaluated/populated chunk in ".. (os
.clock()-t1
)*1000 .."ms")
271 function plantslib
:spawn_on_surfaces(sd
,sp
,sr
,sc
,ss
,sa
)
277 if type(sd
) ~= "table" then
278 biome
.spawn_delay
= sd
-- old api expects ABM interval param here.
279 biome
.spawn_plants
= {sp
}
280 biome
.avoid_radius
= sr
281 biome
.spawn_chance
= sc
282 biome
.spawn_surfaces
= {ss
}
283 biome
.avoid_nodes
= sa
289 if biome
.spawn_delay
*time_scale
>= 1 then
290 biome
.interval
= biome
.spawn_delay
*time_scale
296 plantslib
:set_defaults(biome
)
297 biome
.spawn_plants_count
= table.getn(biome
.spawn_plants
)
300 plantslib
:dbg("Registered spawning ABM:")
301 plantslib
:dbg(dump(biome
))
302 plantslib
:dbg("Number of trigger nodes in this ABM: "..biome
.spawn_plants_count
)
305 minetest
.register_abm({
306 nodenames
= biome
.spawn_surfaces
,
307 interval
= biome
.interval
,
308 chance
= biome
.spawn_chance
,
309 neighbors
= biome
.neighbors
,
310 action
= function(pos
, node
, active_object_count
, active_object_count_wider
)
311 local p_top
= { x
= pos
.x
, y
= pos
.y
+ 1, z
= pos
.z
}
312 local n_top
= minetest
.get_node(p_top
)
313 local perlin1
= minetest
.get_perlin(biome
.seed_diff
, perlin_octaves
, perlin_persistence
, perlin_scale
)
314 local noise1
= perlin1
:get_2d({x
=p_top
.x
, y
=p_top
.z
})
315 local noise2
= plantslib
.perlin_temperature
:get_2d({x
=p_top
.x
, y
=p_top
.z
})
316 local noise3
= plantslib
.perlin_humidity
:get_2d({x
=p_top
.x
+150, y
=p_top
.z
+50})
317 -- if noise1 > biome.plantlife_limit
318 if plantslib
:is_node_loaded(p_top
) then
319 local n_light
= minetest
.get_node_light(p_top
, nil)
320 if not (biome
.avoid_nodes
and biome
.avoid_radius
and minetest
.find_node_near(p_top
, biome
.avoid_radius
+ math
.random(-1.5,2), biome
.avoid_nodes
))
321 and n_light
>= biome
.light_min
322 and n_light
<= biome
.light_max
323 and (not(biome
.neighbors
and biome
.ncount
) or table.getn(minetest
.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
)
324 and (not(biome
.near_nodes
and biome
.near_nodes_count
and biome
.near_nodes_size
) or table.getn(minetest
.find_nodes_in_area({x
=pos
.x
-biome
.near_nodes_size
, y
=pos
.y
-biome
.near_nodes_vertical
, z
=pos
.z
-biome
.near_nodes_size
}, {x
=pos
.x
+biome
.near_nodes_size
, y
=pos
.y
+biome
.near_nodes_vertical
, z
=pos
.z
+biome
.near_nodes_size
}, biome
.near_nodes
)) >= biome
.near_nodes_count
)
325 and (not(biome
.air_count
and biome
.air_size
) or table.getn(minetest
.find_nodes_in_area({x
=p_top
.x
-biome
.air_size
, y
=p_top
.y
, z
=p_top
.z
-biome
.air_size
}, {x
=p_top
.x
+biome
.air_size
, y
=p_top
.y
, z
=p_top
.z
+biome
.air_size
}, "air")) >= biome
.air_count
)
327 local walldir
= plantslib
:find_adjacent_wall(p_top
, biome
.verticals_list
)
328 if biome
.alt_wallnode
and walldir
then
329 if n_top
.name
== "air" then
330 plantslib
:dbg("Spawn: "..biome
.alt_wallnode
.." on top of ("..dump(pos
)..") against wall "..walldir
)
331 minetest
.add_node(p_top
, { name
= biome
.alt_wallnode
, param2
= walldir
})
334 local currentsurface
= minetest
.get_node(pos
).name
335 if currentsurface
~= "default:water_source"
336 or (currentsurface
== "default:water_source" and table.getn(minetest
.find_nodes_in_area({x
=pos
.x
, y
=pos
.y
-biome
.depth_max
-1, z
=pos
.z
}, {x
=pos
.x
, y
=pos
.y
, z
=pos
.z
}, {"default:dirt", "default:dirt_with_grass", "default:ash"})) > 0 )
338 local rnd
= math
.random(1, biome
.spawn_plants_count
)
339 local plant_to_spawn
= biome
.spawn_plants
[rnd
]
340 plantslib
:dbg("Chose entry number "..rnd
.." of "..biome
.spawn_plants_count
)
341 local fdir
= biome
.facedir
342 if biome
.random_facedir
then
343 fdir
= math
.random(biome
.random_facedir
[1],biome
.random_facedir
[2])
344 plantslib
:dbg("Gave it a random facedir: "..fdir
)
346 if type(spawn_plants
) == "string" then
347 plantslib
:dbg("Call function: "..spawn_plants
.."("..dump_pos(pos
)..")")
348 assert(loadstring(spawn_plants
.."("..dump_pos(pos
)..")"))()
349 elseif not biome
.spawn_on_side
and not biome
.spawn_on_bottom
and not biome
.spawn_replace_node
then
350 if n_top
.name
== "air" then
351 plantslib
:dbg("Spawn: "..plant_to_spawn
.." on top of ("..dump(pos
).."); facedir="..fdir
)
352 minetest
.add_node(p_top
, { name
= plant_to_spawn
, param2
= fdir
})
354 elseif biome
.spawn_replace_node
then
357 plantslib
:dbg("Spawn: "..plant_to_spawn
.." to replace "..minetest
.get_node(pos
).name
.." at ("..dump(pos
)..")")
358 minetest
.add_node(pos
, { name
= plant_to_spawn
, param2
= fdir
})
361 elseif biome
.spawn_on_side
then
362 local onside
= plantslib
:find_open_side(pos
)
364 plantslib
:dbg("Spawn: "..plant_to_spawn
.." at side of ("..dump(pos
).."), facedir "..onside
.facedir
.."")
365 minetest
.add_node(onside
.newpos
, { name
= plant_to_spawn
, param2
= onside
.facedir
})
367 elseif biome
.spawn_on_bottom
then
368 if minetest
.get_node({x
=pos
.x
, y
=pos
.y
-1, z
=pos
.z
}).name
== "air" then
369 plantslib
:dbg("Spawn: "..plant_to_spawn
.." on bottom of ("..dump(pos
)..")")
370 minetest
.add_node({x
=pos
.x
, y
=pos
.y
-1, z
=pos
.z
}, { name
= plant_to_spawn
, param2
= fdir
} )
385 function plantslib
:grow_plants(opts
)
391 options
.height_limit
= options
.height_limit
or 5
392 options
.ground_nodes
= options
.ground_nodes
or { "default:dirt_with_grass" }
393 options
.grow_nodes
= options
.grow_nodes
or { "default:dirt_with_grass" }
394 options
.seed_diff
= options
.seed_diff
or 0
397 plantslib
:dbg("Registered growing ABM:")
398 plantslib
:dbg(dump(options
))
401 if options
.grow_delay
*time_scale
>= 1 then
402 options
.interval
= options
.grow_delay
*time_scale
408 minetest
.register_abm({
409 nodenames
= { options
.grow_plant
},
410 interval
= options
.interval
,
411 chance
= options
.grow_chance
,
412 action
= function(pos
, node
, active_object_count
, active_object_count_wider
)
413 local p_top
= {x
=pos
.x
, y
=pos
.y
+1, z
=pos
.z
}
414 local p_bot
= {x
=pos
.x
, y
=pos
.y
-1, z
=pos
.z
}
415 local n_top
= minetest
.get_node(p_top
)
416 local n_bot
= minetest
.get_node(p_bot
)
417 local root_node
= minetest
.get_node({x
=pos
.x
, y
=pos
.y
-options
.height_limit
, z
=pos
.z
})
419 if options
.need_wall
and options
.verticals_list
then
420 walldir
= plantslib
:find_adjacent_wall(p_top
, options
.verticals_list
)
422 if n_top
.name
== "air" and (not options
.need_wall
or (options
.need_wall
and walldir
))
424 -- corner case for changing short junglegrass
425 -- to dry shrub in desert
426 if n_bot
.name
== options
.dry_early_node
and options
.grow_plant
== "junglegrass:short" then
427 plantslib
:dbg("Die: "..options
.grow_plant
.." becomes default:dry_shrub at ("..dump(pos
)..")")
428 minetest
.add_node(pos
, { name
= "default:dry_shrub" })
431 elseif options
.grow_vertically
and walldir
then
432 if plantslib
:search_downward(pos
, options
.height_limit
, options
.ground_nodes
) then
433 plantslib
:dbg("Grow "..options
.grow_plant
.." vertically to "..dump(p_top
))
434 minetest
.add_node(p_top
, { name
= options
.grow_plant
, param2
= walldir
})
438 elseif not options
.grow_result
and not options
.grow_function
then
439 plantslib
:dbg("Die: "..options
.grow_plant
.." at ("..dump(pos
)..")")
440 minetest
.remove_node(pos
)
444 plantslib
:replace_object(pos
, options
.grow_result
, options
.grow_function
, options
.facedir
, options
.seed_diff
)
452 -- Function to decide how to replace a plant - either grow it, replace it with
453 -- a tree, run a function, or die with an error.
456 function plantslib
:replace_object(pos
, replacement
, grow_function
, walldir
, seeddiff
)
457 local growtype
= type(grow_function
)
458 plantslib
:dbg("replace_object called, growtype="..dump(grow_function
))
459 if growtype
== "table" then
460 plantslib
:dbg("Grow: spawn tree at "..dump(pos
))
461 minetest
.remove_node(pos
)
462 plantslib
:grow_tree(pos
, grow_function
)
464 elseif growtype
== "string" then
465 local perlin1
= minetest
.get_perlin(seeddiff
, perlin_octaves
, perlin_persistence
, perlin_scale
)
466 local noise1
= perlin1
:get_2d({x
=pos
.x
, y
=pos
.z
})
467 local noise2
= plantslib
.perlin_temperature
:get_2d({x
=pos
.x
, y
=pos
.z
})
468 plantslib
:dbg("Grow: call function "..grow_function
.."("..dump_pos(pos
)..","..dump(walldir
)..")")
469 assert(loadstring(grow_function
.."("..dump_pos(pos
)..","..dump(walldir
)..")"))()
471 elseif growtype
== "nil" then
472 plantslib
:dbg("Grow: place "..replacement
.." at ("..dump(pos
)..") on wall "..dump(walldir
))
473 minetest
.add_node(pos
, { name
= replacement
, param2
= walldir
})
475 elseif growtype
~= "nil" and growtype
~= "string" and growtype
~= "table" then
476 error("Invalid grow function "..dump(grow_function
).." used on object at ("..dump(pos
)..")")
481 -- function to decide if a node has a wall that's in verticals_list{}
482 -- returns wall direction of valid node, or nil if invalid.
485 function plantslib
:find_adjacent_wall(pos
, verticals
)
486 local verts
= dump(verticals
)
487 if string.find(verts
, minetest
.get_node({ x
=pos
.x
-1, y
=pos
.y
, z
=pos
.z
}).name
) then return 3 end
488 if string.find(verts
, minetest
.get_node({ x
=pos
.x
+1, y
=pos
.y
, z
=pos
.z
}).name
) then return 2 end
489 if string.find(verts
, minetest
.get_node({ x
=pos
.x
, y
=pos
.y
, z
=pos
.z
-1 }).name
) then return 5 end
490 if string.find(verts
, minetest
.get_node({ x
=pos
.x
, y
=pos
.y
, z
=pos
.z
+1 }).name
) then return 4 end
495 -- Function to search downward from the given position, looking for the first
496 -- node that matches the ground table. Returns the new position, or nil if
497 -- height limit is exceeded before finding it.
500 function plantslib
:search_downward(pos
, heightlimit
, ground
)
501 for i
= 0, heightlimit
do
502 if string.find(dump(ground
), minetest
.get_node({x
=pos
.x
, y
=pos
.y
-i
, z
= pos
.z
}).name
) then
503 return {x
=pos
.x
, y
=pos
.y
-i
, z
= pos
.z
}
510 function plantslib
:find_open_side(pos
)
511 if minetest
.get_node({ x
=pos
.x
-1, y
=pos
.y
, z
=pos
.z
}).name
== "air" then
512 return {newpos
= { x
=pos
.x
-1, y
=pos
.y
, z
=pos
.z
}, facedir
= 2}
514 if minetest
.get_node({ x
=pos
.x
+1, y
=pos
.y
, z
=pos
.z
}).name
== "air" then
515 return {newpos
= { x
=pos
.x
+1, y
=pos
.y
, z
=pos
.z
}, facedir
= 3}
517 if minetest
.get_node({ x
=pos
.x
, y
=pos
.y
, z
=pos
.z
-1 }).name
== "air" then
518 return {newpos
= { x
=pos
.x
, y
=pos
.y
, z
=pos
.z
-1 }, facedir
= 4}
520 if minetest
.get_node({ x
=pos
.x
, y
=pos
.y
, z
=pos
.z
+1 }).name
== "air" then
521 return {newpos
= { x
=pos
.x
, y
=pos
.y
, z
=pos
.z
+1 }, facedir
= 5}
527 -- spawn_tree() on generate is routed through here so that other mods can hook
531 function plantslib
:generate_tree(pos
, node_or_function_or_model
)
533 minetest
.spawn_tree(pos
, node_or_function_or_model
)
534 plantslib
:dbg("Generated one tree in ".. (os
.clock()-t
)*1000 .."ms")
538 -- and this one's for the call used in the growing code
541 function plantslib
:grow_tree(pos
, node_or_function_or_model
)
543 minetest
.spawn_tree(pos
, node_or_function_or_model
)
544 plantslib
:dbg("Generated one tree in ".. (os
.clock()-t
)*1000 .."ms")
548 -- check if a node is owned before allowing manual placement of a node
549 -- (used by flowers_plus)
552 function plantslib
:node_is_owned(pos
, placer
)
553 local ownername
= false
554 if type(IsPlayerNodeOwner
) == "function" then -- node_ownership mod
555 if HasOwner(pos
, placer
) then -- returns true if the node is owned
556 if not IsPlayerNodeOwner(pos
, placer
:get_player_name()) then
557 if type(getLastOwner
) == "function" then -- ...is an old version
558 ownername
= getLastOwner(pos
)
559 elseif type(GetNodeOwnerName
) == "function" then -- ...is a recent version
560 ownername
= GetNodeOwnerName(pos
)
562 ownername
= S("someone")
568 elseif type(isprotect
)=="function" then -- glomie's protection mod
569 if not isprotect(5, pos
, placer
) then
570 ownername
= S("someone")
572 elseif type(protector
)=="table" and type(protector
.can_dig
)=="function" then -- Zeg9's protection mod
573 if not protector
.can_dig(5, pos
, placer
) then
574 ownername
= S("someone")
579 if ownername
~= false then
580 minetest
.chat_send_player( placer
:get_player_name(), S("Sorry, %s owns that spot."):format(ownername
) )
588 -- Check for infinite stacks
591 if minetest
.get_modpath("unified_inventory") or not minetest
.settings
:get_bool("creative_mode") then
592 plantslib
.expect_infinite_stacks
= false
594 plantslib
.expect_infinite_stacks
= true
598 -- read a field from a node's definition
601 function plantslib
:get_nodedef_field(nodename
, fieldname
)
602 if not minetest
.registered_nodes
[nodename
] then
605 return minetest
.registered_nodes
[nodename
][fieldname
]
611 minetest
.log("action", S("[Plantlife Library] Loaded"))