Imported from trollstream "ContentDB"

master
OldCoder 2022-09-04 22:03:04 -07:00
commit 64273159dd
19 changed files with 2211 additions and 0 deletions

212
CHANGELOG.md Normal file
View File

@ -0,0 +1,212 @@
Changelog
=========
The semantic of version number is 'Major.minor'. Minor updates are retro-compatible, while major updates may break things.
[1.5] - 2022-03-28
------------------
### Added
- DanRPI added rough slopes 3D models.
- Rough slopes rendering mode, `naturalslopes.setting_rendering_mode` to get selected rendering mode (cubic, smooth or rough).
### Changed
- `naturalslopes.setting_smooth_rendering` is deprecated in favor of `naturalslopeslib.setting_rendering_mode` (see API documentation).
- Settingtype moved from boolean `naturalslopes_smooth_rendering` to enum `naturalslopes_rendering_mode`.
[1.4] - 2022-02-20
------------------
### Added
- `naturalslopeslib.get_all_slopes` to list all shapes except the full block one.
- `Revert` setting to prevent new slopes being generated and turn slopes into full blocks on loading.
### Fixed
- Documentation about `naturalslopeslib.get_all_shapes` that can be called from any shape.
- Warning about mod.conf name entry.
[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
------------------
### Added
- Support for colored nodes (with palette size limitation).
- `naturalslopeslib.propagate_overrides` to remove the need for depedencies.
- Stomp, dig/place and time factor in settings.
### Fixed
- Timed update triggering.
- Some local variable declaration warning.
[1.1] - 2021-02-07
------------------
### Added
- `set_manual_map_generation`.
- `get_slope_defs`.
- Chance factors for different kind of updates.
- Changelog.
### Changed
- Slope update is done last on map generation.
- `is_free*` returns nil when a neighbour node is not available.
- `is_free_for_erosion` is now deprecated, use `is_free_for_shape_update` instead.
- Edges of areas are updated progressively instead of not at all.
[1.0] - 2020-12-30
------------------
Requires Minetest 5.
### Added
- `get_regular_node_name` from a slope name.
- Ceiling slopes.
- Family group for all slopes.
- `get_all_shapes`.
- Progressive map generation method.
- `register_sloped_stomp`.
- Extensive API documentation.
### Removed
- Slope nodes for Minetest Game.
### Changed
- `get_slope_names` return each name indexed by type.
- Namespace change from `naturalslopes` to `naturalslopeslib`.
- Timed update uses `twmlib`.
- Registration is shortened by passing changes from the original definition instead of a full copy.
- Use underscore for domain name in settingstype for consistency with other mods.
[0.9] - 2017-08-30
------------------
### Added
- Backface culling for slope nodes.
- Slope node names are returned upon registration.
- `get_slope_names`.
### Changed
- `default:dirt*` are more likely to be updated.
- `natural_slope` group now indicates the type of slope.
### Fixed
- Registering slopes outside the mod.
[0.8] - 2017-08-25
------------------
### Added
- Reintroduced the smooth rendering, not enabled by default.
### Changed
- Pick a random surface node instead of an area for timed update.
[0.7] - 2017-08-15
------------------
### Added
- `skip` parameter for update_shape.
- `default:clay` and `default:snowblock` slopes.
### Removed
- Smooth rendering.
### Changed
- Updated the description of the mod.
- The ABM transformation is replaced by a random area selection from time to time.
### Fixed
- Some textures for `default` slopes.
[0.6] - 2017-08-12
------------------
### Added
- Pike shape for isolated nodes.
### Changed
- An update is forced when a node is placed above an other one.
### Fixed
- Prevent slopes to propagate with grass for `default`.
[0.5] - 2017-08-09
------------------
### Changed
- Update slope definitions for `default` to drop the full node when slopes are dug.
### Fixed
- Light for slope nodes.
- Updating a slope to an other one.
[0.4] - 2017-08-06
------------------
### Added
- Setting to enable or disable update on map generation.
### Changed
- Nodes on the edge of an area are not updated instead of being updated incorrectly
### Fixed
- Enabling slopes for `default` setting.
- Node definition being shared between slope, erasing some distinctions.
[0.3] - 2017-08-05
------------------
### Added
- Update chance argument for `register_slopes`.
- `register_slopes` uses a node definition instead of a list of definition parameters.
- Slopes for `default:dirt_with_snow`, `default:dirt_with_dry_grass`, `default:dirt_with_rainforest_litter`
- `get_replacement` and `get_replacement_id`
- Update shape on map generation.
### Changed
- `default` is now an optional dependency.
- Full grass texture for slopes.
### Fixed
- Walk advanced settings, chat command.
[0.2] - 2017-07-25
------------------
### Added
- Nodes return to their full block shape whene something is above.
- Cubic shape rendering from `stairs`.
- Update shape on walk with `poschangelib v0.1`.
### Changed
- Use settingstype for options.
[0.1] - 2017-07-21
------------------
Initial release.

52
README.md Normal file
View File

@ -0,0 +1,52 @@
Natural slopes library
======================
* Version 1.5
* 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
shape by themselves according to the surroundings and the material hardness. It creates
more natural looking landscapes and smoothes movements by removing some edges.
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).
See naturalslopeslib_api.txt for the documentation of the API.
## Dependencies
None, this is a standalone library for other mods to build upon. It doesn't
have any effect by itself.
## Optional dependencies:
* `poschangelib`: to enable shape update when walking on nodes
* `twmlib`: to enable update from time to time
## Source code
* Written by Karamel
* Licenced under LGPLv2 or, at your discretion, any later version.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
https://www.gnu.org/licenses/licenses.html#LGPL
## Media
* Models licensed under CC-0.
* Rough slopes models by DanRPI

165
init.lua Normal file
View File

