Trim down plants_lib to one function

master
Wuzzy 2021-09-27 21:02:39 +02:00
parent 9d309c97d1
commit ed78c12ccd
9 changed files with 125 additions and 1390 deletions

View File

@ -361,9 +361,9 @@ minetest.register_abm({
end,
})
--spawning
-- Spawn vines
plantslib:spawn_on_surfaces({
label = "Spawn cave vines",
avoid_nodes = {"hades_vines:cave"},
avoid_radius = 5,
spawn_delay = spawn_interval,
@ -371,10 +371,10 @@ plantslib:spawn_on_surfaces({
spawn_chance = 50,
spawn_surfaces = {"hades_core:dirt_with_grass","hades_core:dirt_with_grass_l1","hades_core:dirt_with_grass_l2","hades_core:dirt_with_grass_l3","hades_core:dirt"},
spawn_on_bottom = true,
plantlife_limit = -0.9,
})
plantslib:spawn_on_surfaces({
label = "Spawn tropical vines",
avoid_nodes = {"hades_vines:cave", "hades_vines:jungle"},
avoid_radius = 3,
spawn_delay = spawn_interval,
@ -386,10 +386,10 @@ plantslib:spawn_on_surfaces({
near_nodes_size = 10,
near_nodes_vertical = 5,
near_nodes_count = 3,
plantlife_limit = -0.9,
})
plantslib:spawn_on_surfaces({
label = "Spawn willow vines",
spawn_plants = {"hades_vines:willow"},
avoid_radius = 3,
spawn_delay = spawn_interval,
@ -400,7 +400,6 @@ plantslib:spawn_on_surfaces({
near_nodes_size = 7,
near_nodes_vertical = 4,
near_nodes_count = 3,
plantlife_limit = -0.9,
})
-- Shears: jojoa1997's shears

View File

@ -9,14 +9,8 @@ hades_waterplants = {}
local SPAWN_DELAY = 1000
local SPAWN_CHANCE = 200
local waterplants_seed_diff = 329
local lilies_max_count = 12
local lilies_rarity = 33
local seaweed_max_count = 20
local seaweed_rarity = 33
-- globals
local lilypads_max_count = {}
local lilypads_rarity = {}
local WATERPLANTS_SEED_DIFF = 329
local WATERPLANTS_SEED_DIFF2 = 459
-- register the various rotations of waterlilies
@ -31,6 +25,17 @@ local lilies_list = {
{ "s4" , "small_4" , 8, false },
}
local node_is_owned = function(pos, placer)
local name = placer:get_player_name()
local is_protected = minetest.is_protected(pos, name) and not minetest.check_player_privs(name, "protection_bypass")
if is_protected then
minetest.record_protection_violation(pos, name)
return true
else
return false
end
end
local generate_on_place = function(basename, plant_table)
return function(itemstack, placer, pt)
local place_pos = nil
@ -69,7 +74,7 @@ local generate_on_place = function(basename, plant_table)
return itemstack
end
if not plantslib:node_is_owned(place_pos, placer) then
if not node_is_owned(place_pos, placer) then
local nodename = basename
@ -238,69 +243,8 @@ for i in ipairs(seaweed_list) do
end
-- ongen registrations
hades_waterplants.grow_waterlily = function(pos)
local right_here = {x=pos.x, y=pos.y+1, z=pos.z}
for i in ipairs(lilies_list) do
local chance = math.random(1,8)
local ext = ""
local num = lilies_list[i][3]
if lilies_list[i][1] ~= nil then
ext = "_"..lilies_list[i][1]
end
if chance == num then
minetest.add_node(right_here, {name="hades_waterplants:waterlily"..ext, param2=math.random(0,3)})
end
end
end
plantslib:register_generate_plant({
surface = {"hades_core:water_source"},
max_count = lilypads_max_count,
rarity = lilypads_rarity,
min_elevation = -30,
max_elevation = 100,
near_nodes = {"hades_core:dirt_with_grass"},
near_nodes_size = 4,
near_nodes_vertical = 1,
near_nodes_count = 1,
plantlife_limit = -0.9,
temp_max = -0.22,
temp_min = 0.22,
},
"hades_waterplants.grow_waterlily"
)
hades_waterplants.grow_seaweed = function(pos)
local right_here = {x=pos.x, y=pos.y+1, z=pos.z}
minetest.add_node(right_here, {name="hades_waterplants:seaweed_"..math.random(1,4), param2=math.random(1,3)})
end
plantslib:register_generate_plant({
surface = {"hades_core:water_source"},
max_count = seaweed_max_count,
rarity = seaweed_rarity,
-- min_elevation = 1,
max_elevation = 100,
near_nodes = {"hades_core:mossystone", "hades_core:dirt_with_grass"},
near_nodes_size = 3,
near_nodes_vertical = 2,
near_nodes_count = 1,
plantlife_limit = -1.0,
},
"hades_waterplants.grow_seaweed"
)
plantslib:spawn_on_surfaces({
label = "Spawn waterlilies",
spawn_delay = SPAWN_DELAY/2,
spawn_plants = {
"hades_waterplants:waterlily",
@ -316,34 +260,34 @@ plantslib:spawn_on_surfaces({
spawn_chance = SPAWN_CHANCE*4,
spawn_surfaces = {"hades_core:water_source"},
avoid_nodes = {"group:flower", "group:flora" },
seed_diff = waterplants_seed_diff,
seed_diff = WATERPLANTS_SEED_DIFF,
light_min = 9,
depth_max = 2,
random_facedir = {0,3}
})
plantslib:spawn_on_surfaces({
label = "Spawn seaweed on water",
spawn_delay = SPAWN_DELAY*2,
spawn_plants = {"hades_waterplants:seaweed"},
spawn_chance = SPAWN_CHANCE*2,
spawn_surfaces = {"hades_core:water_source"},
avoid_nodes = {"group:flower", "group:flora"},
seed_diff = waterplants_seed_diff,
seed_diff = WATERPLANTS_SEED_DIFF2,
light_min = 4,
light_max = 10,
neighbors = {"hades_core:dirt_with_grass"},
facedir = 1
})
plantslib:spawn_on_surfaces({
label = "Spawn seaweed on dirt with grass",
spawn_delay = SPAWN_DELAY*2,
spawn_plants = {"hades_waterplants:seaweed"},
spawn_chance = SPAWN_CHANCE*2,
spawn_surfaces = {"hades_core:dirt_with_grass"},
avoid_nodes = {"group:flower", "group:flora" },
seed_diff = waterplants_seed_diff,
seed_diff = WATERPLANTS_SEED_DIFF2,
light_min = 4,
light_max = 10,
neighbors = {"hades_core:water_source"},

View File

@ -1,62 +1,19 @@
This document describes the Plantlife API.
Last revision: 2013-01-30
This document describes the Hades Plantlife API.
=========
Functions
=========
There are three main functions defined by the main "plants_lib" mod:
There is one main function defined:
spawn_on_surfaces()
register_generate_plant()
grow_plants()
There are also several internal, helper functions that can be called if so
desired, but they are not really intended for use by other mods and may change
at any time. They are briefly described below these main functions, but see
init.lua for details.
All functions in plants lib are declared locally to avoid namespace collisions
with other mods. They are accessible via the "plantslib" method, e.g.
plantslib:spawn_on_surfaces() and so forth.
=====
spawn_on_surfaces(biome)
spawn_on_surfaces(sdelay, splant, sradius, schance, ssurface, savoid)
This first function is an ABM-based spawner function originally created as
part of Ironzorg's flowers mod. It has since been largely extended and
expanded. There are two ways to call this function: You can either pass it
several individual string and number parameters to use the legacy interface,
or you can pass a single biome definition as a table, with all of your options
spelled out nicely. This is the preferred method.
This first function is an ABM-based spawner function.
When used with the legacy interface, you must specify the parameters exactly
in order, with the first five being mandatory (even if some are set to nil),
and the last one being optional:
sdelay: The value passed to the ABM's interval parameter, in seconds.
splant: The node name of the item to spawn (e.g.
"flowers:flower_rose"). A plant will of course only be
spawned if the node about to be replaced is air.
sradius: Don't spawn within this many nodes of the avoid items
mentioned below. If set to nil, this check is skipped.
schance: The value passed to the ABM's chance parameter, normally in
the 10-100 range (1-in-X chance of operating on a given node)
ssurface: String with the name of the node on which to spawn the plant
in question, such as "hades_core:ash" or
"hades_core:dirt_with_grass". It is not recommended to put air,
stone, or plain dirt here if you can use some other node, as
doing so will cause the engine to process potentially large
numbers of such nodes when deciding when to execute the ABM
and where it should operate.
savoid: Table with a list of groups and/or node names to avoid when
spawning the plant, such as {"group:flowers", "hades_trees:tree"}.
When passed a table as the argument, and thus using the modern calling method,
you must pass a number of arguments in the form of an ordinary keyed-value
You must pass a number of arguments in the form of an ordinary keyed-value
table. Below is a list of everything supported by this function:
biome = {
@ -67,6 +24,7 @@ biome = {
-- should be spawned. As with the single-
-- node "ssurface" option in the legacy API,
-- you should not put stone, air, etc. here.
label = string -- label for the ABM (strongly recommended)
---- From here down are a number of optional parameters. You will
---- most likely want to use at least some of these to limit how and
@ -235,368 +193,3 @@ skipped.
By default, if a biome node, size, and count are not defined, the biome
checking is disabled. Same holds true for the nneighbors bit above that.
=====
plantslib:register_generate_plant(biome, node_or_function_or_treedef)
To register an object to be spawned at mapgen time rather than via an ABM,
call this function with two parameters: a table with your object's biome
information, and a string or table describing what to do if the engine finds a
suitable node (see below).
The biome table contains quite a number of options, though there are fewer
here than are available in the ABM-based spawner, as some stuff doesn't make
sense at map-generation time.
biome = {
surface = something, -- What node(s). May be a string such as
-- "hades_core:dirt_with_grass" or a table with
-- multiple such entries.
---- Everything else is optional, but you'll definitely want to use
---- some of these other fields to limit where and under what
---- conditions the objects are spawned.
below_nodes = {table}, -- List of nodes that must be below the target
-- node. Useful in snow biomes to keep
-- objects from spawning in snow that's on the
-- wrong surface for that object.
avoid_nodes = {table}, -- List of nodes or groups to avoid when
-- spawning.
avoid_radius = num, -- how much distance to leave between the
-- object to be added and the objects to be
-- avoided. If this or the avoid_nodes value
-- is nil or omitted, this check is skipped.
-- Avoid using excessively large radii or you
-- will slow down the map generator.
rarity = num, -- how rare should this object be in its
-- biome? Larger values make objects more
-- rare, via: math.random(1,100) > this
max_count = num, -- The absolute maximum number of your object
-- that should be allowed to spawn in a 5x5x5
-- mapblock area (80x80x80 nodes). Defaults
-- to 5, but be sure you set this to some
-- reasonable value depending on your object
-- and its size if 5 is insufficient.
seed_diff = num, -- perlin seed-diff value. Defaults to 0,
-- which causes the function to inherit the
-- global value of 329.
neighbors = {table}, -- What ground nodes must be right next to and
-- at the same elevation as the node to be
-- spawned on.
ncount = num, -- at least this many of the above nodes must
-- be next to the node to spawn on. Any value
-- greater than 8 will probably cause the code
-- to never spawn anything. Defaults to 0.
depth = num, -- how deep/thick of a layer the spawned-on
-- node must be. Typically used for water.
min_elevation = num, -- minimum elevation in meters/nodes.
-- Defaults to -31000 (unlimited).
max_elevation = num, -- maximum elevation. Defaults to +31000
-- (unlimited).
near_nodes = {table}, -- what nodes must be in the general vicinity
-- of the object being spawned.
near_nodes_size = num, -- how wide of a search area to look for
-- the nodes in that list.
near_nodes_vertical = num, -- How high/low of an area to search from
-- the target node.
near_nodes_count = num, -- at least this many of those nodes must be
-- in the area.
plantlife_limit = num, -- The value compared against the generic
-- "plants can grow here" Perlin noise layer.
-- Smaller numbers result in more abundant
-- plants. Range of -1 to +1, with values in
-- the range of about 0 to 0.5 being most
-- useful. Defaults to 0.1.
temp_min = num, -- coldest allowable temperature for a plant
-- to spawn (that is, the highest Perlin
-- temperature map value).
temp_max = num, -- warmest allowable temperature to spawn a
-- plant (lowest Perlin temperature value).
verticals_list = {table}, -- Same as with the spawn_on_surfaces
-- function.
check_air = bool, -- Flag to tell the mapgen code to check for
-- air above the spawn target. Defaults to
-- true if not explicitly set to false.
delete_above = bool, -- Flag to tell the mapgen code to delete the
-- two nodes directly above the spawn target
-- just before adding the plant or tree.
-- Useful when generating in snow biomes.
-- Defaults to false.
delete_above_surround = bool, -- Flag to tell the mapgen code to also
-- delete the four nodes surrounding the above
-- space, and the four nodes above those,
-- resulting in a two-node-deep cross-shaped
-- empty region above the spawn target.
-- Useful when adding trees to snow biomes.
-- Defaults to false.
spawn_replace_node = bool -- same as with the ABM spawner.
}
Regarding node_or_function_or_treedef, this must either be table with an
L-Systems tree definition, or a string with a node or function name.
If you specified a string, the code will attempt to determine, as needed,
whether that string specifies a node name. If it does, that node will be
placed on top of the target position directly.
If it wasn't a node, the code will assume you meant to specify a function
name, in which case that function will be passed a single position parameter
(in the usual table format), indicating where the named function should place
the object. It is called in the form of "somefunction(pos)", and behaves much
like a typical callback.
If you specified a table, the code assumes this table contains an L-Systems
tree definition, then that definition will be passed directly to the
spawn_tree() function along with the position to spawn the tree on.
=====
plantslib:grow_plants(options)
The third function, grow_plants() is used to turn the spawned nodes above
into something else over time. This function has no return value, and accepts
a biome definition table as the only parameter. These are defined like so:
options = {
grow_plant = "string", -- Name of the node to be grown into something
-- else. This value is passed to the ABM as
-- the "nodenames" parameter, so it is the
-- plants themselves that are the ABM trigger,
-- rather than the ground they spawned on. A
-- plant will only grow if the node above it
-- is air. Can also be a table, but note that
-- all nodes referenced therein will be grown
-- into the same object.
grow_delay = num, -- Passed as the ABM "interval" parameter, as
-- with spawning.
grow_chance = num, -- Passed as the ABM "chance" parameter.
grow_result = "string", -- Name of the node into which the grow_plant
-- node(s) should transform when the ABM
-- executes.
---- Everything from here down is optional.
dry_early_node = "string", -- This value is ignored except for jungle
-- grass (a corner case needed by that mod),
-- where it indicates which node the grass
-- must be on in order for it to turn from
-- the short size to this node
-- instead of the medium size.
grow_nodes = {table}, -- One of these nodes must be under the plant
-- in order for it to grow at all. Normally
-- this should be the same as the list of
-- surfaces passed to the spawning ABM as the
-- "nodenames" parameter. This is so that the
-- plant can be manually placed on something
-- like a flower pot or something without it
-- growing and eventually dieing. Defaults to
-- "hades_core:dirt_with_grass".
facedir = num, -- Same as with spawning a plant.
need_wall = bool, -- Set this to true if you the plant needs to
-- grow against a wall. Defaults to false.
verticals_list = {table}, -- List of nodes that should be considered
-- to be wall surfaces when growing the plant
-- vertically. If not provided, the walls
-- check is skipped.
grow_vertically = bool, -- Set this to true if the plant needs to grow
-- vertically, as in climbing poison ivy.
-- Defaults to false.
height_limit = num, -- Set this to limit how tall the desired node
-- can grow. The mod will search straight
-- down from the position being spawned at to
-- find a ground node, set via the parameter
-- below. Defaults to 5 nodes.
ground_nodes = {table}, -- What nodes should be treated as "the
-- ground" below a vertically-growing plant.
-- Usually this should be the same as the
-- grow_nodes table, but might also include,
-- for example, water or some other
-- surrounding material. Defaults to
-- "hades_core:dirt_with_grass".
grow_function = something, -- [*] see below.
seed_diff = num, -- [*] see below.
}
[*] grow_function can take one of three possible settings: it can be nil (or
not provided), a string, or a table.
If it is not provided or it's set to nil, all of the regular growing code is
executed normally, the value of seed_diff, if any, is ignored, and the node to
be placed is assumed to be specified in the grow_result variable.
If this value is set to a simple string, this is treated as the name of the
function to use to grow the plant. In this case, all of the usual growing
code is executeed, but then instead of a plant being simply added to the
world, grow_result is ignored and the named function is executed and passed a
few parmeters in the following general form:
somefunction(pos, perlin1, perlin2)
These values represent the current position (the usual table), the Perlin
noise value for that spot in the generic "plants can grow here" map for the
seed_diff value above, the Perlin value for that same spot from the
temperature map, and the detected neighboring wall face, if there was one (or
nil if not). If seed_diff is not provided, it defaults to 0.
If this variable is instead set to a table, it is treated an an L-Systems tree
definition. All of the growing code is executed in the usual manner, then the
tree described by that definition is spawned at the current position instead,
and grow_result is ignored.
=====
find_adjacent_wall(pos, verticals)
Of the few helper functions, this one expects a position parameter and a table
with the list of nodes that should be considered as walls. The code will
search around the given position for a neighboring wall, returning the first
one it finds as a facedir value, or nil if there are no adjacent walls.
=====
is_node_loaded(pos)
This acts as a wrapper for the minetest.get_node_or_nil(node_pos)
function and accepts a single position parameter. Returns true if the node in
question is already loaded, or false if not.
=====
dbg(string)
This is a simple debug output function which takes one string parameter. It
just checks if DEBUG is true and outputs the phrase "[Plantlife] " followed by
the supplied string, via the print() function, if so.
=====
plantslib:generate_tree(pos, treemodel)
plantslib:grow_tree(pos, treemodel)
In the case of the growing code and the mapgen-based tree generator code,
generating a tree is done via the above two calls, which in turn immediately
call the usual spawn_tree() functions. This rerouting exists as a way for
other mods to hook into plants_lib's tree-growing functions in general,
perhaps to execute something extra whenever a tree is spawned.
plantslib:generate_tree(pos, treemodel) is called any time a
tree is spawned at map generation time. 'pos' is the position of the block on
which the tree is to be placed. 'treemodel' is the standard L-Systems tree
definition table expected by the spawn_tree() function. Refer to the 'trunk'
field in that table to derive the name of the tree being spawned.
plantslib:grow_tree(pos, treemodel) does the same sort of thing whenever a
tree is spawned within the abm-based growing code, for example when growing a
sapling into a tree.
=====
There are other, internal helper functions that are not meant for use by other
mods. Don't rely on them, as they are subject to change without notice.
===============
Global Settings
===============
Set this to true if you want the mod to spam your console with debug info :-)
plantlife_debug = false
======================
Fertile Ground Mapping
======================
The mod uses Perlin noise to create "biomes" of the various plants, via the
minetest.get_perlin() function. At present, there are three layers of
Perlin noise used.
The first one is for a "fertile ground" layer, which I tend to refer to as the
generic "stuff can potentially grow here" layer. Its values are hard-coded:
plantslib.plantlife_seed_diff = 329
perlin_octaves = 3
perlin_persistence = 0.6
perlin_scale = 100
For more information on how Perlin noise is generated, you will need to search
the web, as these default values were from that which is used by minetest_game
to spawn jungle grass at mapgen time, and I'm still learning how Perlin noise
works. ;-)
===================
Temperature Mapping
===================
The second Perlin layer is a temperature map, with values taken from
SPlizard's Snow Biomes mod so that the two will be compatible, since that mod
appears to be the standard now. Those values are:
temperature_seeddiff = 112
temperature_octaves = 3
temperature_persistence = 0.5
temperature_scale = 150
The way Perlin values are used by this mod, in keeping with the snow mod's
apparent methods, larger values returned by the Perlin function represent
*colder* temperatures. In this mod, the following table gives a rough
approximation of how temperature maps to these values, normalized to
0.53 = 0 °C and +1.0 = -25 °C.
Perlin Approx. Temperature
-1.0 81 °C ( 178 °F)
-0.75 68 °C ( 155 °F)
-0.56 58 °C ( 136 °F)
-0.5 55 °C ( 131 °F)
-0.25 41 °C ( 107 °F)
-0.18 38 °C ( 100 °F)
0 28 °C ( 83 °F)
0.13 21 °C ( 70 °F)
0.25 15 °C ( 59 °F)
0.5 2 °C ( 35 °F)
0.53 0 °C ( 32 °F)
0.75 -12 °C ( 11 °F)
0.86 -18 °C ( 0 °F)
1.0 -25 °C (- 13 °F)
Included in this table are even 0.25 steps in Perlin values along with some
common temperatures on both the Centigrade and Fahrenheit scales. Note that
unless you're trying to model the Moon or perhaps Mercury in your mods/maps,
you probably won't need to bother with Perlin values of less than -0.56 or so.
================
Humidity Mapping
================
Last but not least is a moisture/humidity map. Like the temperature map
above, Perlin values can be tested to determine the approximate humidity of
the *air* in the area. This humidity map is basically the perlin layer used
for deserts.
A value of -1.0 is very moist (basically a thick fog, if it could be seen), a
value of +0.4 represents the edge of a desert as usually seen in the game, and
a value of +1.0 is as dry as a bone.
This does not check for nearby water, just general air humidity, and that
being the case, nearby ground does not affect the reported humidity of a
region (because this isn't yet possible to calculate yet). Use the near_nodes
and avoid_nodes parameters and their related options to check for water and
such.
The Perlin values use for this layer are:
humidity_seeddiff = 9130
humidity_octaves = 3
humidity_persistence = 0.5
humidity_scale = 250
And this particular one is mapped slightly differently from the others:
noise3 = perlin3:get2d({x=p_top.x+150, y=p_top.z+50})
(Note the +150 and +50 offsets)

View File

@ -1,317 +0,0 @@
For the code, Ironzorg's textures, and everything else: MIT License
For all of my own textures: CC BY-SA 3.0
-----------
CC BY-SA 3.0 license text
-----------
THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS
CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS
PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE
WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW
IS PROHIBITED.
BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND
AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS
LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS
YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE
OF SUCH TERMS AND CONDITIONS.
1. Definitions
"Collective Work" means a work, such as a periodical issue,
anthology or encyclopedia, in which the Work in its entirety in
unmodified form, along with one or more other contributions,
constituting separate and independent works in themselves, are
assembled into a collective whole. A work that constitutes a
Collective Work will not be considered a Derivative Work (as
defined below) for the purposes of this License.
"Creative Commons Compatible License" means a license that is
listed at http://creativecommons.org/compatiblelicenses that has
been approved by Creative Commons as being essentially equivalent
to this License, including, at a minimum, because that license:
(i) contains terms that have the same purpose, meaning and effect
as the License Elements of this License; and, (ii) explicitly
permits the relicensing of derivatives of works made available
under that license under this License or either a Creative Commons
unported license or a Creative Commons jurisdiction license with
the same License Elements as this License.
"Derivative Work" means a work based upon the Work or upon the
Work and other pre-existing works, such as a translation, musical
arrangement, dramatization, fictionalization, motion picture
version, sound recording, art reproduction, abridgment,
condensation, or any other form in which the Work may be recast,
transformed, or adapted, except that a work that constitutes a
Collective Work will not be considered a Derivative Work for the
purpose of this License. For the avoidance of doubt, where the
Work is a musical composition or sound recording, the
synchronization of the Work in timed-relation with a moving image
("synching") will be considered a Derivative Work for the purpose
of this License.
"License Elements" means the following high-level license
attributes as selected by Licensor and indicated in the title of
this License: Attribution, ShareAlike.
"Licensor" means the individual, individuals, entity or
entities that offers the Work under the terms of this License.
"Original Author" means the individual, individuals, entity or
entities who created the Work.
"Work" means the copyrightable work of authorship offered
under the terms of this License.
"You" means an individual or entity exercising rights under
this License who has not previously violated the terms of this
License with respect to the Work, or who has received express
permission from the Licensor to exercise rights under this License
despite a previous violation.
2. Fair Use Rights. Nothing in this license is intended to reduce,
limit, or restrict any rights arising from fair use, first sale or
other limitations on the exclusive rights of the copyright owner
under copyright law or other applicable laws.
3. License Grant. Subject to the terms and conditions of this
License, Licensor hereby grants You a worldwide, royalty-free,
non-exclusive, perpetual (for the duration of the applicable
copyright) license to exercise the rights in the Work as stated
below:
to reproduce the Work, to incorporate the Work into one or
more Collective Works, and to reproduce the Work as incorporated
in the Collective Works;
to create and reproduce Derivative Works provided that any
such Derivative Work, including any translation in any medium,
takes reasonable steps to clearly label, demarcate or otherwise
identify that changes were made to the original Work. For example,
a translation could be marked "The original work was translated
from English to Spanish," or a modification could indicate "The
original work has been modified.";
to distribute copies or phonorecords of, display publicly,
perform publicly, and perform publicly by means of a digital audio
transmission the Work including as incorporated in Collective
Works;
to distribute copies or phonorecords of, display publicly,
perform publicly, and perform publicly by means of a digital audio
transmission Derivative Works.
For the avoidance of doubt, where the Work is a musical
composition:
Performance Royalties Under Blanket Licenses. Licensor
waives the exclusive right to collect, whether individually or, in
the event that Licensor is a member of a performance rights
society (e.g. ASCAP, BMI, SESAC), via that society, royalties for
the public performance or public digital performance (e.g.
webcast) of the Work.
Mechanical Rights and Statutory Royalties. Licensor waives
the exclusive right to collect, whether individually or via a
music rights agency or designated agent (e.g. Harry Fox Agency),
royalties for any phonorecord You create from the Work ("cover
version") and distribute, subject to the compulsory license
created by 17 USC Section 115 of the US Copyright Act (or the
equivalent in other jurisdictions).
Webcasting Rights and Statutory Royalties. For the avoidance
of doubt, where the Work is a sound recording, Licensor waives the
exclusive right to collect, whether individually or via a
performance-rights society (e.g. SoundExchange), royalties for the
public digital performance (e.g. webcast) of the Work, subject to
the compulsory license created by 17 USC Section 114 of the US
Copyright Act (or the equivalent in other jurisdictions).
The above rights may be exercised in all media and formats whether
now known or hereafter devised. The above rights include the right
to make such modifications as are technically necessary to
exercise the rights in other media and formats. All rights not
expressly granted by Licensor are hereby reserved.
4. Restrictions. The license granted in Section 3 above is
expressly made subject to and limited by the following
restrictions:
You may distribute, publicly display, publicly perform, or
publicly digitally perform the Work only under the terms of this
License, and You must include a copy of, or the Uniform Resource
Identifier for, this License with every copy or phonorecord of the
Work You distribute, publicly display, publicly perform, or
publicly digitally perform. You may not offer or impose any terms
on the Work that restrict the terms of this License or the ability
of a recipient of the Work to exercise of the rights granted to
that recipient under the terms of the License. You may not
sublicense the Work. You must keep intact all notices that refer
to this License and to the disclaimer of warranties. When You
distribute, publicly display, publicly perform, or publicly
digitally perform the Work, You may not impose any technological
measures on the Work that restrict the ability of a recipient of
the Work from You to exercise of the rights granted to that
recipient under the terms of the License. This Section 4(a)
applies to the Work as incorporated in a Collective Work, but this
does not require the Collective Work apart from the Work itself to
be made subject to the terms of this License. If You create a
Collective Work, upon notice from any Licensor You must, to the
extent practicable, remove from the Collective Work any credit as
required by Section 4(c), as requested. If You create a Derivative
Work, upon notice from any Licensor You must, to the extent
practicable, remove from the Derivative Work any credit as
required by Section 4(c), as requested.
You may distribute, publicly display, publicly perform, or
publicly digitally perform a Derivative Work only under: (i) the
terms of this License; (ii) a later version of this License with
the same License Elements as this License; (iii) either the
Creative Commons (Unported) license or a Creative Commons
jurisdiction license (either this or a later license version) that
contains the same License Elements as this License (e.g.
Attribution-ShareAlike 3.0 (Unported)); (iv) a Creative Commons
Compatible License. If you license the Derivative Work under one
of the licenses mentioned in (iv), you must comply with the terms
of that license. If you license the Derivative Work under the
terms of any of the licenses mentioned in (i), (ii) or (iii) (the
"Applicable License"), you must comply with the terms of the
Applicable License generally and with the following provisions:
(I) You must include a copy of, or the Uniform Resource Identifier
for, the Applicable License with every copy or phonorecord of each
Derivative Work You distribute, publicly display, publicly
perform, or publicly digitally perform; (II) You may not offer or
impose any terms on the Derivative Works that restrict the terms
of the Applicable License or the ability of a recipient of the
Work to exercise the rights granted to that recipient under the
terms of the Applicable License; (III) You must keep intact all
notices that refer to the Applicable License and to the disclaimer
of warranties; and, (IV) when You distribute, publicly display,
publicly perform, or publicly digitally perform the Work, You may
not impose any technological measures on the Derivative Work that
restrict the ability of a recipient of the Derivative Work from
You to exercise the rights granted to that recipient under the
terms of the Applicable License. This Section 4(b) applies to the
Derivative Work as incorporated in a Collective Work, but this
does not require the Collective Work apart from the Derivative
Work itself to be made subject to the terms of the Applicable
License.
If You distribute, publicly display, publicly perform, or
publicly digitally perform the Work (as defined in Section 1
above) or any Derivative Works (as defined in Section 1 above) or
Collective Works (as defined in Section 1 above), You must, unless
a request has been made pursuant to Section 4(a), keep intact all
copyright notices for the Work and provide, reasonable to the
medium or means You are utilizing: (i) the name of the Original
Author (or pseudonym, if applicable) if supplied, and/or (ii) if
the Original Author and/or Licensor designate another party or
parties (e.g. a sponsor institute, publishing entity, journal) for
attribution ("Attribution Parties") in Licensor's copyright
notice, terms of service or by other reasonable means, the name of
such party or parties; the title of the Work if supplied; to the
extent reasonably practicable, the Uniform Resource Identifier, if
any, that Licensor specifies to be associated with the Work,
unless such URI does not refer to the copyright notice or
licensing information for the Work; and, consistent with Section
3(b) in the case of a Derivative Work, a credit identifying the
use of the Work in the Derivative Work (e.g., "French translation
of the Work by Original Author," or "Screenplay based on original
Work by Original Author"). The credit required by this Section
4(c) may be implemented in any reasonable manner; provided,
however, that in the case of a Derivative Work or Collective Work,
at a minimum such credit will appear, if a credit for all
contributing authors of the Derivative Work or Collective Work
appears, then as part of these credits and in a manner at least as
prominent as the credits for the other contributing authors. For
the avoidance of doubt, You may only use the credit required by
this Section for the purpose of attribution in the manner set out
above and, by exercising Your rights under this License, You may
not implicitly or explicitly assert or imply any connection with,
sponsorship or endorsement by the Original Author, Licensor and/or
Attribution Parties, as appropriate, of You or Your use of the
Work, without the separate, express prior written permission of
the Original Author, Licensor and/or Attribution Parties.
5. Representations, Warranties and Disclaimer
UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING,
LICENSOR OFFERS THE WORK AS-IS AND ONLY TO THE EXTENT OF ANY
RIGHTS HELD IN THE LICENSED WORK BY THE LICENSOR. THE LICENSOR
MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE
WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT
LIMITATION, WARRANTIES OF TITLE, MARKETABILITY, MERCHANTIBILITY,
FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE
OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE
OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT
ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY
NOT APPLY TO YOU.
6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY
APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY
LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE
OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE
WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
7. Termination
This License and the rights granted hereunder will terminate
automatically upon any breach by You of the terms of this License.
Individuals or entities who have received Derivative Works or
Collective Works from You under this License, however, will not
have their licenses terminated provided such individuals or
entities remain in full compliance with those licenses. Sections
1, 2, 5, 6, 7, and 8 will survive any termination of this License.
Subject to the above terms and conditions, the license granted
here is perpetual (for the duration of the applicable copyright in
the Work). Notwithstanding the above, Licensor reserves the right
to release the Work under different license terms or to stop
distributing the Work at any time; provided, however that any such
election will not serve to withdraw this License (or any other
license that has been, or is required to be, granted under the
terms of this License), and this License will continue in full
force and effect unless terminated as stated above.
8. Miscellaneous
Each time You distribute or publicly digitally perform the
Work (as defined in Section 1 above) or a Collective Work (as
defined in Section 1 above), the Licensor offers to the recipient
a license to the Work on the same terms and conditions as the
license granted to You under this License.
Each time You distribute or publicly digitally perform a
Derivative Work, Licensor offers to the recipient a license to the
original Work on the same terms and conditions as the license
granted to You under this License.
If any provision of this License is invalid or unenforceable
under applicable law, it shall not affect the validity or
enforceability of the remainder of the terms of this License, and
without further action by the parties to this agreement, such
provision shall be reformed to the minimum extent necessary to
make such provision valid and enforceable.
No term or provision of this License shall be deemed waived
and no breach consented to unless such waiver or consent shall be
in writing and signed by the party to be charged with such waiver
or consent.
This License constitutes the entire agreement between the
parties with respect to the Work licensed here. There are no
understandings, agreements or representations with respect to the
Work not specified here. Licensor shall not be bound by any
additional provisions that may appear in any communication from
You. This License may not be modified without the mutual written
agreement of the Licensor and You.
Creative Commons Notice
Creative Commons is not a party to this License, and makes no
warranty whatsoever in connection with the Work. Creative Commons
will not be liable to You or any party on any legal theory for any
damages whatsoever, including without limitation any general,
special, incidental or consequential damages arising in connection
to this license. Notwithstanding the foregoing two (2) sentences,
if Creative Commons has expressly identified itself as the
Licensor hereunder, it shall have all rights and obligations of
Licensor.
Except for the limited purpose of indicating to the public
that the Work is licensed under the CCPL, Creative Commons does
not authorize the use by either party of the trademark "Creative
Commons" or any related trademark or logo of Creative Commons
without the prior written consent of Creative Commons. Any
permitted use will be in compliance with Creative Commons'
then-current trademark usage guidelines, as may be published on
its website or otherwise made available upon request from time to
time. For the avoidance of doubt, this trademark restriction does
not form part of this License.
Creative Commons may be contacted at
http://creativecommons.org/.

View File

@ -1,94 +1,11 @@
README file for Plantlife mod, by Vanessa Ezekowitz
README file for Hades Plantlife mod
---------------------------------------------------
Plantlife is a combined form of my Flowers, Jungle Grass, and Poison Ivy mods
and has been significantly rewritten and re-organized. This mod supplies all
three of these components and should be 100% compatible with mods that used the
old versions.
Its purpose is to add various kinds of flowers, cotton plants, water foliage,
poison ivy, and jungle grass in a few sizes. All of these are spawned as
normal nodes and can be collected and used in any recipes that depend on the
old mods.
This mod is a simple framework for plant spawning for Hades Revisited.
It is an utility mod for other mods to use in order to spawn plants like
vines and waterlilies.
Spawning of plants is sensitive to the amount of available light. Flowers,
cotton, and waterlilies only spawn when there at least a signficant amount of
light. Seaweed will grow only in dimly-lit areas. Jungle grass and poison ivy
also grow in the daytime, but require less light than flowers.
Technically, it's a trimmed-down fork of the Plantlife mod by Vanessa Ezekowitz.
Growing of jungle grass and poison ivy will only occur for plants that are on
the same surface that is necessary for them to spawn on, so they won't grow if
placed on e.g. cobble or homedecor flower pot, etc. This doesn't affect
wall-climbing poison ivy, since it uses a different growth pattern.
All plants use perlin noise to keep where they grow under control - no more
random spread of plants! In addition, the density of the plants in any region
they appear in has been fixed and brought under control.
Poison ivy is found sparsely among junglegrass, but will not grow near flowers.
------------------------------------------------------------------------------
Important details:
Configuration: Any of the three components of this mod can be disabled by
editing plants/init.lua, near the beginning of the file, and changing one or
more of these lines to false:
local enable_flowers = true
local enable_junglegrass = true
local enable_poisonivy = false
Dependencies: Just the game's default stuff.
Recommends: Nothing in particular.
Conflicts: This mod should not be installed alongside the original Flowers,
Jungle Grass, or Poison Ivy mods. If those exist, delete them, as this mod
supplies all of their functionality.
Software Requirements: This mod required Minetest 0.4.4 from January 7, 2013 or
later. It will NOT work with any prior version.
------------------------------------------------------------------------------
Crafting:
Only Flowers have crafting recipes. For a flower pot, put three
clay bricks in the crafting grid in a "v" shape like so (yields 1):
- - -
B - B
- B -
To put a flower into a pot, just put one of each into the crafting grid.
------------------------------------------------------------------------------
Notes:
Flowers has been completely rewritten, almost from scratch, using the most
recent and advanced features the game engine has to offer. It should be
significantly faster than the original Ironzorg Flowers mod. Flowers and
cotton spawn on grass only, seaweed spawns on water or grass close to the
shoreline, or on very small stone islands in water. Waterlilies of course
spawn on water.
There are four different sizes of jungle grasses, all of which yield a single
junglegrass object when gathered (so all four sizes may be used where jungle
grass is called for). The largest size uses the game's standard jungle grass
node, while the others are defined by this mod.
Junglegrass will spawn on grass, sand, desert sand and the tops of papyrus and
cactus (though rarely), and will do so anywhere in the map. Grass on the
ground will grow and eventually disappear (die), while grass in the desert will
grow and eventually turn into dry shrubs.
Poison Ivy will spawn on grass and in some cases, on vertical surfaces
including trees and jungle trees where they meet the dirt or grass. Ivy
previously spawned on the ground taller/thicker or start climbing up said
vertical surfaces and trees.
At present, the poison ivy presents little more than an annoyance - they can
only be cut down and either re-planted or thrown away. No damage is done by
harvesting them, yet. ;-)
Developers, see the API.md text file to learn how to use this mod.

View File

@ -1,4 +0,0 @@
The plantlife code was modified and is NOT the orginal code.
It wouldn't work in other games. Please load the orginal code from the
forums if you want use it for other games.
All new changes are WTPFL-licensed.

View File

@ -1,86 +1,30 @@
-- Plantlife library mod by Vanessa Ezekowitz
-- last revision, 2013-01-24
--
-- License: MIT License
--
-- I got the temperature map idea from "hmmmm", values used for it came from
-- Splizard's snow mod.
--
-- Various settings - most of these probably won't need to be changed
-- Hades Plantslib mod
plantslib = {}
-- Set to true to spam console with debugging info (needs verbose debug level)
local DEBUG = false
plantslib.modpath = minetest.get_modpath("plants_lib")
local S = minetest.get_translator("plantlife")
local DEBUG = false --... except if you want to spam the console with debugging info :-)
function plantslib:dbg(msg)
local function dbg(msg)
if DEBUG then
minetest.log("verbose", "[Plantlife] "..msg)
minetest.log("verbose", "[plants_lib] "..msg)
end
end
plantslib.plantlife_seed_diff = 329 -- needs to be global so other mods can see it
--global
local maxp = {}
local minp = {}
local spawn_plants = {}
--
local perlin_octaves = 3
local perlin_persistence = 0.6
local perlin_scale = 100
local temperature_seeddiff = 112
local temperature_octaves = 3
local temperature_persistence = 0.5
local temperature_scale = 150
local humidity_seeddiff = 9130
local humidity_octaves = 3
local humidity_persistence = 0.5
local humidity_scale = 250
local time_scale = 1
local time_speed = tonumber(minetest.settings:get("time_speed"))
if time_speed and time_speed > 0 then
time_scale = 72 / time_speed
end
plantslib:dbg("time_speed = "..dump(time_speed))
plantslib:dbg("time_scale = 72 / time_speed = "..dump(time_scale))
--PerlinNoise(seed, octaves, persistence, scale)
plantslib.perlin_temperature = PerlinNoise(temperature_seeddiff, temperature_octaves, temperature_persistence, temperature_scale)
plantslib.perlin_humidity = PerlinNoise(humidity_seeddiff, humidity_octaves, humidity_persistence, humidity_scale)
-- Local functions
local function dump_pos(pos)
return "{x="..pos.x..",y="..pos.y..",z="..pos.z.."}"
end
function plantslib:is_node_loaded(node_pos)
local function is_node_loaded (node_pos)
local n = minetest.get_node_or_nil(node_pos)
if (not n) or (n.name == "ignore") then
return false
@ -88,18 +32,7 @@ function plantslib:is_node_loaded(node_pos)
return true
end
function plantslib:clone_node(name)
node2={}
node=minetest.registered_nodes[name]
for k,v in pairs(node) do
node2[k]=v
end
return node2
end
function plantslib:set_defaults(biome)
local function set_defaults(biome)
biome.seed_diff = biome.seed_diff or 0
biome.min_elevation = biome.min_elevation or -31000
biome.max_elevation = biome.max_elevation or 31000
@ -130,346 +63,10 @@ function plantslib:set_defaults(biome)
biome.facedir = biome.facedir or 0
end
-- Spawn plants using the map generator
function plantslib:register_generate_plant(biomedef, node_or_function_or_model)
plantslib:dbg("Registered mapgen spawner:")
plantslib:dbg(dump(biomedef))
minetest.register_on_generated(plantslib:search_for_surfaces(minp, maxp, biomedef, node_or_function_or_model))
end
function plantslib:search_for_surfaces(minp, maxp, biomedef, node_or_function_or_model)
return function(minp, maxp, blockseed)
local t1=os.clock()
local biome = biomedef
plantslib:set_defaults(biome)
plantslib:dbg("Started checking generated mapblock volume...")
local searchnodes = minetest.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 }
local perlin1 = minetest.get_perlin(biome.seed_diff, perlin_octaves, perlin_persistence, perlin_scale)
local noise1 = perlin1:get_2d({x=p_top.x, y=p_top.z})
local noise2 = plantslib.perlin_temperature:get_2d({x=p_top.x, y=p_top.z})
local noise3 = plantslib.perlin_humidity:get_2d({x=p_top.x+150, y=p_top.z+50})
if (not biome.depth or minetest.get_node({ x = pos.x, y = pos.y-biome.depth-1, z = pos.z }).name ~= biome.surface)
and (not biome.check_air or (biome.check_air and minetest.get_node(p_top).name == "air"))
and pos.y >= biome.min_elevation
and pos.y <= biome.max_elevation
-- and noise1 > biome.plantlife_limit
-- and noise2 <= biome.temp_min
-- and noise2 >= biome.temp_max
-- and noise3 <= biome.humidity_min
-- and noise3 >= biome.humidity_max
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)
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)
and math.random(1,100) > biome.rarity
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) )
then
table.insert(in_biome_nodes, pos)
num_in_biome_nodes = num_in_biome_nodes + 1
end
end
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.")
if num_in_biome_nodes > 0 then
plantslib:dbg("Calculated maximum of "..math.min(biome.max_count*3, num_in_biome_nodes).." nodes to be checked in that list.")
for i = 1, math.min(biome.max_count, num_in_biome_nodes) do
local tries = 0
local spawned = false
while tries < 2 and not spawned do
local pos = in_biome_nodes[math.random(1, num_in_biome_nodes)]
if biome.spawn_replace_node then
pos.y = pos.y-1
end
local p_top = { x = pos.x, y = pos.y + 1, z = pos.z }
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
if biome.delete_above then
minetest.remove_node(p_top)
minetest.remove_node({x=p_top.x, y=p_top.y+1, z=p_top.z})
end
if biome.delete_above_surround then
minetest.remove_node({x=p_top.x-1, y=p_top.y, z=p_top.z})
minetest.remove_node({x=p_top.x+1, y=p_top.y, z=p_top.z})
minetest.remove_node({x=p_top.x, y=p_top.y, z=p_top.z-1})
minetest.remove_node({x=p_top.x, y=p_top.y, z=p_top.z+1})
minetest.remove_node({x=p_top.x-1, y=p_top.y+1, z=p_top.z})
minetest.remove_node({x=p_top.x+1, y=p_top.y+1, z=p_top.z})
minetest.remove_node({x=p_top.x, y=p_top.y+1, z=p_top.z-1})
minetest.remove_node({x=p_top.x, y=p_top.y+1, z=p_top.z+1})
end
if biome.spawn_replace_node then
minetest.remove_node(pos)
end
if type(node_or_function_or_model) == "table" then
plantslib:dbg("Spawn tree at {"..dump(pos).."}")
plantslib:generate_tree(pos, node_or_function_or_model)
elseif type(node_or_function_or_model) == "string" then
if not minetest.registered_nodes[node_or_function_or_model] then
plantslib:dbg("Call function: "..node_or_function_or_model.."("..dump_pos(pos)..")")
local t2=os.clock()
assert(loadstring(node_or_function_or_model.."("..dump_pos(pos)..")"))()
plantslib:dbg("Executed that function in ".. (os.clock()-t2)*1000 .."ms")
else
plantslib:dbg("Add node: "..node_or_function_or_model.." at ("..dump(p_top)..")")
minetest.add_node(p_top, { name = node_or_function_or_model })
end
end
spawned = true
else
tries = tries + 1
plantslib:dbg("No room to spawn object at {"..dump(pos).."} -- trying again elsewhere")
end
end
if tries == 2 then
plantslib:dbg("Unable to spawn that object. Giving up on it.")
end
end
end
plantslib:dbg("Evaluated/populated chunk in ".. (os.clock()-t1)*1000 .."ms")
end
end
-- The spawning ABM
function plantslib:spawn_on_surfaces(sd,sp,sr,sc,ss,sa)
local biome = {}
if type(sd) ~= "table" then
biome.spawn_delay = sd -- old api expects ABM interval param here.
biome.spawn_plants = {sp}
biome.avoid_radius = sr
biome.spawn_chance = sc
biome.spawn_surfaces = {ss}
biome.avoid_nodes = sa
else
biome = sd
end
if biome.spawn_delay*time_scale >= 1 then
biome.interval = biome.spawn_delay*time_scale
else
biome.interval = 1
end
plantslib:set_defaults(biome)
biome.spawn_plants_count = table.getn(biome.spawn_plants)
plantslib:dbg("Registered spawning ABM:")
plantslib:dbg(dump(biome))
plantslib:dbg("Number of trigger nodes in this ABM: "..biome.spawn_plants_count )
minetest.register_abm({
label = "Plantlife: Spawn plants",
nodenames = biome.spawn_surfaces,
interval = biome.interval,
chance = biome.spawn_chance,
neighbors = biome.neighbors,
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.get_node(p_top)
local perlin1 = minetest.get_perlin(biome.seed_diff, perlin_octaves, perlin_persistence, perlin_scale)
local noise1 = perlin1:get_2d({x=p_top.x, y=p_top.z})
local noise2 = plantslib.perlin_temperature:get_2d({x=p_top.x, y=p_top.z})
local noise3 = plantslib.perlin_humidity:get_2d({x=p_top.x+150, y=p_top.z+50})
-- if noise1 > biome.plantlife_limit
if plantslib:is_node_loaded(p_top) then
local n_light = minetest.get_node_light(p_top, nil)
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))
and n_light >= biome.light_min
and n_light <= biome.light_max
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 )
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)
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)
then
local walldir = plantslib:find_adjacent_wall(p_top, biome.verticals_list)
if biome.alt_wallnode and walldir then
if n_top.name == "air" then
plantslib:dbg("Spawn: "..biome.alt_wallnode.." on top of ("..dump(pos)..") against wall "..walldir)
minetest.add_node(p_top, { name = biome.alt_wallnode, param2 = walldir })
end
else
local currentsurface = minetest.get_node(pos).name
if currentsurface ~= "hades_core:water_source"
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 )
then
local rnd = math.random(1, biome.spawn_plants_count)
local plant_to_spawn = biome.spawn_plants[rnd]
plantslib:dbg("Chose entry number "..rnd.." of "..biome.spawn_plants_count)
local fdir = biome.facedir
if biome.random_facedir then
fdir = math.random(biome.random_facedir[1],biome.random_facedir[2])
plantslib:dbg("Gave it a random facedir: "..fdir)
end
if type(spawn_plants) == "string" then
plantslib:dbg("Call function: "..spawn_plants.."("..dump_pos(pos)..")")
assert(loadstring(spawn_plants.."("..dump_pos(pos)..")"))()
elseif not biome.spawn_on_side and not biome.spawn_on_bottom and not biome.spawn_replace_node then
if n_top.name == "air" then
plantslib:dbg("Spawn: "..plant_to_spawn.." on top of ("..dump(pos).."); facedir="..fdir)
minetest.add_node(p_top, { name = plant_to_spawn, param2 = fdir })
end
elseif biome.spawn_replace_node then
plantslib:dbg("Spawn: "..plant_to_spawn.." to replace "..minetest.get_node(pos).name.." at ("..dump(pos)..")")
minetest.add_node(pos, { name = plant_to_spawn, param2 = fdir })
elseif biome.spawn_on_side then
local onside = plantslib:find_open_side(pos)
if onside then
plantslib:dbg("Spawn: "..plant_to_spawn.." at side of ("..dump(pos).."), facedir "..onside.facedir.."")
minetest.add_node(onside.newpos, { name = plant_to_spawn, param2 = onside.facedir })
end
elseif biome.spawn_on_bottom then
if minetest.get_node({x=pos.x, y=pos.y-1, z=pos.z}).name == "air" then
plantslib:dbg("Spawn: "..plant_to_spawn.." on bottom of ("..dump(pos)..")")
minetest.add_node({x=pos.x, y=pos.y-1, z=pos.z}, { name = plant_to_spawn, param2 = fdir} )
end
end
end
end
end
end
end
})
end
-- The growing ABM
function plantslib:grow_plants(opts)
local options = opts
options.height_limit = options.height_limit or 5
options.ground_nodes = options.ground_nodes or { "hades_core:dirt_with_grass" }
options.grow_nodes = options.grow_nodes or { "hades_core:dirt_with_grass" }
options.seed_diff = options.seed_diff or 0
plantslib:dbg("Registered growing ABM:")
plantslib:dbg(dump(options))
if options.grow_delay*time_scale >= 1 then
options.interval = options.grow_delay*time_scale
else
options.interval = 1
end
minetest.register_abm({
label = "Plantlib: Plant growth/death",
nodenames = { options.grow_plant },
interval = options.interval,
chance = options.grow_chance,
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.get_node(p_top)
local n_bot = minetest.get_node(p_bot)
local root_node = minetest.get_node({x=pos.x, y=pos.y-options.height_limit, z=pos.z})
local walldir = nil
if options.need_wall and options.verticals_list then
walldir = plantslib:find_adjacent_wall(p_top, options.verticals_list)
end
if n_top.name == "air" and (not options.need_wall or (options.need_wall and walldir))
then
if options.grow_vertically and walldir then
if plantslib:search_downward(pos, options.height_limit, options.ground_nodes) then
plantslib:dbg("Grow "..options.grow_plant.." vertically to "..dump(p_top))
minetest.add_node(p_top, { name = options.grow_plant, param2 = walldir})
end
elseif not options.grow_result and not options.grow_function then
plantslib:dbg("Die: "..options.grow_plant.." at ("..dump(pos)..")")
minetest.remove_node(pos)
else
plantslib:replace_object(pos, options.grow_result, options.grow_function, options.facedir, options.seed_diff)
end
end
end
})
end
-- Function to decide how to replace a plant - either grow it, replace it with
-- a tree, run a function, or die with an error.
function plantslib:replace_object(pos, replacement, grow_function, walldir, seeddiff)
local growtype = type(grow_function)
plantslib:dbg("replace_object called, growtype="..dump(grow_function))
if growtype == "table" then
plantslib:dbg("Grow: spawn tree at "..dump(pos))
minetest.remove_node(pos)
plantslib:grow_tree(pos, grow_function)
return
elseif growtype == "string" then
local perlin1 = minetest.get_perlin(seeddiff, perlin_octaves, perlin_persistence, perlin_scale)
local noise1 = perlin1:get_2d({x=pos.x, y=pos.z})
local noise2 = plantslib.perlin_temperature:get_2d({x=pos.x, y=pos.z})
plantslib:dbg("Grow: call function "..grow_function.."("..dump_pos(pos)..","..dump(walldir)..")")
assert(loadstring(grow_function.."("..dump_pos(pos)..","..dump(walldir)..")"))()
return
elseif growtype == "nil" then
plantslib:dbg("Grow: place "..replacement.." at ("..dump(pos)..") on wall "..dump(walldir))
minetest.add_node(pos, { name = replacement, param2 = walldir})
return
elseif growtype ~= "nil" and growtype ~= "string" and growtype ~= "table" then
error("Invalid grow function "..dump(grow_function).." used on object at ("..dump(pos)..")")
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.
function plantslib:find_adjacent_wall(pos, verticals)
local function find_adjacent_wall(pos, verticals)
local verts = dump(verticals)
if string.find(verts, minetest.get_node({ x=pos.x-1, y=pos.y, z=pos.z }).name) then return 3 end
if string.find(verts, minetest.get_node({ x=pos.x+1, y=pos.y, z=pos.z }).name) then return 2 end
@ -478,23 +75,7 @@ function plantslib:find_adjacent_wall(pos, verticals)
return nil
end
-- Function to search downward from the given position, looking for the first
-- node that matches the ground table. Returns the new position, or nil if
-- height limit is exceeded before finding it.
function plantslib:search_downward(pos, heightlimit, ground)
for i = 0, heightlimit do
if string.find(dump(ground), minetest.get_node({x=pos.x, y=pos.y-i, z = pos.z}).name) then
return {x=pos.x, y=pos.y-i, z = pos.z}
end
end
return false
end
function plantslib:find_open_side(pos)
local function find_open_side(pos)
if minetest.get_node({ x=pos.x-1, y=pos.y, z=pos.z }).name == "air" then
return {newpos = { x=pos.x-1, y=pos.y, z=pos.z }, facedir = 2}
end
@ -510,64 +91,90 @@ function plantslib:find_open_side(pos)
return nil
end
-- The spawning ABM
-- spawn_tree() on generate is routed through here so that other mods can hook
-- into it.
function plantslib:spawn_on_surfaces(biome)
function plantslib:generate_tree(pos, node_or_function_or_model)
local t=os.clock()
minetest.spawn_tree(pos, node_or_function_or_model)
plantslib:dbg("Generated one tree in ".. (os.clock()-t)*1000 .."ms")
end
-- and this one's for the call used in the growing code
function plantslib:grow_tree(pos, node_or_function_or_model)
local t=os.clock()
minetest.spawn_tree(pos, node_or_function_or_model)
plantslib:dbg("Generated one tree in ".. (os.clock()-t)*1000 .."ms")
end
-- check if a node is owned before allowing manual placement of a node
-- (used by flowers_plus)
function plantslib:node_is_owned(pos, placer)
local name = placer:get_player_name()
local is_protected = minetest.is_protected(pos, name) and not minetest.check_player_privs(name, "protection_bypass")
if is_protected then
minetest.record_protection_violation(pos, name)
return true
if biome.spawn_delay*time_scale >= 1 then
biome.interval = biome.spawn_delay*time_scale
else
return false
biome.interval = 1
end
set_defaults(biome)
biome.spawn_plants_count = table.getn(biome.spawn_plants)
dbg("Registered spawning ABM:")
dbg(dump(biome))
dbg("Number of trigger nodes in this ABM: "..biome.spawn_plants_count )
minetest.register_abm({
label = biome.label,
nodenames = biome.spawn_surfaces,
interval = biome.interval,
chance = biome.spawn_chance,
neighbors = biome.neighbors,
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.get_node(p_top)
if is_node_loaded(p_top) then
local n_light = minetest.get_node_light(p_top, nil)
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))
and n_light >= biome.light_min
and n_light <= biome.light_max
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 )
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)
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)
then
local walldir = find_adjacent_wall(p_top, biome.verticals_list)
if biome.alt_wallnode and walldir then
if n_top.name == "air" then
dbg("Spawn: "..biome.alt_wallnode.." on top of ("..dump(pos)..") against wall "..walldir)
minetest.add_node(p_top, { name = biome.alt_wallnode, param2 = walldir })
end
else
local currentsurface = minetest.get_node(pos).name
if currentsurface ~= "hades_core:water_source"
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 )
then
local rnd = math.random(1, biome.spawn_plants_count)
local plant_to_spawn = biome.spawn_plants[rnd]
dbg("Chose entry number "..rnd.." of "..biome.spawn_plants_count)
local fdir = biome.facedir
if biome.random_facedir then
fdir = math.random(biome.random_facedir[1],biome.random_facedir[2])
dbg("Gave it a random facedir: "..fdir)
end
if not biome.spawn_on_side and not biome.spawn_on_bottom and not biome.spawn_replace_node then
if n_top.name == "air" then
dbg("Spawn: "..plant_to_spawn.." on top of ("..dump(pos).."); facedir="..fdir)
minetest.add_node(p_top, { name = plant_to_spawn, param2 = fdir })
end
elseif biome.spawn_replace_node then
dbg("Spawn: "..plant_to_spawn.." to replace "..minetest.get_node(pos).name.." at ("..dump(pos)..")")
minetest.add_node(pos, { name = plant_to_spawn, param2 = fdir })
elseif biome.spawn_on_side then
local onside = find_open_side(pos)
if onside then
dbg("Spawn: "..plant_to_spawn.." at side of ("..dump(pos).."), facedir "..onside.facedir.."")
minetest.add_node(onside.newpos, { name = plant_to_spawn, param2 = onside.facedir })
end
elseif biome.spawn_on_bottom then
if minetest.get_node({x=pos.x, y=pos.y-1, z=pos.z}).name == "air" then
dbg("Spawn: "..plant_to_spawn.." on bottom of ("..dump(pos)..")")
minetest.add_node({x=pos.x, y=pos.y-1, z=pos.z}, { name = plant_to_spawn, param2 = fdir} )
end
end
end
end
end
end
end
})
end
-- Check for infinite stacks (LEGACY)
if minetest.get_modpath("unified_inventory") or not minetest.settings:get_bool("creative_mode") then
plantslib.expect_infinite_stacks = false
else
plantslib.expect_infinite_stacks = true
end
-- read a field from a node's definition
function plantslib:get_nodedef_field(nodename, fieldname)
if not minetest.registered_nodes[nodename] then
return nil
end
return minetest.registered_nodes[nodename][fieldname]
end
minetest.log("action", "[Plantlife Library] Loaded")

View File

@ -1,2 +0,0 @@
# textdomain:plants_lib
Sorry, someone owns that spot.=Tschuldigung, jemanden gehört diese Stelle.

View File

@ -1,2 +0,0 @@
# textdomain:plants_lib
Sorry, someone owns that spot.=