Naturalslopeslib Lua API
========================

Table of contents
-- Introduction
-- Usage
-- Definitions
---- ReplacementTable
-- Registration API
---- naturalslopeslib.default_definition
---- naturalslopeslib.reset_defaults
---- naturalslopeslib.register_slope
---- naturalslopeslib.set_slopes
---- naturalslopeslib.register_sloped_stomp
---- naturalslopeslib.propagate_overrides
-- Getters
---- naturalslopeslib.get_slope_defs
---- naturalslopeslib.get_regular_node_name
---- naturalslopeslib.get_replacement
---- naturalslopeslib.get_replacement_id
---- naturalslopeslib.get_all_shapes
---- naturalslopeslib.list_registered_slopes
-- Shape update API
---- naturalslopeslib.is_free_for_shape_update
---- naturalslopeslib.area_is_free_for_shape_update
---- naturalslopeslib.get_replacement_node
---- naturalslopeslib.chance_update_shape
---- naturalslopeslib.update_shape
---- naturalslopeslib.update_shape_on_walk
-- Map generation
---- naturalslopeslib.set_manual_map_generation
---- naturalslopeslib.area_chance_update_shape
---- naturalslopeslib.register_progressive_area_update
-- Settings getters
---- naturalslopeslib.setting_enable_surface_update
---- naturalslopeslib.setting_enable_shape_on_walk
---- naturalslopeslib.setting_enable_shape_on_generation
---- naturalslopeslib.setting_generation_method
---- naturalslopeslib.setting_generation_factor
---- naturalslopeslib.setting_stomp_factor
---- naturalslopeslib.setting_dig_place_factor
---- naturalslopeslib.setting_time_factor
---- naturalslopeslib.setting_generation_skip
---- naturalslopeslib.setting_enable_shape_on_dig_place
---- naturalslopeslib.setting_smooth_rendering
-- Chat commands
---- updshape


Introduction
------------

Naturalslopeslib adds the ability for given nodes to turn into slopes and back to full block shape by itself according to the surroundings and the material hardness. It creates natural landscape and smoothes movements.

Slopes can be generated in various ways. Those events can be turned on or off in settings. The shape is updated on generation, with time, by stepping on edges or when digging and placing nodes.

As Minetest main unit is the block, having half-sized blocks can break a lot of things. Thus half-blocks like slopes are still considered as a single block. A single slope can turn back to a full node and vice-versa and half-blocks are not considered buildable upon (they will transform back into full block).

Usage
-----

You may register slopes in two ways: letting the mod generating all the stuff or getting the definitions and registering the nodes in the calling mod. With the first method, slope nodes will be registered within naturalslopeslib while with the second method, you can set the slope names from the calling mod. In both cases, the shape update behaviour is handled automatically by the library according to the settings and the availability of poschangelib and twmlib.

For the first method, just call naturalslopeslib.register_slopes. For example:

	naturalslopeslib.register_slopes("default:dirt")

You can use `naturalslopeslib.get_all_shapes` to get the name of the slope nodes.

For the second method, get the slope definitions from naturalslopeslib.get_slope_defs and register the four nodes manually with the desired names with minetest.register_node. When done, call naturalslopeslib.set_slopes to link all the different shapes.

For example:

	local slope_defs = naturalslopeslib.get_slope_defs("defaut:dirt")
	local slope_names = {
		"default:dirt_slope", "default:dirt_inner_corner",
		"default:dirt_outer_corner", "default:dirt_pike"
	}
	for i, def in ipairs(slope_defs) do
		minetest.register_node(slope_names[i], def)
	end
	naturalslopeslib.set_slopes("default:dirt",
		slope_names[1], slope_names[2],
		slope_names[3], slope_names[4], factors)

Regarding dependencies, the slopes are defined by copying the current definition of the original node. This means that modifications applied to the original node after the slopes are registered are not applied to slopes. If you want the slopes to act like the original nodes no matter what happen to their definition, you can call naturalslopes.propagate_overrides() before or after registering slopes. That way all future call to minetest.override_item (even from other unknown mods) will also apply to slopes silently, removing the need to explicitely define mod requirements.


Definitions
-----------

### ReplacementTable

