Compare commits

...

6 Commits

  1. 13
      CHANGELOG.md
  2. 6
      README.md
  3. 12
      init.lua
  4. 1
      mod.conf
  5. 195
      naturalslopeslib_api.txt
  6. 124
      register_slopes.lua
  7. 128
      update_shape.lua

13
CHANGELOG.md

@ -3,6 +3,19 @@ Changelog
The semantic of version number is 'Major.minor'. Minor updates are retro-compatible, while major updates may break things.
[1.3] - 2021-08-08
------------------
### Added
- `naturalslopeslib.default_definition` and `naturalslopeslib.reset_defaults` to factorize common definition.
- `color_convert` function as parameter to slope definitions to convert color values between 256 and 8 values.
### Fixed
- `naturalslopeslib.chance_update_shape` and `naturalslopeslib.update_shape` return true only when the node actually changed.
- Keeping color value when switching shape.
- Removing properties with "nil" with `naturalslopeslib.register_slopes`.
[1.2] - 2021-02-23
------------------

6
README.md

@ -1,7 +1,7 @@
Natural slopes library
======================
* Version 1.2
* Version 1.3
* With thanks to all modders, mainly from the stairs mod for study.
This mod add the ability for nodes to turn into slopes and back to full block
@ -26,8 +26,8 @@ have any effect by itself.
## Optional dependencies:
* poschangelib: to enable shape update when walking on nodes
* twmlib: to enable update from time to time
* `poschangelib`: to enable shape update when walking on nodes
* `twmlib`: to enable update from time to time
## Source code

12
init.lua

@ -1,7 +1,8 @@
-- Global namespace for functions
naturalslopeslib = {
_register_on_generated = true,
_propagate_overrides = false
_propagate_overrides = false,
default_definition = {} -- initialized below
}
local poschangelib_available = false
@ -14,6 +15,15 @@ for _, name in ipairs(minetest.get_modnames()) do
end
end
function naturalslopeslib.reset_defaults()
naturalslopeslib.default_definition = {
drop_source = false,
tiles = {},
groups = {}
}
end
naturalslopeslib.reset_defaults()
--- Get the name of the regular node from a slope, or nil.
function naturalslopeslib.get_regular_node_name(node_name)
if string.find(node_name, ":slope_") == nil then

1
mod.conf

@ -1,2 +1,3 @@
description = Library for sloped variation of nodes and automatic shape-shifting according to surroundings
optional_depends = twmlib, poschangelib
min_minetest_version = 5.0

195
naturalslopeslib_api.txt