@ -0,0 +1,165 @@
-- Global namespace for functions
naturalslopeslib = {
_register_on_generated = true,
_propagate_overrides = false,
default_definition = {} -- initialized below
}
local poschangelib_available = false
local twmlib_available = false
for _, name in ipairs(minetest.get_modnames()) do
if name == "poschangelib" then
poschangelib_available = true
elseif name == "twmlib" then
twmlib_available = true
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
return nil
end
for _, regex in ipairs({"^(.-:)slope_inner_(.*)$", "^(.-:)slope_outer_(.*)$", "^(.-:)slope_pike_(.*)$", "^(.-:)slope_(.*)$"}) do
local match, match2 = string.match(node_name, regex)
if match and minetest.registered_nodes[match .. match2] ~= nil then
return match .. match2
end
end
return nil
end
--- {Private} Get the default node name for slopes from a subname.
-- For example 'dirt' will be named 'naturalslopeslib:slope_dirt'
-- See naturalslopeslib.get_all_shapes to get the actual node names.
function naturalslopeslib.get_straight_slope_name(subname)
return minetest.get_current_modname() .. ':slope_' .. subname
end
function naturalslopeslib.get_inner_corner_slope_name(subname)
return minetest.get_current_modname() .. ':slope_inner_' .. subname
end
function naturalslopeslib.get_outer_corner_slope_name(subname)
return minetest.get_current_modname() .. ':slope_outer_' .. subname
end
function naturalslopeslib.get_pike_slope_name(subname)
return minetest.get_current_modname() .. ':slope_pike_' .. subname
end
-- Set functions to get configuration and default values
function naturalslopeslib.setting_enable_surface_update()
if not twmlib_available then return false end
local value = minetest.settings:get_bool('naturalslopeslib_enable_surface_update')
if value == nil then return true end
return value
end
function naturalslopeslib.setting_enable_shape_on_walk()
if not poschangelib_available then return false end
local value = minetest.settings:get_bool('naturalslopeslib_enable_shape_on_walk')
if value == nil then return true end
return value
end
function naturalslopeslib.setting_enable_shape_on_generation()
local value = minetest.settings:get_bool('naturalslopeslib_register_default_slopes')
if value == nil then value = true end
return value
end
function naturalslopeslib.setting_generation_method()
local value = minetest.settings:get('naturalslopeslib_generation_method')
if value == nil then value = 'VoxelManip' end
return value
end
function naturalslopeslib.setting_generation_factor()
return tonumber(minetest.settings:get('naturalslopeslib_update_shape_generate_factor')) or 0.05
end
function naturalslopeslib.setting_stomp_factor()
return tonumber(minetest.settings:get('naturalslopeslib_update_shape_stomp_factor')) or 1.0
end
function naturalslopeslib.setting_dig_place_factor()
return tonumber(minetest.settings:get('naturalslopeslib_update_shape_dig_place_factor')) or 1.0
end
function naturalslopeslib.setting_time_factor()
return tonumber(minetest.settings:get('naturalslopeslib_update_shape_time_factor')) or 1.0
end
function naturalslopeslib.setting_generation_skip()
return tonumber(minetest.settings:get('naturalslopeslib_update_shape_generate_skip')) or 0
end
function naturalslopeslib.setting_enable_shape_on_dig_place()
local value = minetest.settings:get_bool('naturalslopeslib_enable_shape_on_dig_place')
if value == nil then value = true end
return value
end
function naturalslopeslib.setting_revert()
local value = minetest.settings:get_bool('naturalslopeslib_revert')
if value == nil then value = false end
return value
end
--- @deprecated, use naturalslopeslib.setting_rendering_mode()
function naturalslopeslib.setting_smooth_rendering()
local mode = naturalslopeslib.setting_rendering_mode()
return (mode == 'Smooth' or mode == 'Rough')
end
function naturalslopeslib.setting_rendering_mode()
local value = minetest.settings:get('naturalslopeslib_rendering_mode')
if value == nil then
-- Backward compatibility, load from naturalslopeslib_smooth_rendering
value = minetest.settings:get_bool('naturalslopeslib_smooth_rendering')
if value == true then
value = 'Smooth'
else
value = 'Cubic'
end
end
if value == nil then value = 'Cubic' end
return value
end
function naturalslopeslib.set_manual_map_generation()
naturalslopeslib._register_on_generated = false
end
function naturalslopeslib.propagate_overrides()
if naturalslopeslib._propagate_overrides then
return
end
naturalslopeslib._propagate_overrides = true
local old_override = minetest.override_item
minetest.override_item = function(name, redefinition)
local shapes = naturalslopeslib.get_all_shapes(name)
if #shapes == 1 then
old_override(name, redefinition)
return
end
local slope_redef = table.copy(redefinition)
-- Prevent slopes fixed attribute override
slope_redef.drawtype = nil
slope_redef.nodebox = nil
slope_redef.mesh = nil
slope_redef.selection_box = nil
slope_redef.collision_box = nil
slope_redef.paramtype = nil
if slope_redef.paramtype2 ~= nil then
if slope_redef.paramtype2 == "color" or slope_redef.paramtype2 == "colorfacedir" then
slope_redef.paramtype2 = "colorfacedir"
else
slope_redef.paramtype2 = "facedir"
end
end
old_override(name, redefinition)
for i=2, #shapes, 1 do
old_override(shapes[i], slope_redef)
end
end
end
dofile(minetest.get_modpath(minetest.get_current_modname()) .. "/update_shape.lua")
-- Include registration methods
dofile(minetest.get_modpath(minetest.get_current_modname()) .. "/register_slopes.lua")
dofile(minetest.get_modpath(minetest.get_current_modname()) .. "/sloped_stomp.lua")

4
mod.conf Normal file
View File

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

View File

@ -0,0 +1,27 @@
# Licensed under CC-0
v -0.5 -0.5 -0.5
v 0.5 -0.5 -0.5
v 0.5 -0.5 0.5
v -0.5 -0.5 0.5
v 0.5 0.5 0.5
v 0.5 0.5 -0.5
v -0.5 0.5 0.5
vt 0 0
vt 0 1
vt 1 0
vt 1 1
vn 0 -1 0
vn 1 0 0
vn 0 0 -1
vn -1 0 0
vn 0 0 1
vn -1 1 -1
vn 1 1 -1
s off
f 1/1/1 2/3/1 3/4/1 4/2/1
f 3/1/2 2/3/2 6/4/2 5/2/2
f 2/1/3 1/3/3 6/4/3
f 1/1/4 4/3/4 7/4/4
f 4/1/5 3/3/5 5/4/5 7/2/5
f 6/2/6 1/1/6 5/4/6
f 5/2/7 1/3/7 7/4/7

View File

@ -0,0 +1,43 @@
# Licensed under CC-0
v -0.5 -0.5 -0.5
v 0.5 -0.5 -0.5
v 0.5 -0.5 0.5
v -0.5 -0.5 0.5
v 0.5 0.5 0.5
v 0.5 0.5 -0.5
v -0.5 0.5 0.5
v -0.1 0.2 0.2
v 0.2 0.2 -0.1
vt 0 0
vt 0 1
vt 1 0
vt 1 1
vt 0.3 0.4
vt 0.7 0.8
vn 0 -1 0
vn 1 0 0
vn 0 0 -1
vn -1 0 0
vn 0 0 1
vn -1 1 -1
vn 1 1 -1
vn 0.5 1 -0.5
vn -0.5 1 0.5
s off
f 1/1/1 2/3/1 3/4/1 4/2/1
f 3/1/2 2/3/2 6/4/2 5/2/2
f 2/1/3 1/3/3 6/4/3
f 1/1/4 4/3/4 7/4/4
f 4/1/5 3/3/5 5/4/5 7/2/5
f 6/4/6 1/3/6 9/6/9
f 5/2/7 6/4/6 9/6/9
f 5/2/7 8/5/8 7/1/7
f 8/5/8 1/3/6 7/1/7
f 5/2/7 9/6/9 8/5/8
f 9/6/9 1/3/6 8/5/8

View File