A table containing references to various shape. The type of references can either be a name or an internal id.

	{
		source         = full node reference,
		straight       = straight slope reference,
		inner          = inner corner reference,
		outer          = outer corner reference,
		pike           = pike/slab reference,
		chance         = inverted chance of happening,
		chance_factors = multiplicator for `chance` for each type
			{mapgen = w, stomp = x, time = y, place = z}.
			By default all of these factors are 1 (no effect).
	}


Registration API
----------------

### naturalslopeslib.default_definition

This tables holds default definition override for newly registered slopes. When using register_slope, they are added to def_changes if not already set to avoid copy/pasting a lot of things and automate some behaviours.

	{
		drop_source = true/false
		-- When true, if no drop is defined, it is set to the source node 
		-- instead of the slope.
		-- For example, digging a dirt slope gives a dirt block (when true)
		-- or the dirt slope (when false)
		tiles = {{align_style="world"}}
		-- As for tile definitions, the list can hold up to 6 values,
		-- but only align_style, backface_culling and scale are read.
		groups = {not_in_creative_inventory = 1}
		-- The list of groups to add with their value.
		-- Set a group value to 0 to remove it
		other keys
		-- Override this key when no change is explicitely set.
	}

Note that changes to default_definitions are not retroactives. If the defaults are changed on the run, all slopes that were previously registered are not affected.

Good practices are setting the defaults before registering your slopes, then calling naturalslopeslib.reset_defaults() to prevent your settings to effect further declarations.

### naturalslopeslib.reset_defaults()

Resets `naturalslopeslib.default_definition` to the less-impacting values.

These defaults are as follow:

	{
		drop_source = false,
		tiles = {},
		groups = {}
	}

### naturalslopeslib.register_slope(base_node_name, def_changes, update_chance, factors, color_convert)

Registers all slope shapes and automatic stomping for a full node.

* `base_node_name` the full block node name.
* `def_changes` changes to apply from the base node definition.
  * All the attributes are copied to the sloped nodes expect those ones which are replaced:
    * `drawtype` set to "nodebox" or "mesh" according to the rendering mode
    * `nodebox` or `mesh` is replaced
    * `selection_box` and `collision_box` matching to the according mesh
    * `paramtype` is set to "light", and `paramtype2` to "facedir" or "colorfacedir"
    * the group `"natural_slope"` is added (1 = straight, 2 = inner corner, 3 = outer corner, 4 = pike)
    * the group `"family:<full node name>"` is added
  * Then they are changed from def_changes. Use `"nil"` string to explicitely erase a value (an not `nil`).
* `update_chance` inverted chance for the node to be updated.
* `factors` optional table for chance factors. By default each factor is 1.
* `color_convert` optional function to convert color palettes (see below). Ignored when paramtype2 from the base node is not `"color"`. By default, it matches the first 8 values, and other color values are set to 0.
* returns ReplacementTable.

About color palettes: The palette for slopes can only have 8 colors while the original one can hold up to 256 colors. A reduced palette must be provided for nodes which paramtype2 is "color" even if not all colors are used. To control how the palette values are converted, you may pass a function(int, bool) as `color_convert`. When the second parameter is true, the first parameter is the full block color index (from 0 to 255) and it must return an index for the slope color (from 0 to 7). When false the first parameter is the slope color index (from 0 to 7) and it must return an index for the full block color index (from 0 to 255).

### naturalslopeslib.set_slopes(base_node_name, straight_name, inner_name, outer_name, pike_name, update_chance, factors)

* Link existing nodes. Same as register_slopes but without registering new nodes. Use it when the shapes are already registered from eslewhere. The node definitions are not changed at all.
* `base_node_name` the full node name.
* `straight_name` the straight slope node name.
* `inner_name` the inner corner node name.
* `outer_name` the outer corner node name.
* `pike_name` the pike/slab node name.
* `update_chance` the inverted chance of happening.
* `factors` optional table for chance factors. By default each factor is 1.
* returns a `ReplacementTable`.

### naturalslopeslib.register_sloped_stomp(source_node_name, dest_node_name, stomp_desc)

Register `stomp_desc` from all shapes of `source_node_name` to `dest_node_name`.

It requires `poschangelib`. If the mod is not activated, this function will do nothing.

### naturalslopeslib.propagate_overrides()