@ -7,6 +7,8 @@ Table of contents
-- Definitions
---- ReplacementTable
-- Registration API
---- naturalslopeslib.default_definition
---- naturalslopeslib.reset_defaults
---- naturalslopeslib.register_slope
---- naturalslopeslib.set_slopes
---- naturalslopeslib.register_sloped_stomp
@ -58,24 +60,27 @@ 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 the first method, just call naturalslopeslib.register_slopes. For example:
For example:
naturalslopeslib.register_slopes("default:dirt")
naturalslopeslib.register_slopes("default:dirt")
You can use naturalslopeslib.get_all_shapes to get the name of the slope nodes.
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)
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.
@ -86,23 +91,63 @@ 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).
}
{
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.register_slope(base_node_name, def_changes, update_chance, factors)
### 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.
* 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:
@ -110,14 +155,15 @@ Registration API
* `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).
* 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.
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.
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)
@ -129,16 +175,17 @@ Warning: The palette for slopes can only have 8 colors while the original one ca
* `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 ReplacementTable.
* returns a `ReplacementTable`.
### naturalslopeslib.register_sloped_stomp(source_node_name, dest_node_name, stomp_desc)
* Requires `poschangelib`. If the mod is not installed, this function will do nothing.
* Register `stomp_desc` from all shapes of `source_node_name` to `dest_node_name`.
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.
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
@ -155,10 +202,10 @@ Getters
* `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).
* 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.
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)
@ -169,16 +216,17 @@ Warning: The palette for slopes can only have 8 colors while the original one ca
### naturalslopeslib.get_replacement(source_node_name)
* `source_node_name` a registered node name.
* returns a SlopeDef. Nil if no slopes are registered.
* 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.
* returns a `ReplacementTable` with node ids as values. Nil if no slopes are registered.
### naturalslopeslib.get_all_shapes(source_node_name)
Returns the node and all it's slope names in a table {source, straight, inner corner, outer corner, pike}. Returns {source} if there are no slopes for this node.
* `source_node_name` The full block node name.
@ -187,38 +235,44 @@ Shape update API
### naturalslopeslib.is_free_for_shape_update(pos)
* Checks if a node is considered free for defining which shape could be picked.
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 occupide, nil if unknown (not loaded)
* 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.
* Was previously named naturalslopeslib.area_is_free_for_erosion.
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 occupide, nil if unknown (not loaded)
* 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.
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.
* `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.
* `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.
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
@ -227,14 +281,15 @@ Shape update API
### naturalslopeslib.update_shape(pos, node)
* Do shape update disregarding chances.
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.
Callback for poschangelib, to get the same effect as naturalslopeslib.update_shape.
Map generation
@ -245,14 +300,12 @@ 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.
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.
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)
@ -261,8 +314,8 @@ Which is updating an area on generation after other map generation functions.
### 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.
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)
@ -277,36 +330,36 @@ 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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()
@ -315,17 +368,17 @@ These functions get the current settings with the default value if not set.
### 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.
* 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.
* 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
### /updshape
* requires `server` privilege.
* Force updating the node the player is standing upon.

124
register_slopes.lua

@ -1,9 +1,29 @@
-- Default color index conversion: match values for 0-7 and set to 0 for other values.
local function default_color_convert(color_index, to_slope)
if to_slope then
if color_index > 7 then
return 0
else
return color_index
end
else
return color_index
end
end
-- Table of replacement from solid block to slopes.
-- Populated on slope node registration with add_replacement
-- @param colored_source (boolean) true when paramtype2 is color for the source node
-- color_convert is a function(int, int, bool) to convert the color palette values,
-- it is ignored when colored_source is false.
local replacements = {}
local replacement_ids = {}
local function add_replacement(source_name, update_chance, chance_factors, fixed_replacements)
local function add_replacement(source_name, update_chance, chance_factors, fixed_replacements, colored_source, color_to_slope, color_convert)
if not colored_source then
color_convert = nil
elseif color_convert == nil then
color_convert = default_color_convert
end
local subname = string.sub(source_name, string.find(source_name, ':') + 1)
local straight_name = nil
local ic_name = nil
@ -33,7 +53,9 @@ local function add_replacement(source_name, update_chance, chance_factors, fixed
outer = oc_name,
pike = pike_name,
chance = update_chance,
chance_factors = chance_factors
chance_factors = chance_factors,
_colored_source = colored_source,
_color_convert = color_convert
}
local dest_data_id = {
source = source_id,
@ -42,7 +64,9 @@ local function add_replacement(source_name, update_chance, chance_factors, fixed
outer = oc_id,
pike = pike_id,
chance = update_chance,
chance_factors = chance_factors
chance_factors = chance_factors,
_colored_source = colored_source,
_color_convert = color_convert
}
-- Block
replacements[source_name] = dest_data
@ -190,6 +214,17 @@ local function get_pike_def(base_node_name, node_def, update_chance)
return node_def
end
-- Expand `tiles` to use the {name = "image"} format for each tile
local function convert_to_expanded_tiles_def(tiles)
if tiles then
for i, tile_def in ipairs(tiles) do
if type(tile_def) == "string" then
tiles[i] = {name = tile_def}
end
end
end
end
function naturalslopeslib.get_slope_defs(base_node_name, def_changes)
local base_node_def = minetest.registered_nodes[base_node_name]
if not base_node_def then
@ -197,6 +232,7 @@ function naturalslopeslib.get_slope_defs(base_node_name, def_changes)
return
end
local full_copy = table.copy(base_node_def)
local changes_copy = table.copy(def_changes)
for key, value in pairs(def_changes) do
if value == "nil" then
full_copy[key] = nil
@ -204,6 +240,67 @@ function naturalslopeslib.get_slope_defs(base_node_name, def_changes)
full_copy[key] = value
end
end
-- Handle default drop overrides
if not base_node_def.drop and not def_changes.drop and naturalslopeslib.default_definition.drop_source then
-- If drop is not set and was not reseted
full_copy.drop = base_node_name
end
-- Convert all tile definition to the list format to be able to override properties
if not full_copy.tiles or #full_copy.tiles == 0 then
full_copy.tiles = {{}}
end
convert_to_expanded_tiles_def(full_copy.tiles)
if not changes_copy.tiles or #changes_copy.tiles == 0 then
changes_copy.tiles = {{}}
end
convert_to_expanded_tiles_def(changes_copy.tiles)
local default_tile_changes = table.copy(naturalslopeslib.default_definition.tiles)
if not default_tile_changes or #default_tile_changes == 0 then
default_tile_changes = {{}}
end
convert_to_expanded_tiles_def(default_tile_changes)
-- Make tile changes and default changes the same size
local desired_size = math.max(#full_copy.tiles, #changes_copy.tiles, #default_tile_changes)
while #changes_copy.tiles < desired_size do
table.insert(changes_copy.tiles, table.copy(changes_copy.tiles[#changes_copy.tiles]))
end
while #default_tile_changes < desired_size do
-- no need to copy because defaults won't be alterated
table.insert(default_tile_changes, default_tile_changes[#default_tile_changes])
end
while #full_copy.tiles < desired_size do
table.insert(full_copy.tiles, table.copy(full_copy.tiles[#full_copy.tiles]))
end
-- Apply default tile changes
for i = 1, desired_size, 1 do
if default_tile_changes[i].align_style ~= nil and changes_copy.tiles[i].align_style == nil then
full_copy.tiles[i].align_style = default_tile_changes[i].align_style
end
if default_tile_changes[i].backface_culling ~= nil and changes_copy.tiles[i].backface_culling == nil then
full_copy.tiles[i].backface_culling = default_tile_changes[i].backface_culling
end
if default_tile_changes[i].scale and changes_copy.tiles[i].scale == nil then
full_copy.tiles[i].scale = default_tile_changes[i].scale
end
end
-- Handle default groups
for group, value in pairs(naturalslopeslib.default_definition.groups) do
if not def_changes.groups or def_changes.groups[group] == nil then
full_copy.groups[group] = value
end
end
-- Handle other values
for key, value in pairs(naturalslopeslib.default_definition) do
if key ~= "groups" and key ~= "drop_source" and key ~= "tiles" then
if changes_copy[key] == nil then
if type(value) == "table" then
full_copy[key] = table.copy(value)
else
full_copy[key] = value
end
end
end
end
-- Use a copy because tables are passed by reference. Otherwise the node
-- description is shared and updated after each call
return {
@ -232,8 +329,9 @@ end
-- @param node_desc: base for slope node descriptions.
-- @param update_chance: inverted chance for the node to be updated.
-- @param factors (optional): chance factor for each type.
-- @param color_convert (optional): the function to convert color palettes
-- @return Table of slope names: [straight, inner, outer, pike] or nil on error.
function naturalslopeslib.register_slope(base_node_name, def_changes, update_chance, factors)
function naturalslopeslib.register_slope(base_node_name, def_changes, update_chance, factors, color_convert)
if not update_chance then
minetest.log('error', 'Natural slopes: chance is not set for node ' .. base_node_name)
return
@ -243,14 +341,6 @@ function naturalslopeslib.register_slope(base_node_name, def_changes, update_cha
minetest.log("error", "Trying to register slopes for an unknown node " .. (base_node_name or "nil"))
return
end
local full_copy = table.copy(base_node_def)
for key, value in pairs(def_changes) do
if value == "nil" then
full_copy[key] = nil
else
full_copy[key] = value
end
end
local chance_factors = default_factors(factors)
-- Get new definitions
local subname = string.sub(base_node_name, string.find(base_node_name, ':') + 1)
@ -260,7 +350,7 @@ function naturalslopeslib.register_slope(base_node_name, def_changes, update_cha
naturalslopeslib.get_outer_corner_slope_name(subname),
naturalslopeslib.get_pike_slope_name(subname)
}
local slope_defs = naturalslopeslib.get_slope_defs(base_node_name, full_copy)
local slope_defs = naturalslopeslib.get_slope_defs(base_node_name, def_changes)
-- Register all slopes
local stomp_factor = naturalslopeslib.setting_stomp_factor()
for i, name in ipairs(slope_names) do
@ -274,7 +364,8 @@ function naturalslopeslib.register_slope(base_node_name, def_changes, update_cha
end
end
-- Register replacements
add_replacement(base_node_name, update_chance, chance_factors, slope_names)
local colored = base_node_def.paramtype2 == "color"
add_replacement(base_node_name, update_chance, chance_factors, slope_names, colored, color_convert)
-- Enable on walk update for base node
if naturalslopeslib.setting_enable_shape_on_walk() then
poschangelib.register_stomp(base_node_name,
@ -295,7 +386,7 @@ function naturalslopeslib.register_slope(base_node_name, def_changes, update_cha
end
--- Add a slopping behaviour to existing nodes.
function naturalslopeslib.set_slopes(base_node_name, straight_name, inner_name, outer_name, pike_name, update_chance, factors)
function naturalslopeslib.set_slopes(base_node_name, straight_name, inner_name, outer_name, pike_name, update_chance, factors, color_convert)
-- Defensive checks
if not minetest.registered_nodes[base_node_name] then
if not base_node_name then
@ -319,7 +410,8 @@ function naturalslopeslib.set_slopes(base_node_name, straight_name, inner_name,
local chance_factors = default_factors(factors)
-- Set shape update data
local slope_names = {straight_name, inner_name, outer_name, pike_name}
add_replacement(base_node_name, update_chance, chance_factors, slope_names)
local colored = minetest.registered_nodes[base_node_name].paramtype2 == "color"
add_replacement(base_node_name, update_chance, chance_factors, slope_names, colored, color_convert)
-- Set surface update
if naturalslopeslib.setting_enable_surface_update() then
local time_factor = naturalslopeslib.setting_time_factor()

128
update_shape.lua

@ -6,18 +6,60 @@ Describes the falling/eroding effect for slopes
Pick replacement, node and area
--]]
--- {Private} Pick a replacement node and set it at pos.
-- Manage color for param2
-- @param replacement the replacement table
-- @param source the name or id of the node being transformed
-- @param dest the name or id of the new shape
-- @param param2_source the param2 value before transformation
-- @param param2_dest the param2 value for facedir (if any) after transformation
-- @return a new param2 value for dest node with color if necessary
local function manage_param2_color(replacement, source, dest, param2_source, param2_dest)
if not replacement._colored_source then
return param2_dest
end
if dest == replacement.source then
-- param2_source will hold a 'color' value
if source == replacement.source then
-- from 'color' to 'color'
return param2_source
else
-- from 'color' to 'colorfacedir'
local new_color_index = replacement._color_convert(param2_source, true) % 8
return param2_dest + (new_color_index * 32)
end
else
-- param2_source will hold a 'colorfacedir' value
if dest == replacement.source then
-- from 'colorfacedir' to 'color'
local old_color = math.floor(param2_source / 32)
return replacement._color_convert(old_color, false) % 256
else
-- from 'colorfacedir' to an other 'colorfacedir'
local color = math.floor(param2_source / 32)
return param2_dest + (color * 32)
end
end
end
--- {Private} Pick a replacement node.
-- @param type The replacement shape. Either 'block', 'straight', 'ic' or 'oc'
-- @param name The name of the node to replace.
-- @param pos The position of the node to replace
-- @param param2 Optional value to orient the new node.
-- @return True if the node is replaced, false otherwise.
local function pick_replacement(slope_type, name, pos, param2)
local replacement = naturalslopeslib.get_replacement(name)
-- @param name The name (or id for area) of the node to replace.
-- @param old_param2 The current value of param2 for the node to replace
-- @param param2 Facedir value to orient the new node.
-- @param for_area True when picking for an area, changes the parameter types
-- @return node {name=new_name, param2=new_param2} or area data {id=new_id, param2_data=new_param2}
-- or nil if dest node is not found.
local function pick_replacement(slope_type, name, old_param2, param2, for_area)
local replacement
if for_area then
replacement = naturalslopeslib.get_replacement_id(name)
else
replacement = naturalslopeslib.get_replacement(name)
end
if not replacement then return nil end
local dest_node_name = nil
if slope_type == 'block' and replacement.source then
return {name=replacement.source}
dest_node_name = replacement.source
elseif slope_type == 'pike' and replacement.pike then
dest_node_name = replacement.pike
elseif slope_type == 'straight' and replacement.straight then
@ -28,31 +70,13 @@ local function pick_replacement(slope_type, name, pos, param2)
dest_node_name = replacement.outer
end
if dest_node_name then
return {name = dest_node_name, paramtype2='facedir',
param2 = param2}
end
return nil
end
local function area_pick_replacement(slope_type, data, param2_data, id, index, param2)
local replacement = naturalslopeslib.get_replacement_id(id)
if not replacement then return false end
local dest_node_id = nil
local paramtype2 = nil
if slope_type == 'block' and replacement.source then
return {id = replacement.source}
elseif slope_type == 'pike' and replacement.pike then
dest_node_id = replacement.pike
elseif slope_type == 'straight' and replacement.straight then
dest_node_id = replacement.straight
elseif slope_type == 'ic' and replacement.inner then
dest_node_id = replacement.inner
elseif slope_type == 'oc' and replacement.outer then
dest_node_id = replacement.outer
end
if dest_node_id then
return {id = dest_node_id, paramtype2="facedir",
param2_data = param2}
if param2 == nil then param2 = 0 end
local color_param2 = manage_param2_color(replacement, name, dest_node_name, old_param2, param2)
if for_area then
return {id = dest_node_name, param2_data = color_param2}
else
return {name = dest_node_name, param2 = color_param2}
end
end
return nil
end
@ -98,7 +122,10 @@ function naturalslopeslib.get_replacement_node(pos, node, area, data, param2_dat
local new_pos = nil
local replacement = nil
local node_name = nil -- Either name or id
local for_area = false
local old_param2 = 0
if area then
for_area = true
is_free = function (at_index) -- always use with new_pos
return naturalslopeslib.area_is_free_for_shape_update(area, data, at_index)
end
@ -106,16 +133,13 @@ function naturalslopeslib.get_replacement_node(pos, node, area, data, param2_dat
local area_pos = area:position(pos)
return area:indexp(vector.add(area_pos, add))
end
replacement = function(slope_type, name, pos, pointing)
return area_pick_replacement(slope_type,
data, param2_data, name, pos, pointing)
end
node_name = node
old_param2 = param2_data[pos]
else
is_free = naturalslopeslib.is_free_for_shape_update
new_pos = function(add) return vector.add(pos, add) end
replacement = pick_replacement
node_name = node.name
old_param2 = node.param2
end
local is_ground -- ground or ceiling node
local pointing_y = -1
@ -132,7 +156,7 @@ function naturalslopeslib.get_replacement_node(pos, node, area, data, param2_dat
is_ground = false
pointing_y = 5
else -- nothing below and above
return replacement("block", node_name, pos)
return pick_replacement("block", node_name, old_param2, 0, for_area)
end
-- Check blocks around
local airXP = is_free(new_pos({x=1, y=0, z=0}))
@ -151,26 +175,26 @@ function naturalslopeslib.get_replacement_node(pos, node, area, data, param2_dat
if free_neighbors == 4 or free_neighbors == 3 then
local param2 = 0
if is_ground == false then param2 = 20 end
return replacement("pike", node_name, pos, param2)
return pick_replacement("pike", node_name, old_param2, param2, for_area)
-- For two free neighbors
elseif free_neighbors == 2 then
-- at opposite sides, block
local param2
if (airXP and airXM) or (airZP and airZM) then
return replacement('block', node_name, pos)
return pick_replacement('block', node_name, old_param2, 0, for_area)
-- side by side, outer corner
elseif (airXP and airZP) then
if is_ground then param2 = 3 else param2 = 22 end
return replacement("oc", node_name, pos, param2)
return pick_replacement("oc", node_name, old_param2, param2, for_area)
elseif (airXP and airZM) then
if is_ground then param2 = 0 else param2 = 21 end
return replacement("oc", node_name, pos, param2)
return pick_replacement("oc", node_name, old_param2, param2, for_area)
elseif (airXM and airZP) then
if is_ground then param2 = 2 else param2 = 23 end
return replacement("oc", node_name, pos, param2)
return pick_replacement("oc", node_name, old_param2, param2, for_area)
elseif (airXM and airZM) then
if is_ground then param2 = 1 else param2 = 20 end
return replacement("oc", node_name, pos, param2)
return pick_replacement("oc", node_name, old_param2, param2, for_area)
end
-- For one free neighbor, straight slope
elseif free_neighbors == 1 then
@ -180,7 +204,7 @@ function naturalslopeslib.get_replacement_node(pos, node, area, data, param2_dat
elseif airZP then if is_ground then param2 = 2 else param2 = 6 end
elseif airZM then if is_ground then param2 = 0 else param2 = 8 end
end
return replacement("straight", node_name, pos, param2)
return pick_replacement("straight", node_name, old_param2, param2, for_area)
-- For no free neighbor check for a free diagonal for an inner corner
-- or fully surrounded for a rebuild
else
@ -191,18 +215,18 @@ function naturalslopeslib.get_replacement_node(pos, node, area, data, param2_dat
local param2
if airXPZP and not airXPZM and not airXMZP and not airXMZM then
if is_ground then param2 = 3 else param2 = 15 end
return replacement("ic", node_name, pos, param2)
return pick_replacement("ic", node_name, old_param2, param2, for_area)
elseif not airXPZP and airXPZM and not airXMZP and not airXMZM then
if is_ground then param2 = 0 else param2 = 8 end
return replacement("ic", node_name, pos, param2)
return pick_replacement("ic", node_name, old_param2, param2, for_area)
elseif not airXPZP and not airXPZM and airXMZP and not airXMZM then
if is_ground then param2 = 2 else param2 = 23 end
return replacement("ic", node_name, pos, param2)
return pick_replacement("ic", node_name, old_param2, param2, for_area)
elseif not airXPZP and not airXPZM and not airXMZP and airXMZM then
if is_ground then param2 = 1 else param2 = 17 end
return replacement("ic", node_name, pos, param2)
return pick_replacement("ic", node_name, old_param2, param2, for_area)
else
return replacement('block', node_name, pos)
return pick_replacement('block', node_name, old_param2, 0, for_area)
end
end
end
@ -233,7 +257,7 @@ end
-- @return True if the node was updated, false otherwise.
function naturalslopeslib.update_shape(pos, node)
local replacement = naturalslopeslib.get_replacement_node(pos, node)
if replacement then
if replacement and (replacement.name ~= node.name or node.param2 ~= replacement.param2) then
minetest.set_node(pos, replacement)
return true
else

Loading…
Cancel
Save