@ -0,0 +1,21 @@
# Licensed under CC-0
v -0.5 -0.5 -0.5
v 0.5 -0.5 -0.5
v 0.5 -0.5 0.5
v -0.5 -0.5 0.5
v 0.5 0.5 0.5
vt 0 0
vt 0 1
vt 1 0
vt 1 1
vn 0 -1 0
vn 1 0 0
vn 0 1 -1
vn -1 1 0
vn 0 0 1
s off
f 1/1/1 2/3/1 3/4/1 4/2/1
f 3/1/2 2/3/2 5/2/2
f 2/1/3 1/3/3 5/4/3
f 1/1/4 4/3/4 5/4/4
f 4/1/5 3/3/5 5/4/5

View File

@ -0,0 +1,35 @@
# Licensed under CC-0
v -0.5 -0.5 -0.5
v 0.5 -0.5 -0.5
v 0.5 -0.5 0.5
v -0.5 -0.5 0.5
v 0.5 0.5 0.5
v -0.1 -0.2 0.2
v 0.2 -0.2 -0.1
vt 0 0
vt 0 1
vt 1 0
vt 1 1
vt 0.4 0.3
vt 0.8 0.7
vn 0 -1 0
vn 1 0 0
vn 0 1 -1
vn -1 1 0
vn 0 0 1
vn 0.5 1 -0.5
vn -0.5 1 0.5
s off
f 1/1/1 2/3/1 3/4/1 4/2/1
f 3/1/2 2/3/2 5/2/2
f 4/1/5 3/3/5 5/4/5
f 5/2/2 2/1/3 7/6/7
f 2/1/3 1/3/3 7/6/7
f 1/1/4 4/3/4 6/5/6
f 4/3/4 5/2/2 6/5/6
f 5/2/2 7/6/7 6/5/6
f 7/6/7 1/3/3 6/5/6

View File

@ -0,0 +1,21 @@
# Licensed under CC-0
v -0.5 -0.5 -0.5
v 0.5 -0.5 -0.5
v 0.5 -0.5 0.5
v -0.5 -0.5 0.5
v 0 0 0
vt 0 0
vt 0 1
vt 1 0
vt 1 1
vn 0 -1 0
vn 1 0.5 0
vn 0 0.5 -1
vn -1 0.5 0
vn 0 0.5 1
s off
f 1/1/1 2/3/1 3/4/1 4/2/1
f 3/1/2 2/3/2 5/2/2
f 2/1/3 1/3/3 5/4/3
f 1/1/4 4/3/4 5/4/4
f 4/1/5 3/3/5 5/4/5

View File

@ -0,0 +1,37 @@
# Licensed under CC-0
v -0.5 -0.5 -0.5
v 0.5 -0.5 -0.5
v 0.5 -0.5 0.5
v -0.5 -0.5 0.5
v -0.2 0 0
v 0.1 0 -0.15
v 0.1 0 0.15
vt 0 0
vt 0 1
vt 1 0
vt 1 1
vt 0.65 0.68
vt 0.65 0.32
vt 0.22 0.5
vn 0 -1 0
vn 1 0.5 0
vn 0 0.5 -1
vn -1 0.5 0
vn 0 0.5 1
vn 0 1 0
s off
f 1/2/1 2/4/1 3/3/1 4/1/1
f 1/2/4 4/1/4 5/5/6
f 3/3/2 2/4/2 7/7/6
f 2/4/3 1/2/3 6/6/6
f 4/1/5 3/3/5 7/7/6
f 1/2/4 5/5/6 6/6/6
f 4/1/5 7/7/6 5/5/6
f 2/4/3 6/6/6 7/7/6
f 5/5/6 7/7/6 6/6/6

View File

@ -0,0 +1,22 @@
# Licensed under CC-0
v -0.5 -0.5 -0.5
v 0.5 -0.5 -0.5
v 0.5 -0.5 0.5
v -0.5 -0.5 0.5
v 0.5 0.5 0.5
v -0.5 0.5 0.5
vt 0 0
vt 0 1
vt 1 0
vt 1 1
vn 0 -1 0
vn 1 0 0
vn -1 0 0
vn 0 0 1
vn 0 1 1
s off
f 1/1/1 2/3/1 3/4/1 4/2/1
f 1/1/3 4/3/3 6/4/3
f 3/1/2 2/3/2 5/2/2
f 2/1/4 1/3/4 6/4/4 5/2/4
f 4/1/5 3/3/5 5/4/5 6/2/5

View File

@ -0,0 +1,37 @@
# Licensed under CC-0
v -0.5 -0.5 -0.5
v 0.5 -0.5 -0.5
v 0.5 -0.5 0.5
v -0.5 -0.5 0.5
v 0.5 0.5 0.5
v -0.5 0.5 0.5
v -0.2 0.28 0.12
v 0.2 -0.28 -0.12
vt 0 0
vt 0 1
vt 1 0
vt 1 1
vt 0.25 0.65
vt 0.65 0.25
vn 0 -1 0
vn 1 0 0
vn -1 0 0
vn 0 0 1
vn 0 1 1
vn 0.15 0.4 -0.4
vn -0.15 0.4 -0.4
s off
f 1/1/1 2/3/1 3/4/1 4/2/1
f 1/1/3 4/3/3 6/4/3
f 3/1/2 2/3/2 5/2/2
f 4/1/5 3/3/5 5/4/5 6/2/5
f 1/1/3 6/2/3 7/5/5
f 1/1/7 8/6/5 2/3/7
f 2/3/2 8/6/5 5/4/2
f 5/4/7 7/5/5 6/2/7
f 5/4/6 8/6/5 7/5/5
f 8/6/5 1/1/6 7/5/5

396
naturalslopeslib_api.txt Normal file
View File

@ -0,0 +1,396 @@
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_rendering_mode
-- 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_rendering_mode()
* Returns `Cubic`, `Smooth` or `Rough`. This setting is read only when registering nodes and may not reflect the actual value if it was changed while the server is running.
* Was previously `naturalslopeslib.setting_smooth_rendering`, that is now deprecated but still available and returns `true` for `Smooth` and `Rough` modes.
Chat commands
-------------
### /updshape
* requires `server` privilege.
* Force updating the node the player is standing upon.

476
register_slopes.lua Normal file
View File

