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")
20 local S
= minetest
.get_translator("plantlife")
22 local DEBUG
= false --... except if you want to spam the console with debugging info :-)
25 function plantslib
:dbg(msg
)
27 minetest
.log("verbose", "[Plantlife] "..msg
)
32 plantslib
.plantlife_seed_diff
= 329 -- needs to be global so other mods can see it
36 local spawn_plants
= {}
38 local perlin_octaves
= 3
39 local perlin_persistence
= 0.6
40 local perlin_scale
= 100
43 local temperature_seeddiff
= 112
44 local temperature_octaves
= 3
45 local temperature_persistence
= 0.5
46 local temperature_scale
= 150
49 local humidity_seeddiff
= 9130
50 local humidity_octaves
= 3
51 local humidity_persistence
= 0.5
52 local humidity_scale
= 250
56 local time_speed
= tonumber(minetest
.settings
:get("time_speed"))
59 if time_speed
and time_speed
> 0 then
60 time_scale
= 72 / time_speed
64 plantslib
:dbg("time_speed = "..dump(time_speed
))
65 plantslib
:dbg("time_scale = 72 / time_speed = "..dump(time_scale
))
68 --PerlinNoise(seed, octaves, persistence, scale)
71 plantslib
.perlin_temperature
= PerlinNoise(temperature_seeddiff
, temperature_octaves
, temperature_persistence
, temperature_scale
)
72 plantslib
.perlin_humidity
= PerlinNoise(humidity_seeddiff
, humidity_octaves
, humidity_persistence
, humidity_scale
)
78 local function dump_pos(pos
)
79 return "{x="..pos
.x
..",y="..pos
.y
..",z="..pos
.z
.."}"
83 function plantslib
:is_node_loaded(node_pos
)
84 local n
= minetest
.get_node_or_nil(node_pos
)
85 if (not n
) or (n
.name
== "ignore") then
92 function plantslib
:clone_node(name
)
94 node
=minetest
.registered_nodes
[name
]
95 for k
,v
in pairs(node
) do
102 function plantslib
:set_defaults(biome
)
103 biome
.seed_diff
= biome
.seed_diff
or 0
104 biome
.min_elevation
= biome
.min_elevation
or -31000
105 biome
.max_elevation
= biome
.max_elevation
or 31000
106 biome
.temp_min
= biome
.temp_min
or 1
107 biome
.temp_max
= biome
.temp_max
or -1
108 biome
.humidity_min
= biome
.humidity_min
or 1
109 biome
.humidity_max
= biome
.humidity_max
or -1
110 biome
.plantlife_limit
= biome
.plantlife_limit
or 0.1
111 biome
.near_nodes_vertical
= biome
.near_nodes_vertical
or 1
114 -- specific to on-generate
117 biome
.neighbors
= biome
.neighbors
or biome
.surface
118 biome
.near_nodes_size
= biome
.near_nodes_size
or 0
119 biome
.near_nodes_count
= biome
.near_nodes_count
or 1
120 biome
.rarity
= biome
.rarity
or 50
121 biome
.max_count
= biome
.max_count
or 5
122 if biome
.check_air
~= false then biome
.check_air
= true end
125 -- specific to abm spawner
126 biome
.seed_diff
= biome
.seed_diff
or 0
127 biome
.light_min
= biome
.light_min
or 0
128 biome
.light_max
= biome
.light_max
or 15
129 biome
.depth_max
= biome
.depth_max
or 1
130 biome
.facedir
= biome
.facedir
or 0
134 -- Spawn plants using the map generator
137 function plantslib
:register_generate_plant(biomedef
, node_or_function_or_model
)
138 plantslib
:dbg("Registered mapgen spawner:")
139 plantslib
:dbg(dump(biomedef
))
142 minetest
.register_on_generated(plantslib
:search_for_surfaces(minp
, maxp
, biomedef
, node_or_function_or_model
))
146 function plantslib
:search_for_surfaces(minp
, maxp
, biomedef
, node_or_function_or_model
)
147 return function(minp
, maxp
, blockseed
)
151 local biome
= biomedef
152 plantslib
:set_defaults(biome
)
155 plantslib
:dbg("Started checking generated mapblock volume...")
156 local searchnodes
= minetest
.find_nodes_in_area(minp
, maxp
, biome
.surface
)
157 local in_biome_nodes
= {}
158 local num_in_biome_nodes
= 0
159 for i
in ipairs(searchnodes
) do
160 local pos
= searchnodes
[i
]
161 local p_top
= { x
= pos
.x
, y
= pos
.y
+ 1, z
= pos
.z
}
162 local perlin1
= minetest
.get_perlin(biome
.seed_diff
, perlin_octaves
, perlin_persistence
, perlin_scale
)
163 local noise1
= perlin1
:get_2d({x
=p_top
.x
, y
=p_top
.z
})
164 local noise2
= plantslib
.perlin_temperature
:get_2d({x
=p_top
.x
, y
=p_top
.z
})
165 local noise3
= plantslib
.perlin_humidity
:get_2d({x
=p_top
.x
+150, y
=p_top
.z
+50})
166 if (not biome
.depth
or minetest
.get_node({ x
= pos
.x
, y
= pos
.y
-biome
.depth
-1, z
= pos
.z
}).name
~= biome
.surface
)
167 and (not biome
.check_air
or (biome
.check_air
and minetest
.get_node(p_top
).name
== "air"))
168 and pos
.y
>= biome
.min_elevation
169 and pos
.y
<= biome
.max_elevation
170 -- and noise1 > biome.plantlife_limit
171 -- and noise2 <= biome.temp_min
172 -- and noise2 >= biome.temp_max
173 -- and noise3 <= biome.humidity_min
174 -- and noise3 >= biome.humidity_max
175 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
)
176 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
)
177 and math
.random(1,100) > biome
.rarity
178 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
) )
180 table.insert(in_biome_nodes
, pos
)
181 num_in_biome_nodes
= num_in_biome_nodes
+ 1
186 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.")
189 if num_in_biome_nodes
> 0 then
190 plantslib
:dbg("Calculated maximum of "..math
.min(biome
.max_count
*3, num_in_biome_nodes
).." nodes to be checked in that list.")
191 for i
= 1, math
.min(biome
.max_count
, num_in_biome_nodes
) do
193 local spawned
= false
194 while tries
< 2 and not spawned
do
195 local pos
= in_biome_nodes
[math
.random(1, num_in_biome_nodes
)]
196 if biome
.spawn_replace_node
then
199 local p_top
= { x
= pos
.x
, y
= pos
.y
+ 1, z
= pos
.z
}
202 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
203 if biome
.delete_above
then
204 minetest
.remove_node(p_top
)
205 minetest
.remove_node({x
=p_top
.x
, y
=p_top
.y
+1, z
=p_top
.z
})
209 if biome
.delete_above_surround
then
210 minetest
.remove_node({x
=p_top
.x
-1, y
=p_top
.y
, z
=p_top
.z
})
211 minetest
.remove_node({x
=p_top
.x
+1, y
=p_top
.y
, z
=p_top
.z
})
212 minetest
.remove_node({x
=p_top
.x
, y
=p_top
.y
, z
=p_top
.z
-1})
213 minetest
.remove_node({x
=p_top
.x
, y
=p_top
.y
, z
=p_top
.z
+1})
216 minetest
.remove_node({x
=p_top
.x
-1, y
=p_top
.y
+1, z
=p_top
.z
})
217 minetest
.remove_node({x
=p_top
.x
+1, y
=p_top
.y
+1, z
=p_top
.z
})
218 minetest
.remove_node({x
=p_top
.x
, y
=p_top
.y
+1, z
=p_top
.z
-1})
219 minetest
.remove_node({x
=p_top
.x
, y
=p_top
.y
+1, z
=p_top
.z
+1})
223 if biome
.spawn_replace_node
then
224 minetest
.remove_node(pos
)
228 if type(node_or_function_or_model
) == "table" then
229 plantslib
:dbg("Spawn tree at {"..dump(pos
).."}")
230 plantslib
:generate_tree(pos
, node_or_function_or_model
)
233 elseif type(node_or_function_or_model
) == "string" then
234 if not minetest
.registered_nodes
[node_or_function_or_model
] then
235 plantslib
:dbg("Call function: "..node_or_function_or_model
.."("..dump_pos(pos
)..")")
237 assert(loadstring(node_or_function_or_model
.."("..dump_pos(pos
)..")"))()
238 plantslib
:dbg("Executed that function in ".. (os
.clock()-t2
)*1000 .."ms")
240 plantslib
:dbg("Add node: "..node_or_function_or_model
.." at ("..dump(p_top
)..")")
241 minetest
.add_node(p_top
, { name
= node_or_function_or_model
})
247 plantslib
:dbg("No room to spawn object at {"..dump(pos
).."} -- trying again elsewhere")
251 plantslib
:dbg("Unable to spawn that object. Giving up on it.")
255 plantslib
:dbg("Evaluated/populated chunk in ".. (os
.clock()-t1
)*1000 .."ms")
263 function plantslib
:spawn_on_surfaces(sd
,sp
,sr
,sc
,ss
,sa
)
269 if type(sd
) ~= "table" then
270 biome
.spawn_delay
= sd
-- old api expects ABM interval param here.
271 biome
.spawn_plants
= {sp
}
272 biome
.avoid_radius
= sr
273 biome
.spawn_chance
= sc
274 biome
.spawn_surfaces
= {ss
}
275 biome
.avoid_nodes
= sa
281 if biome
.spawn_delay
*time_scale
>= 1 then
282 biome
.interval
= biome
.spawn_delay
*time_scale
288 plantslib
:set_defaults(biome
)
289 biome
.spawn_plants_count
= table.getn(biome
.spawn_plants
)
292 plantslib
:dbg("Registered spawning ABM:")
293 plantslib
:dbg(dump(biome
))
294 plantslib
:dbg("Number of trigger nodes in this ABM: "..biome
.spawn_plants_count
)
297 minetest
.register_abm({
298 label
= "Plantlife: Spawn plants",
299 nodenames
= biome
.spawn_surfaces
,
300 interval
= biome
.interval
,
301 chance
= biome
.spawn_chance
,
302 neighbors
= biome
.neighbors
,
303 action
= function(pos
, node
, active_object_count
, active_object_count_wider
)
304 local p_top
= { x
= pos
.x
, y
= pos
.y
+ 1, z
= pos
.z
}
305 local n_top
= minetest
.get_node(p_top
)
306 local perlin1
= minetest
.get_perlin(biome
.seed_diff
, perlin_octaves
, perlin_persistence
, perlin_scale
)
307 local noise1
= perlin1
:get_2d({x
=p_top
.x
, y
=p_top
.z
})
308 local noise2
= plantslib
.perlin_temperature
:get_2d({x
=p_top
.x
, y
=p_top
.z
})
309 local noise3
= plantslib
.perlin_humidity
:get_2d({x
=p_top
.x
+150, y
=p_top
.z
+50})
310 -- if noise1 > biome.plantlife_limit
311 if plantslib
:is_node_loaded(p_top
) then
312 local n_light
= minetest
.get_node_light(p_top
, nil)
313 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
))
314 and n_light
>= biome
.light_min
315 and n_light
<= biome
.light_max
316 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
)
317 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
)
318 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
)
320 local walldir
= plantslib
:find_adjacent_wall(p_top
, biome
.verticals_list
)
321 if biome
.alt_wallnode
and walldir
then
322 if n_top
.name
== "air" then
323 plantslib
:dbg("Spawn: "..biome
.alt_wallnode
.." on top of ("..dump(pos
)..") against wall "..walldir
)
324 minetest
.add_node(p_top
, { name
= biome
.alt_wallnode
, param2
= walldir
})
327 local currentsurface
= minetest
.get_node(pos
).name
328 if currentsurface
~= "hades_core:water_source"
329 or (currentsurface
== "hades_core: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
}, {"hades_core:dirt", "hades_core:dirt_with_grass", "hades_core:ash"})) > 0 )
331 local rnd
= math
.random(1, biome
.spawn_plants_count
)
332 local plant_to_spawn
= biome
.spawn_plants
[rnd
]
333 plantslib
:dbg("Chose entry number "..rnd
.." of "..biome
.spawn_plants_count
)
334 local fdir
= biome
.facedir
335 if biome
.random_facedir
then
336 fdir
= math
.random(biome
.random_facedir
[1],biome
.random_facedir
[2])
337 plantslib
:dbg("Gave it a random facedir: "..fdir
)
339 if type(spawn_plants
) == "string" then
340 plantslib
:dbg("Call function: "..spawn_plants
.."("..dump_pos(pos
)..")")
341 assert(loadstring(spawn_plants
.."("..dump_pos(pos
)..")"))()
342 elseif not biome
.spawn_on_side
and not biome
.spawn_on_bottom
and not biome
.spawn_replace_node
then
343 if n_top
.name
== "air" then
344 plantslib
:dbg("Spawn: "..plant_to_spawn
.." on top of ("..dump(pos
).."); facedir="..fdir
)
345 minetest
.add_node(p_top
, { name
= plant_to_spawn
, param2
= fdir
})
347 elseif biome
.spawn_replace_node
then
350 plantslib
:dbg("Spawn: "..plant_to_spawn
.." to replace "..minetest
.get_node(pos
).name
.." at ("..dump(pos
)..")")
351 minetest
.add_node(pos
, { name
= plant_to_spawn
, param2
= fdir
})
354 elseif biome
.spawn_on_side
then
355 local onside
= plantslib
:find_open_side(pos
)
357 plantslib
:dbg("Spawn: "..plant_to_spawn
.." at side of ("..dump(pos
).."), facedir "..onside
.facedir
.."")
358 minetest
.add_node(onside
.newpos
, { name
= plant_to_spawn
, param2
= onside
.facedir
})
360 elseif biome
.spawn_on_bottom
then
361 if minetest
.get_node({x
=pos
.x
, y
=pos
.y
-1, z
=pos
.z
}).name
== "air" then
362 plantslib
:dbg("Spawn: "..plant_to_spawn
.." on bottom of ("..dump(pos
)..")")
363 minetest
.add_node({x
=pos
.x
, y
=pos
.y
-1, z
=pos
.z
}, { name
= plant_to_spawn
, param2
= fdir
} )
378 function plantslib
:grow_plants(opts
)
384 options
.height_limit
= options
.height_limit
or 5
385 options
.ground_nodes
= options
.ground_nodes
or { "hades_core:dirt_with_grass" }
386 options
.grow_nodes
= options
.grow_nodes
or { "hades_core:dirt_with_grass" }
387 options
.seed_diff
= options
.seed_diff
or 0
390 plantslib
:dbg("Registered growing ABM:")
391 plantslib
:dbg(dump(options
))
394 if options
.grow_delay
*time_scale
>= 1 then
395 options
.interval
= options
.grow_delay
*time_scale
401 minetest
.register_abm({
402 label
= "Plantlib: Plant growth/death",
403 nodenames
= { options
.grow_plant
},
404 interval
= options
.interval
,
405 chance
= options
.grow_chance
,
406 action
= function(pos
, node
, active_object_count
, active_object_count_wider
)
407 local p_top
= {x
=pos
.x
, y
=pos
.y
+1, z
=pos
.z
}
408 local p_bot
= {x
=pos
.x
, y
=pos
.y
-1, z
=pos
.z
}
409 local n_top
= minetest
.get_node(p_top
)
410 local n_bot
= minetest
.get_node(p_bot
)
411 local root_node
= minetest
.get_node({x
=pos
.x
, y
=pos
.y
-options
.height_limit
, z
=pos
.z
})
413 if options
.need_wall
and options
.verticals_list
then
414 walldir
= plantslib
:find_adjacent_wall(p_top
, options
.verticals_list
)
416 if n_top
.name
== "air" and (not options
.need_wall
or (options
.need_wall
and walldir
))
418 if options
.grow_vertically
and walldir
then
419 if plantslib
:search_downward(pos
, options
.height_limit
, options
.ground_nodes
) then
420 plantslib
:dbg("Grow "..options
.grow_plant
.." vertically to "..dump(p_top
))
421 minetest
.add_node(p_top
, { name
= options
.grow_plant
, param2
= walldir
})
425 elseif not options
.grow_result
and not options
.grow_function
then
426 plantslib
:dbg("Die: "..options
.grow_plant
.." at ("..dump(pos
)..")")
427 minetest
.remove_node(pos
)
431 plantslib
:replace_object(pos
, options
.grow_result
, options
.grow_function
, options
.facedir
, options
.seed_diff
)
439 -- Function to decide how to replace a plant - either grow it, replace it with
440 -- a tree, run a function, or die with an error.
443 function plantslib
:replace_object(pos
, replacement
, grow_function
, walldir
, seeddiff
)
444 local growtype
= type(grow_function
)
445 plantslib
:dbg("replace_object called, growtype="..dump(grow_function
))
446 if growtype
== "table" then
447 plantslib
:dbg("Grow: spawn tree at "..dump(pos
))
448 minetest
.remove_node(pos
)
449 plantslib
:grow_tree(pos
, grow_function
)
451 elseif growtype
== "string" then
452 local perlin1
= minetest
.get_perlin(seeddiff
, perlin_octaves
, perlin_persistence
, perlin_scale
)
453 local noise1
= perlin1
:get_2d({x
=pos
.x
, y
=pos
.z
})
454 local noise2
= plantslib
.perlin_temperature
:get_2d({x
=pos
.x
, y
=pos
.z
})
455 plantslib
:dbg("Grow: call function "..grow_function
.."("..dump_pos(pos
)..","..dump(walldir
)..")")
456 assert(loadstring(grow_function
.."("..dump_pos(pos
)..","..dump(walldir
)..")"))()
458 elseif growtype
== "nil" then
459 plantslib
:dbg("Grow: place "..replacement
.." at ("..dump(pos
)..") on wall "..dump(walldir
))
460 minetest
.add_node(pos
, { name
= replacement
, param2
= walldir
})
462 elseif growtype
~= "nil" and growtype
~= "string" and growtype
~= "table" then
463 error("Invalid grow function "..dump(grow_function
).." used on object at ("..dump(pos
)..")")
468 -- function to decide if a node has a wall that's in verticals_list{}
469 -- returns wall direction of valid node, or nil if invalid.
472 function plantslib
:find_adjacent_wall(pos
, verticals
)
473 local verts
= dump(verticals
)
474 if string.find(verts
, minetest
.get_node({ x
=pos
.x
-1, y
=pos
.y
, z
=pos
.z
}).name
) then return 3 end
475 if string.find(verts
, minetest
.get_node({ x
=pos
.x
+1, y
=pos
.y
, z
=pos
.z
}).name
) then return 2 end
476 if string.find(verts
, minetest
.get_node({ x
=pos
.x
, y
=pos
.y
, z
=pos
.z
-1 }).name
) then return 5 end
477 if string.find(verts
, minetest
.get_node({ x
=pos
.x
, y
=pos
.y
, z
=pos
.z
+1 }).name
) then return 4 end
482 -- Function to search downward from the given position, looking for the first
483 -- node that matches the ground table. Returns the new position, or nil if
484 -- height limit is exceeded before finding it.
487 function plantslib
:search_downward(pos
, heightlimit
, ground
)
488 for i
= 0, heightlimit
do
489 if string.find(dump(ground
), minetest
.get_node({x
=pos
.x
, y
=pos
.y
-i
, z
= pos
.z
}).name
) then
490 return {x
=pos
.x
, y
=pos
.y
-i
, z
= pos
.z
}
497 function plantslib
:find_open_side(pos
)
498 if minetest
.get_node({ x
=pos
.x
-1, y
=pos
.y
, z
=pos
.z
}).name
== "air" then
499 return {newpos
= { x
=pos
.x
-1, y
=pos
.y
, z
=pos
.z
}, facedir
= 2}
501 if minetest
.get_node({ x
=pos
.x
+1, y
=pos
.y
, z
=pos
.z
}).name
== "air" then
502 return {newpos
= { x
=pos
.x
+1, y
=pos
.y
, z
=pos
.z
}, facedir
= 3}
504 if minetest
.get_node({ x
=pos
.x
, y
=pos
.y
, z
=pos
.z
-1 }).name
== "air" then
505 return {newpos
= { x
=pos
.x
, y
=pos
.y
, z
=pos
.z
-1 }, facedir
= 4}
507 if minetest
.get_node({ x
=pos
.x
, y
=pos
.y
, z
=pos
.z
+1 }).name
== "air" then
508 return {newpos
= { x
=pos
.x
, y
=pos
.y
, z
=pos
.z
+1 }, facedir
= 5}
514 -- spawn_tree() on generate is routed through here so that other mods can hook
518 function plantslib
:generate_tree(pos
, node_or_function_or_model
)
520 minetest
.spawn_tree(pos
, node_or_function_or_model
)
521 plantslib
:dbg("Generated one tree in ".. (os
.clock()-t
)*1000 .."ms")
525 -- and this one's for the call used in the growing code
528 function plantslib
:grow_tree(pos
, node_or_function_or_model
)
530 minetest
.spawn_tree(pos
, node_or_function_or_model
)
531 plantslib
:dbg("Generated one tree in ".. (os
.clock()-t
)*1000 .."ms")
535 -- check if a node is owned before allowing manual placement of a node
536 -- (used by flowers_plus)
539 function plantslib
:node_is_owned(pos
, placer
)
540 local ownername
= false
541 if type(IsPlayerNodeOwner
) == "function" then -- node_ownership mod
542 if HasOwner(pos
, placer
) then -- returns true if the node is owned
543 if not IsPlayerNodeOwner(pos
, placer
:get_player_name()) then
544 if type(getLastOwner
) == "function" then -- ...is an old version
545 ownername
= getLastOwner(pos
)
546 elseif type(GetNodeOwnerName
) == "function" then -- ...is a recent version
547 ownername
= GetNodeOwnerName(pos
)
549 ownername
= S("someone")
555 elseif type(isprotect
)=="function" then -- glomie's protection mod
556 if not isprotect(5, pos
, placer
) then
557 ownername
= S("someone")
559 elseif type(protector
)=="table" and type(protector
.can_dig
)=="function" then -- Zeg9's protection mod
560 if not protector
.can_dig(5, pos
, placer
) then
561 ownername
= S("someone")
566 if ownername
~= false then
567 minetest
.chat_send_player( placer
:get_player_name(), S("Sorry, @1 owns that spot.", ownername
) )
575 -- Check for infinite stacks
578 if minetest
.get_modpath("unified_inventory") or not minetest
.settings
:get_bool("creative_mode") then
579 plantslib
.expect_infinite_stacks
= false
581 plantslib
.expect_infinite_stacks
= true
585 -- read a field from a node's definition
588 function plantslib
:get_nodedef_field(nodename
, fieldname
)
589 if not minetest
.registered_nodes
[nodename
] then
592 return minetest
.registered_nodes
[nodename
][fieldname
]
598 minetest
.log("action", "[Plantlife Library] Loaded")