Once called, calling `minetest.override_item` from that point will also apply the modifications to the corresponding slopes. Once called, this behaviour cannot be disabled.


Getters
-------

### naturalslopeslib.get_slope_defs(base_node_name, def_changes)

* `base_node_name` the full block node name.
* `def_changes` changes to apply from the base node definition.
  * All the attributes are copied to the sloped nodes expect those ones which are replaced:
    * `drawtype` set to "nodebox" or "mesh" according to the rendering mode
    * `nodebox` or `mesh` is replaced
    * `selection_box` and `collision_box` matching to the according mesh
    * `paramtype` is set to "light", and `paramtype2` to "facedir" or "colorfacedir"
    * the group "natural_slope" is added (1 = straight, 2 = inner corner, 3 = outer corner, 4 = pike)
    * the group "family:<full node name>" is added
  * Then they are changed from `def_changes`. Use `"nil"` string to explicitely erase a value (an not `nil`).
* returns a table of node definitions for straight slope, inner corner, outer corner and pike in that order.

Warning: The palette for slopes can only have 8 colors while the original one can hold up to 256 colors. A reduced palette must be provided for nodes which paramtype2 is `"color"` even if not all colors are used.

### naturalslopeslib.get_regular_node_name(slope_node_name)

* `slope_node_name` a node name.
* returns the name of the regular node (the unslopped one). Nil if it is not a slope node.
* It may be unnaccurate as it checks only if the name follows the internal pattern for slope names.

### naturalslopeslib.get_replacement(source_node_name)

* `source_node_name` a registered node name.
* returns a `ReplacementTable`. Nil if no slopes are registered.

### naturalslopeslib.get_replacement_id(source_id)

* `source_id` the id of the node.
* returns a `ReplacementTable` with node ids as values. Nil if no slopes are registered.

### naturalslopeslib.get_all_shapes(source_node_name)

Returns all variant shape names in a table {block, straight slope, inner corner, outer corner, pike}. Returns {source_node_name} if there are no other shapes for this node.

* `source_node_name` a node name, can be a full block or a slope.

### naturalslopeslib.get_all_slopes(source_node_name)

Returns all sloped variant shape names in a table {straight slope, inner corner, outer corner, pike}. Returns {} if there are no slopes for this node.

* `source_node_name` a node name, can be a full block or a slope.

### naturalslopeslib.list_registered_nodes()

Returns the list of nodes in block shape that have slopes registered for.


Shape update API
----------------

### naturalslopeslib.is_free_for_shape_update(pos)

Checks if a node is considered free for defining which shape could be picked.

* `pos` the position of the node to check (probably a neighbour of a candidate to shape update).
* returns `true` if the node is free, `false` if occupied, `nil` if unknown (not loaded)

### naturalslopeslib.area_is_free_for_shape_update(area, data, index)

Checks if a node is considered free for defining which shape could be picked.

* `area` VoxelArea to use.
* `data` Data from VoxelManip.
* `index` position in area.
* returns `true` if the node is free, `false` if occupied, `nil` if unknown (not loaded)

Was previously named naturalslopeslib.area_is_free_for_erosion.

### naturalslopeslib.get_replacement_node(pos, node, [area, data, param2_data])

Get the replacement node according to it's surroundings. This function exists in two formats, for a single position or a VoxelArea.

In both case, it returns the parameters to update the node or nil when no replacement is available.

* For a single node
	* `pos` the position of the node or index with VoxelArea.
	* `node` the node at that position.
	* returns a node for minetest.set_node.
* For a VoxelArea
	* `index` (the `pos` argument) the index within the area.
	* `content_id` (the `node` argument) the node at that position or content id with VoxelArea.
	* `area` the VoxelArea, nil for single position update (determines which type of the two previous arguments are).
	* `data` Data from VoxelManip, nil for single position update.
	* `param2_data` param2 data from VoxelManip, nil for single position update.
	* Returns a table with id and param2_data.

### naturalslopeslib.chance_update_shape(pos, node, factor, type)

Do shape update when random roll passes on a single node.

* `pos` the position to update.
* `node` the node at pos.
* `factor` optional chance factor, when > 1 it have more chances to happen
* `type` optional update type for chance factors. Either "mapgen", "stomp", "place" or "time". When not set, the chance factor is ignored (as if it is 1). It is cumulative with `factor`.
* returns true if an update was done, false otherwise.