@ -0,0 +1,476 @@
-- 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 source_list = {}
local replacements = {}
local replacement_ids = {}
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
local oc_name = nil
local pike_name = nil
if fixed_replacements then
straight_name = fixed_replacements[1]
ic_name = fixed_replacements[2]
oc_name = fixed_replacements[3]
pike_name = fixed_replacements[4]
else
straight_name = naturalslopeslib.get_straight_slope_name(subname)
ic_name = naturalslopeslib.get_inner_corner_slope_name(subname)
oc_name = naturalslopeslib.get_outer_corner_slope_name(subname)
pike_name = naturalslopeslib.get_pike_slope_name(subname)
end
local source_id = minetest.get_content_id(source_name)
local straight_id = minetest.get_content_id(straight_name)
local ic_id = minetest.get_content_id(ic_name)
local oc_id = minetest.get_content_id(oc_name)
local pike_id = minetest.get_content_id(pike_name)
-- Full to slopes
local dest_data = {
source = source_name,
straight = straight_name,
inner = ic_name,
outer = oc_name,
pike = pike_name,
chance = update_chance,
chance_factors = chance_factors,
_colored_source = colored_source,
_color_convert = color_convert
}
local dest_data_id = {
source = source_id,
straight = straight_id,
inner = ic_id,
outer = oc_id,
pike = pike_id,
chance = update_chance,
chance_factors = chance_factors,
_colored_source = colored_source,
_color_convert = color_convert
}
table.insert(source_list, source_name)
-- Block
replacements[source_name] = dest_data
replacement_ids[source_id] = dest_data_id
-- Straight
replacements[straight_name] = dest_data
replacement_ids[straight_id] = dest_data_id
-- Inner
replacements[ic_name] = dest_data
replacement_ids[ic_id] = dest_data_id
-- Outer
replacements[oc_name] = dest_data
replacement_ids[oc_id] = dest_data_id
-- Pike
replacements[pike_name] = dest_data
replacement_ids[pike_id] = dest_data_id
end
--- Get the list of nodes in block shape that have slopes registered for.
function naturalslopeslib.list_registered_nodes()
return table.copy(source_list)
end
--- Get replacement description of a node.
-- Contains replacement names in either source or (straight, inner, outer)
-- and chance.
function naturalslopeslib.get_replacement(source_node_name)
return replacements[source_node_name]
end
--- Get replacement description of a node by content id for VoxelManip.
-- Contains replacement ids in either source or (straight, inner, outer)
-- and chance.
function naturalslopeslib.get_replacement_id(source_id)
return replacement_ids[source_id]
end
function naturalslopeslib.get_all_shapes(source_node_name)
if replacements[source_node_name] then
local rp = replacements[source_node_name]
return {rp.source, rp.straight, rp.inner, rp.outer, rp.pike}
else
return {source_node_name}
end
end
function naturalslopeslib.get_all_slopes(source_node_name)
if replacements[source_node_name] then
local rp = replacements[source_node_name]
return {rp.straight, rp.inner, rp.outer, rp.pike}
else
return {}
end
end
--[[ Bounding boxes
--]]
local slope_straight_box = {
type = "fixed",
fixed = {
{-0.5, -0.5, -0.5, 0.5, 0, 0.5},
{-0.5, 0, 0, 0.5, 0.5, 0.5},
},
}
local slope_inner_corner_box = {
type = "fixed",
fixed = {
{-0.5, -0.5, -0.5, 0.5, 0, 0.5},
{-0.5, 0, 0, 0.5, 0.5, 0.5},
{-0.5, 0, -0.5, 0, 0.5, 0},
},
}
local slope_outer_corner_box = {
type = "fixed",
fixed = {
{-0.5, -0.5, -0.5, 0.5, 0, 0.5},
{-0.5, 0, 0, 0, 0.5, 0.5},
},
}
local slope_pike_box = {
type = "fixed",
fixed = {
{-0.5, -0.5, -0.5, 0.5, 0, 0.5},
},
}
local function apply_default_slope_def(base_node_name, node_def, slope_group_value)
node_def.paramtype = 'light'
if node_def.paramtype2 == 'color' or node_def.paramtype2 == 'colorfacedir' then
node_def.paramtype2 = 'colorfacedir'
else
node_def.paramtype2 = 'facedir'
end
if not node_def.groups then node_def.groups = {} end
node_def.groups.natural_slope = slope_group_value
if not node_def.groups["family:" .. base_node_name] then
node_def.groups["family:" .. base_node_name] = 1
end
return node_def
end
--- {Private} Update the node definition for a straight slope
local function get_straight_def(base_node_name, node_def)
node_def = apply_default_slope_def(base_node_name, node_def, 1)
local rendering = naturalslopeslib.setting_rendering_mode()
if rendering == 'Smooth' then
node_def.drawtype = 'mesh'
node_def.mesh = 'naturalslopeslib_straight.obj'
elseif rendering == 'Rough' then
node_def.drawtype = 'mesh'
node_def.mesh = 'naturalslopeslib_straight_rough.obj'
else
node_def.drawtype = 'nodebox'
node_def.node_box = slope_straight_box
end
node_def.selection_box = slope_straight_box
node_def.collision_box = slope_straight_box
return node_def
end
--- {Private} Update the node definition for an inner corner
local function get_inner_def(base_node_name, node_def)
node_def = apply_default_slope_def(base_node_name, node_def, 2)
local rendering = naturalslopeslib.setting_rendering_mode()
if rendering == 'Smooth' then
node_def.drawtype = 'mesh'
node_def.mesh = 'naturalslopeslib_inner.obj'
elseif rendering == 'Rough' then
node_def.drawtype = 'mesh'
node_def.mesh = 'naturalslopeslib_inner_rough.obj'
else
node_def.drawtype = 'nodebox'
node_def.node_box = slope_inner_corner_box
end
node_def.selection_box = slope_inner_corner_box
node_def.collision_box = slope_inner_corner_box
return node_def
end
--- {Private} Update the node definition for an outer corner
local function get_outer_def(base_node_name, node_def)
node_def = apply_default_slope_def(base_node_name, node_def, 3)
local rendering = naturalslopeslib.setting_rendering_mode()
if rendering == 'Smooth' then
node_def.drawtype = 'mesh'
node_def.mesh = 'naturalslopeslib_outer.obj'
elseif rendering == 'Rough' then
node_def.drawtype = 'mesh'
node_def.mesh = 'naturalslopeslib_outer_rough.obj'
else
node_def.drawtype = 'nodebox'
node_def.node_box = slope_outer_corner_box
end
node_def.selection_box = slope_outer_corner_box
node_def.collision_box = slope_outer_corner_box
return node_def
end
--- {Private} Update the node definition for a pike
local function get_pike_def(base_node_name, node_def, update_chance)
node_def = apply_default_slope_def(base_node_name, node_def, 4)
if naturalslopeslib.setting_smooth_rendering() then
node_def.drawtype = 'mesh'
node_def.mesh = 'naturalslopeslib_pike.obj'
else
node_def.drawtype = 'nodebox'
node_def.node_box = slope_pike_box
end
node_def.selection_box = slope_pike_box
node_def.collision_box = slope_pike_box
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
minetest.log("error", "Trying to get slopes for an unknown node " .. (base_node_name or "nil"))
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
else
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 {
get_straight_def(base_node_name, table.copy(full_copy)),
get_inner_def(base_node_name, table.copy(full_copy)),
get_outer_def(base_node_name, table.copy(full_copy)),
get_pike_def(base_node_name, table.copy(full_copy))
}
end
local function default_factors(factors)
local f = {}
if factors == nil then factors = {} end
for _, name in ipairs({"mapgen", "time", "stomp", "place"}) do
if factors[name] ~= nil then
f[name] = factors[name]
else
f[name] = 1
end
end
return f
end
--- Register slopes from a full block node.
-- @param base_node_name: The full block node name.
-- @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, color_convert)
if not update_chance then
minetest.log('error', 'Natural slopes: chance is not set for node ' .. base_node_name)
return
end
local base_node_def = minetest.registered_nodes[base_node_name]
if not base_node_def then
minetest.log("error", "Trying to register slopes for an unknown node " .. (base_node_name or "nil"))
return
end
local chance_factors = default_factors(factors)
-- Get new definitions
local subname = string.sub(base_node_name, string.find(base_node_name, ':') + 1)
local slope_names = {
naturalslopeslib.get_straight_slope_name(subname),
naturalslopeslib.get_inner_corner_slope_name(subname),
naturalslopeslib.get_outer_corner_slope_name(subname),
naturalslopeslib.get_pike_slope_name(subname)
}
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
minetest.register_node(name, slope_defs[i])
-- Register walk listener
if naturalslopeslib.setting_enable_shape_on_walk() then
poschangelib.register_stomp(name,
naturalslopeslib.update_shape_on_walk,
{name = name .. '_upd_shape',
chance = update_chance * chance_factors.stomp * stomp_factor, priority = 500})
end
end
-- Register replacements
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() and not naturalslopeslib.setting_revert() then
poschangelib.register_stomp(base_node_name,
naturalslopeslib.update_shape_on_walk,
{name = base_node_name .. '_upd_shape',
chance = update_chance * chance_factors.stomp * stomp_factor, priority = 500})
end
-- Enable surface update
local time_factor = naturalslopeslib.setting_time_factor()
if naturalslopeslib.setting_enable_surface_update() and not naturalslopeslib.setting_revert() then
twmlib.register_twm({
nodenames = {base_node_name, slope_defs[1], slope_defs[2], slope_defs[3], slope_defs[4]},
chance = update_chance * chance_factors.time * time_factor,
action = naturalslopeslib.update_shape
})
end
-- Enable revert LBM
if naturalslopeslib.setting_revert() then
minetest.register_lbm({
label = 'naturalslopes_revert',
name = minetest.get_current_modname() .. ':revert_slopes_' .. string.gsub(base_node_name, ':', '_'),
nodenames = slope_names,
run_at_every_load = true,
action = function (pos, node)
minetest.swap_node(pos, { name = base_node_name })
end
})
end
return naturalslopeslib.get_replacement(base_node_name)
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, color_convert)
-- Defensive checks
if not minetest.registered_nodes[base_node_name] then
if not base_node_name then
minetest.log('error', 'naturalslopeslib.set_slopes failed: base node_name is nil.')
else
minetest.log('error', 'naturalslopeslib.set_slopes failed: ' .. base_node_name .. ' is not registered.')
end
return
end
if not minetest.registered_nodes[straight_name]
or not minetest.registered_nodes[inner_name]
or not minetest.registered_nodes[outer_name]
or not minetest.registered_nodes[pike_name] then
minetest.log('error', 'naturalslopeslib.set_slopes failed: one of the slopes for ' .. base_node_name .. ' is not registered.')
return
end
if not update_chance then
minetest.log('error', 'Natural slopes: chance is not set for node ' .. base_node_name)
return
end
local chance_factors = default_factors(factors)
-- Set shape update data
local slope_names = {straight_name, inner_name, outer_name, pike_name}
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() and not naturalslopeslib.setting_revert() then
local time_factor = naturalslopeslib.setting_time_factor()
twmlib.register_twm({
nodenames = {base_node_name, straight_name, inner_name, outer_name, pike_name},
chance = update_chance * chance_factors.time * time_factor,
action = naturalslopeslib.update_shape
})
end
-- Set walk listener for the 5 nodes
if naturalslopeslib.setting_enable_shape_on_walk() and not naturalslopeslib.setting_revert() then
local stomp_factor = naturalslopeslib.setting_stomp_factor()
local stomp_desc = {name = base_node_name .. '_upd_shape',
chance = update_chance * chance_factors.stomp * stomp_factor, priority = 500}
poschangelib.register_stomp(base_node_name, naturalslopeslib.update_shape_on_walk, stomp_desc)
for i, name in pairs(slope_names) do
poschangelib.register_stomp(name, naturalslopeslib.update_shape_on_walk, stomp_desc)
end
end
return naturalslopeslib.get_replacement(base_node_name)
end

0
revert.lua Normal file
View File

BIN
screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

63
settingtypes.txt Normal file
View File

@ -0,0 +1,63 @@
## Update shape ABM
# Enable or disable the automatic update of the shape of nodes at the surface (requires twmlib mod).
naturalslopeslib_enable_surface_update (Timed surface update) bool true
## Update shape on walk
# Enable or disable shape update when walking on blocks (requires poschangelib mod).
naturalslopeslib_enable_shape_on_walk (Enable shaping by walking on edges) bool true
## Update shape on world generation
# Enable or disable shape update when the world is generated.
# This will probably put a lot of pressure on the server. Hopefully it doesn't happen that frequently.
naturalslopeslib_enable_shape_on_generate (Shape update on generation) bool true
# Define which method is used when generating a new area.
# - VoxelManip (default) is the most efficient one, but the area will be available only once it is completely updated and may be resource intensive. This is the preferred method as map generation is optimized not to impact the game.
# - Progressive is way much slower, but the area is available directly in it's untransformed shape. The nodes will be updated one after the other while the game is rather idle. This method is preferred on old single core CPU that cannot benefit from map generation optimization, if the areas are too long to emerge or if you like to see mountains smoothing themselves progressively.
naturalslopeslib_generation_method (Generation method) enum VoxelManip VoxelManip,Progressive
# Inverted chance factor on generation. The lesser, the more likely the node will be
# generated in their updated shape. 0.1 means 10 times more likely.
naturalslopeslib_update_shape_generate_factor (Generation factor) float 0.05 0
# Inverted chance factor when stomping. The lesser, the more likely the node will be
# updated when walking on it. 0.1 means 10 times more likely.
naturalslopeslib_update_shape_stomp_factor (Stomp factor) float 1.0 0
# Inverted chance factor on digging/placing. The lesser, the more likely the
# nodes will be updated when the neighbour nodes change. 0.1 means 10 times
# more likely.
naturalslopeslib_update_shape_dig_place_factor (Dig/place factor) float 1.0 0
# Inverted chance factor on time. The lesser, the more likely the node will be
# updated with time. 0.1 means 10 times more likely.
naturalslopeslib_update_shape_time_factor (Time factor) float 1.0 0
# Skip n nodes when generating an area. The higher, the less nodes can be updated but it dramatically saves server resources.
naturalslopeslib_update_shape_generate_skip (Generation skip nodes) int 0 0
## Update shape on dig and place
# Enable or disable shape update of neighbor nodes when one is placed or updated.
naturalslopeslib_enable_shape_on_dig_place (Shape update on dig and place) bool true
## Revert changes
# Prevent new updates and revert slopes to their original block shape when they are loaded.
# Activate this function if you would like to deactivate natural slopes in existing worlds, but leave all the slopes mods loaded to prevent unknown nodes to appearing until all revert is done.
# Revert cannot be undone once blocks are loaded.
naturalslopeslib_revert (Revert) bool false
## Rendering
# Rendering mode for slopes. It only affects the visuals. The collision boxes are always matching the cubic style.
# Anything different from Cubic will shows sharp discontinuities between slopes and non-sloped nodes, and will mislead players because the bottom part of a slope is in fact half a node higher than it looks like.
# - Cubic (default) divides blocs in 8 and give stair-like look
# - Smooth uses smooth triangles.
# - Rough shows more accidented slopes.
naturalslopeslib_rendering_mode (Rendering mode) enum Cubic Cubic,Smooth,Rough