### naturalslopeslib.update_shape(pos, node)

Do shape update disregarding chances.

* `pos` the position to update.
* `node` the node at pos.
* returns true if an update was done, false otherwise.

### naturalslopeslib.update_shape_on_walk(player, pos, node, desc, trigger_meta)

Callback for poschangelib, to get the same effect as naturalslopeslib.update_shape.


Map generation
--------------

These functions allows to tweak the map generation to change the default behaviour.
Which is updating an area on generation after other map generation functions.

### naturalslopeslib.set_manual_map_generation()

Disables the default registration to handle the mapgen manually. Once it is called, other mods should take care of handling shape update on generation. Otherwise nothing is done.

### naturalslopeslib.area_chance_update_shape(minp, maxp, factor, skip, type)

Massive shape update with VoxelManip. This is the VoxelManip on generation method.

* `minp` lower boundary of area.
* `mapx` higher boundary of area.
* `factor` Inverted factor for chance (0.1 means 10 times more likely to update)
* `skip` optional random skip, roughfly ignore skip/2 to skip nodes.
* `type` optional update type for chance factors. Either "mapgen", "stomp", "place" or "time". When not set, the chance factor is ignored (as if it is 1). It is cumulative with `factor`.

### naturalslopeslib.register_progressive_area_update(minp, maxp, factor, skip, type)

Mark an area to be updated progressively. This is the Progressive on generation method. The area is not updated instantly but added to a list.

* `minp` lower boundary of area.
* `mapx` higher boundary of area.
* `factor` Inverted factor for chance (0.1 means 10 times more likely to update)
* `skip` optional random skip, roughfly ignore skip/2 to skip nodes.
* `type` optional update type for chance factors. Either "mapgen", "stomp", "place" or "time". When not set, the chance factor is ignored (as if it is 1). It is cumulative with `factor`.


Settings getters
----------------

These functions get the current settings with the default value if not set.

### naturalslopeslib.setting_enable_surface_update()

* Returns `true` or `false`. Always `false` if twmlib is not available.

### naturalslopeslib.setting_enable_shape_on_walk()

* Returns `true` or `false`. Always `false` if poschangelib is not available.

### naturalslopeslib.setting_enable_shape_on_generation()

* Returns `true` or `false`. It may not reflect the actual behaviour if the default mapgen behaviour was disabled by naturalslopeslib.set_manual_map_generation.

### naturalslopeslib.setting_generation_method()

* Returns `"VoxelManip"` or `"Progressive"`. It may not reflect the actual behaviour if the default mapgen behaviour was disabled by naturalslopeslib.set_manual_map_generation.

### naturalslopeslib.setting_generation_factor()

* Returns the chance factor for map generation to reflect the landscape age. It is cumulative with the `"mapgen"` chance factor of each node if any is defined. It may not reflect the actual behaviour if the default mapgen behaviour was disabled by naturalslopeslib.set_manual_map_generation.

### naturalslopeslib.setting_stomp_factor()

* Returns the chace factor when walking on nodes. It is cumulative with the `"stomp"` chance factor of each node if any is defined.
* This factor is applied upon node registration.

### naturalslopeslib.setting_dig_place_factor()

* Returns the chace factor when the neighbouring nodes change. It is cumulative with the `"place"` chance factor of each node if any is defined.

### naturalslopeslib.setting_time_factor()

* Returns the chace factor on timed update. It is cumulative with the `"time"` chance factor of each node if any is defined.
* This factor is applied upon node registration.

### naturalslopeslib.setting_generation_skip()

* Returns the approximate number of nodes skipped for each node. It may not reflect the actual behaviour if the default mapgen behaviour was disabled by naturalslopeslib.set_manual_map_generation.

### naturalslopeslib.setting_enable_shape_on_dig_place()

* Returns `true` or `false`. This setting is read only on startup and may not reflect the actual value if it was changed while the server is running.

### naturalslopeslib.setting_smooth_rendering()

* Returns `true` or `false`. This setting is read only when registering nodes and may not reflect the actual value if it was changed while the server is running.


Chat commands
-------------

### /updshape

* requires `server` privilege.
* Force updating the node the player is standing upon.