23
sloped_stomp.lua Normal file
View File

@ -0,0 +1,23 @@
if minetest.get_modpath("poschangelib") == nil then
-- Register the function that does nothing to prevent crashes
naturalslopeslib.register_sloped_stomp = function(source_node_name, dest_node_name, stomp_desc)
end
else
naturalslopeslib.register_sloped_stomp = function(source_node_name, dest_node_name, stomp_desc)
local source_slopes = naturalslopeslib.get_replacement(source_node_name)
local dest_slopes = naturalslopeslib.get_replacement(dest_node_name)
if source_slopes == nil then
minetest.log("warning", "[register_sloped_stomp] No slope registered for " .. source_node_name)
return
end
if dest_slopes == nil then
minetest.log("warning", "[register_sloped_stomp] No slope registered for " .. dest_node_name)
return
end
for _, slope_type in pairs({"source", "straight", "inner", "outer", "pike"}) do
poschangelib.register_stomp(source_slopes[slope_type], dest_slopes[slope_type], stomp_desc)
end
end
end -- if mod exists end

577
update_shape.lua Normal file
View File

@ -0,0 +1,577 @@
--[[
Describes the falling/eroding effect for slopes
--]]
--[[
Pick replacement, node and area
--]]
-- 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 (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
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
dest_node_name = replacement.straight
elseif slope_type == 'ic' and replacement.inner then
dest_node_name = replacement.inner
elseif slope_type == 'oc' and replacement.outer then
dest_node_name = replacement.outer
end
if dest_node_name then
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
--[[
Surrounding checks and get replacement
--]]
--- Check if a node is considered empty to switch shape.
-- @param pos The position to check
function naturalslopeslib.is_free_for_shape_update(pos)
if not pos then return nil end
local node = minetest.get_node_or_nil(pos)
if node == nil then
return nil
end
return node.name == 'air'
end
local air_id = minetest.get_content_id('air')
function naturalslopeslib.area_is_free_for_shape_update(area, data, index)
if not area:containsi(index) then
return nil
end
return data[index] == air_id
end
-- Deprecated name
naturalslopeslib.area_is_free_for_erosion = naturalslopeslib.area_is_free_for_shape_update
--- Get the replacement node according to it's surroundings.
-- @param pos The position of the node or index with VoxelArea.
-- @param node The node at that position or content id with VoxelArea.
-- @param area The VoxelArea, nil for single position update.
-- @param data Data from VoxelManip, nil for single position update.
-- @param param2_data Param2 data from VoxelManip, nil for single position update.
-- @return A node to use with minetest.set_node
-- or a table with id and param2_data if called with an area.
-- Nil if no replacement is found or a neighbour cannot be read.
function naturalslopeslib.get_replacement_node(pos, node, area, data, param2_data)
-- Set functions and data according to update mode: single or VoxelManip
local is_free = nil
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
new_pos = function(add) -- Get new index from current with add position
local area_pos = area:position(pos)
return area:indexp(vector.add(area_pos, add))
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
node_name = node.name
old_param2 = node.param2
end
local is_ground -- ground or ceiling node
local pointing_y = -1
-- If there's something above and below, get back to full block
local above_free = is_free(new_pos({x=0, y=1, z=0}))
local below_free = is_free(new_pos({x=0, y=-1, z=0}))
if above_free == nil or below_free == nil then
return nil
end
if above_free and not below_free then
is_ground = true
pointing_y = 1
elseif below_free and not above_free then
is_ground = false
pointing_y = 5
else -- nothing below and above
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}))
if airXP == nil then return nil end
local airXM = is_free(new_pos({x=-1, y=0, z=0}))
if airXM == nil then return nil end
local airZP = is_free(new_pos({x=0, y=0, z=1}))
if airZP == nil then return nil end
local airZM = is_free(new_pos({x=0, y=0, z=-1}))
if airZM == nil then return nil end
local free_neighbors = 0
for index, free in next, {airXP, airXM, airZP, airZM} do
if free then free_neighbors = free_neighbors + 1 end
end
-- For four or three free neighbors, pike (slab)
if free_neighbors == 4 or free_neighbors == 3 then
local param2 = 0
if is_ground == false then param2 = 20 end
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 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 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 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 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 pick_replacement("oc", node_name, old_param2, param2, for_area)
end
-- For one free neighbor, straight slope
elseif free_neighbors == 1 then
local param2 = 0
if airXP then if is_ground then param2 = 3 else param2 = 15 end
elseif airXM then if is_ground then param2 = 1 else param2 = 17 end
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 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
local airXPZP = is_free(new_pos({x=1, y=0, z=1}))
local airXPZM = is_free(new_pos({x=1, y=0, z=-1}))
local airXMZP = is_free(new_pos({x=-1, y=0, z=1}))
local airXMZM = is_free(new_pos({x=-1, y=0, z=-1}))
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 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 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 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 pick_replacement("ic", node_name, old_param2, param2, for_area)
else
return pick_replacement('block', node_name, old_param2, 0, for_area)
end
end
end
--[[
Do the replacement
--]]
-- Do shape update when random roll passes on a single node.
function naturalslopeslib.chance_update_shape(pos, node, factor, type)
if factor == nil then factor = 1 end
local replacement = naturalslopeslib.get_replacement(node.name)
if not replacement then return false end
local chance_factor = 1
if type == "mapgen" or type == "stomp" or type == "place" or type == "time" then
chance_factor = replacement.chance_factors[type]
end
if (math.random() * (replacement.chance * factor * chance_factor)) < 1.0 then
return naturalslopeslib.update_shape(pos, node)
end
return false
end
--- Try to update the shape of a node according to it's surroundings.
-- @param pos The position of the node.
-- @param node The node at that position.
-- @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 and (replacement.name ~= node.name or node.param2 ~= replacement.param2) then
minetest.set_node(pos, replacement)
return true
else
return false
end
end
local function get_edges(minp, maxp)
-- corner000 = minp
local corner001 = {x = minp.x, y = minp.y, z = maxp.z}
local corner010 = {x = minp.x, y = maxp.y, z = minp.z}
local corner011 = {x = minp.x, y = maxp.y, z = maxp.z}
local corner100 = {x = maxp.x, y = minp.y, z = minp.z}
local corner101 = {x = maxp.x, y = minp.y, z = maxp.z}
local corner110 = {x = maxp.x, y = maxp.y, z = minp.z}
-- corner111 = maxp
return { -- min pos, max pos, normal[x, y ,z]
-- The 8 corners
{minp, minp, {-1, -1, -1}},
{corner001, corner001, {-1, -1, 1}},
{corner010, corner010, {-1, 1, -1}},
{corner011, corner011, {-1, 1, 1}},
{corner100, corner100, { 1, -1, -1}},
{corner101, corner101, { 1, -1, 1}},
{corner110, corner110, { 1, 1, -1}},
{maxp, maxp, { 1, 1, 1}},
-- The 8 segments
{{x = minp.x + 1, y = minp.y, z = minp.z}, {x = maxp.x - 1, y = minp.y, z = minp.z}, { 0, -1, -1}},
{{x = minp.x + 1, y = maxp.y, z = minp.z}, {x = maxp.x - 1, y = maxp.y, z = minp.z}, { 0, 1, -1}},
{{x = minp.x, y = minp.y + 1, z = minp.z}, {x = minp.x, y = maxp.y - 1, z = minp.z}, {-1, 0, -1}},
{{x = maxp.x, y = minp.y + 1, z = minp.z}, {x = maxp.x, y = maxp.y - 1, z = minp.z}, { 1, 0, -1}},
{{x = minp.x + 1, y = minp.y, z = maxp.z}, {x = maxp.x - 1, y = minp.y, z = maxp.z}, { 0, -1, 1}},
{{x = minp.x + 1, y = maxp.y, z = maxp.z}, {x = maxp.x - 1, y = maxp.y, z = maxp.z}, { 0, 1, 1}},
{{x = minp.x, y = minp.y + 1, z = maxp.z}, {x = minp.x, y = maxp.y - 1, z = maxp.z}, { -1, 0, 1}},
{{x = maxp.x, y = minp.y + 1, z = maxp.z}, {x = maxp.x, y = maxp.y - 1, z = maxp.z}, { 1, 0, 1}},
-- The 6 faces
{{x = minp.x + 1, y = minp.y, z = minp.z + 1}, {x = maxp.x - 1, y = minp.y, z = maxp.z - 1}, { 0, -1, 0}},
{{x = minp.x + 1, y = maxp.y, z = minp.z + 1}, {x = maxp.x - 1, y = maxp.y, z = maxp.z - 1}, { 0, 1, 0}},
{{x = minp.x, y = minp.y + 1, z = minp.z + 1}, {x = minp.x, y = maxp.y - 1, z = maxp.z - 1}, { -1, 0, 0}},
{{x = maxp.x, y = minp.y + 1, z = minp.z + 1}, {x = maxp.x, y = maxp.y - 1, z = maxp.z - 1}, { 1, 0, 0}},
{{x = minp.x + 1, y = minp.y + 1, z = minp.z}, {x = maxp.x - 1, y = maxp.y - 1, z = minp.z}, { 0, 0, -1}},
{{x = minp.x + 1, y = minp.y + 1, z = maxp.z}, {x = maxp.x - 1, y = maxp.y - 1, z = maxp.z}, { 0, 0, 1}}
}
end
--- Massive shape update with VoxelManip.
-- @param minp Lower boundary of area.
-- @param mapx Higher boundary of area.
-- @param factor Factor for chance (0.1 means 10 times more likely to update)
-- @param skip (optional) Don't parse all nodes, skip randomly skip/2 to skip nodes
-- @param progressive_edges (optional) When true, edges are generated progressively (default)
-- @param type (optional) Transformation type for chance factor.
-- at every loop.
function naturalslopeslib.area_chance_update_shape(minp, maxp, factor, skip, progressive_edges, type)
if not skip then skip = 0 end
if progressive_edges == nil then progressive_edges = true end
-- Run on every block
local vm, emin, emax = minetest.get_voxel_manip()
local e1, e2 = vm:read_from_map(minp, maxp)
local area = VoxelArea:new{MinEdge = e1, MaxEdge = e2}
local data = vm:get_data()
local param2_data = vm:get_param2_data()
local i = area:indexp(e1)
local imax = area:indexp(e2)
if progressive_edges then
local edges = get_edges(minp, maxp)
for _, edge in ipairs(edges) do
naturalslopeslib.register_progressive_area_update(edge[1], edge[2], factor, skip, type, {x = edge[3][1], y = edge[3][2], z = edge[3][3]})
end
end
while i <= imax do
local x = (i-1) % area.ystride
local y = (i-1) % area.zstride
if x == 0 or x == area.ystride - 1
or y == 0 or y == area.zstride - 1 then
-- Skip edges
else
local replacement = naturalslopeslib.get_replacement_id(data[i])
if replacement ~= nil then
local chance_factor = 1
if type == "mapgen" or type == "stomp" or type == "place" or type == "time" then
chance_factor = replacement.chance_factors[type]
end
if math.random() * (replacement.chance * factor * chance_factor) < 1.0 then
local new_data = naturalslopeslib.get_replacement_node(i, data[i], area, data, param2_data)
if new_data then
data[i] = new_data.id
if new_data.param2_data then
param2_data[i] = new_data.param2_data
end
end
end
end
end
i = i + 1 + math.random(skip / 2, skip)
end
vm:set_data(data)
vm:set_param2_data(param2_data)
vm:write_to_map()
end
naturalslopeslib.progressive_area_updates = {}
function naturalslopeslib.register_progressive_area_update(minp, maxp, factor, skip, type, edge_normal)
if edge_normal ~= nil or minp.x == maxp.x or minp.y == maxp.y or minp.z == maxp.z then
-- Explicit edge or ignored
table.insert(naturalslopeslib.progressive_area_updates, {minp = minp, maxp = maxp,
factor = factor, skip = skip, i = 1, edge_normal = edge_normal})
return
end
-- else register the inner cube and all edges
-- The inner cube
table.insert(naturalslopeslib.progressive_area_updates, {
minp = vector.add(minp, 1),
maxp = vector.add(maxp, -1),
factor = factor, skip = skip, i = 1, edge_normal = nil})
local edges = get_edges(minp, maxp)
-- Register
for _, edge in ipairs(edges) do
table.insert(naturalslopeslib.progressive_area_updates, {
minp = edge[1], maxp = edge[2],
factor = factor, type = type, skip = skip, i = 1,
edge_normal = {x = edge[3][1], y = edge[3][2], z = edge[3][3]}
})
end
end
local function check_area_edges(area)
if area.edge_normal == nil then
return true
end
local edge = area.edge_normal
local pos = area.minp
local requirements = math.abs(edge.x) + math.abs(edge.y) + math.abs(edge.z)
local found = 0
if edge.x ~= 0 then
if minetest.get_node_or_nil(vector.add(pos, {x = edge.x, y = 0, z = 0})) ~= nil then
found = found + 1
end
end
if edge.y ~= 0 then
if minetest.get_node_or_nil(vector.add(pos, {x = 0, y = edge.y, z = 0})) ~= nil then
found = found + 1
end
end
if edge.z ~= 0 then
if minetest.get_node_or_nil(vector.add(pos, {x = 0, y = 0, z = edge.z})) ~= nil then
found = found + 1
end
end
return found == requirements
end
local function progressive_area_update(start_time)
if #naturalslopeslib.progressive_area_updates == 0 then
return true
end
if start_time == nil then
start_time = os.clock()
end
-- pick an area around a player at random and process it
local players = minetest.get_connected_players()
local processed_area_index = nil
local alt_processed_area_index = nil
for area_index, area in ipairs(naturalslopeslib.progressive_area_updates) do
for _, p in ipairs(players) do
local minp = area.minp
local maxp = area.maxp
local ppos = p:get_pos()
if ppos.x >= minp.x and ppos.x <= maxp.x and ppos.y >= minp.y and ppos.y <= maxp.y and ppos.z >= minp.z and ppos.z <= maxp.z then
-- Prefer an area in which a player is
if (check_area_edges(area)) then
processed_area_index = area_index
break
end
elseif alt_processed_area_index == nil and ppos.x + 16 >= minp.x and ppos.x - 16 <= maxp.x and ppos.y + 16 >= minp.y and ppos.y - 16 <= maxp.y and ppos.z + 16 >= minp.z and ppos.z - 16 <= maxp.z then
-- Else pick an area near a player
if (check_area_edges(area)) then
alt_processed_area_index = area_index
end
end
end
if processed_area_index ~= nil then
local area = naturalslopeslib.progressive_area_updates[processed_area_index]
end
end
if processed_area_index == nil then
if alt_processed_area_index ~= nil then
processed_area_index = alt_processed_area_index
else
processed_area_index = 1 -- try to reduce the queue as fast as possible
end
end
local area = naturalslopeslib.progressive_area_updates[processed_area_index]
local i = area.i
local y_size = area.maxp.y - area.minp.y + 1
local z_size = area.maxp.z - area.minp.z + 1
local imax = y_size * z_size * (area.maxp.x - area.minp.x + 1)
while i <= imax do
local x = math.floor((i - 1) / (y_size * z_size))
local y = math.floor((i - 1) / z_size) % y_size
local z = (i - 1) % (z_size)
local pos = {x = area.minp.x + x, y = area.minp.y + y, z = area.minp.z + z}
local node = minetest.get_node(pos)
naturalslopeslib.chance_update_shape(pos, node, area.factor, area.type)
i = i + 1 + math.random(area.skip / 2, area.skip)
if (os.clock() - start_time) > 0.1 and i <= imax then
area.i = i
return false
end
end
table.remove(naturalslopeslib.progressive_area_updates, processed_area_index)
if os.clock() - start_time < 0.1 then
progressive_area_update(start_time)
end
return true
end
local generation_dtime = 0
local function generation_globalstep(dtime)
generation_dtime = generation_dtime + dtime
if generation_dtime > 0.1 then
progressive_area_update()
generation_dtime = 0
end
end
minetest.register_globalstep(generation_globalstep)
minetest.register_on_shutdown(function()
if #naturalslopeslib.progressive_area_updates > 0 then
minetest.log("info", "Processing slope generation for queued areas")
for i, area in ipairs(naturalslopeslib.progressive_area_updates) do
minetest.log("info", (#naturalslopeslib.progressive_area_updates - i + 1) .. " remaining area(s)")
naturalslopeslib.area_chance_update_shape(area.minp, area.maxp, area.factor, area.skip, false, area.type)
end
end
end)
--[[
Triggers registration
--]]
-- Stomp function to get the replacement node name
function naturalslopeslib.update_shape_on_walk(player, pos, node, desc, trigger_meta)
return naturalslopeslib.get_replacement_node(pos, node)
end
-- Chat command
minetest.register_chatcommand('updshape', {
func = function(name, param)
local player = minetest.get_player_by_name(name)
if not player then return false, 'Player not found' end
if not minetest.check_player_privs(player, {server=true}) then return false, 'Update shape requires server privileges' end
local pos = player:get_pos()
local node_pos = {['x'] = pos.x, ['y'] = pos.y - 1, ['z'] = pos.z}
local node = minetest.get_node(node_pos)
if naturalslopeslib.update_shape(node_pos, node) then
return true, 'Shape updated.'
end
return false, node.name .. " cannot have it's shape updated."
end,
})
-- On generation big update
local function register_on_generation()
if not naturalslopeslib._register_on_generated then
return
end
if naturalslopeslib.setting_enable_shape_on_generation() then
if naturalslopeslib.setting_generation_method() == "Progressive" then
minetest.register_on_generated(function(minp, maxp, seed)
naturalslopeslib.register_progressive_area_update(minp, maxp, naturalslopeslib.setting_generation_factor(), naturalslopeslib.setting_generation_skip(), "mapgen")
end)
else
minetest.register_on_generated(function(minp, maxp, seed)
naturalslopeslib.area_chance_update_shape(minp, maxp, naturalslopeslib.setting_generation_factor(), naturalslopeslib.setting_generation_skip(), true, "mapgen")
end)
end
end
end
if not naturalslopeslib.setting_revert() then
minetest.register_on_mods_loaded(register_on_generation)
end
--- On place neighbor update
local function on_place_or_dig(pos, force_below)
local function update(pos, x, y, z, factor)
local new_pos = vector.add(pos, vector.new(x, y, z))
naturalslopeslib.chance_update_shape(new_pos, minetest.get_node(new_pos), factor, "place")
end
-- Update 8 neighbors plus above and below
local place_factor = naturalslopeslib.setting_dig_place_factor()
update(pos, 0, 0, 0, place_factor)
update(pos, 1, 0, 0, place_factor)
update(pos, 0, 0, 1, place_factor)
update(pos, -1, 0, 0, place_factor)
update(pos, 0, 0, -1, place_factor)
update(pos, 1, 0, 1, place_factor)
update(pos, 1, 0, -1, place_factor)
update(pos, -1, 0, 1, place_factor)
update(pos, -1, 0, -1, place_factor)
if force_below then update(pos, 0, -1, 0, 0)
else update(pos, 0, -1, 0, place_factor)
end
update(pos, 0, 1, 0, place_factor)
end
if naturalslopeslib.setting_enable_shape_on_dig_place() and not naturalslopeslib.setting_revert() then
minetest.register_on_placenode(function(pos, new_node, placer, old_node, item_stack, pointed_thing)
on_place_or_dig(pos, true)
end)
minetest.register_on_dignode(function(pos, old_node, digger)
on_place_or_dig(pos)
end)
end