aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHybridDog <3192173+HybridDog@users.noreply.github.com>2020-10-06 20:49:46 +0200
committerGitHub <noreply@github.com>2020-10-06 20:49:46 +0200
commit2f4037752b023f87ca1f8859a8dce4f833353967 (patch)
tree5c52b01857c8c46d709d43d1bbc194a3805acded
parente80fc22dd996e5b0efd8c4f67700c0920e323e46 (diff)
downloadminetest-2f4037752b023f87ca1f8859a8dce4f833353967.tar.gz
minetest-2f4037752b023f87ca1f8859a8dce4f833353967.tar.bz2
minetest-2f4037752b023f87ca1f8859a8dce4f833353967.zip
Add minetest.get_artificial_light and minetest.get_natural_light (#5680)
Add more detailed light detection functions, a function to get the artificial light (torches) and a function to get the sunlight as seen by the player (you can specify timeofday). Co-authored-by: rubenwardy <rw@rubenwardy.com>
-rw-r--r--builtin/game/misc.lua6
-rw-r--r--doc/lua_api.txt16
-rw-r--r--games/devtest/mods/testtools/init.lua2
-rw-r--r--games/devtest/mods/testtools/light.lua22
-rw-r--r--games/devtest/mods/testtools/textures/testtools_lighttool.pngbin0 -> 1659 bytes
-rw-r--r--src/script/lua_api/l_env.cpp41
-rw-r--r--src/script/lua_api/l_env.h5
-rw-r--r--src/serverenvironment.cpp85
-rw-r--r--src/serverenvironment.h3
9 files changed, 180 insertions, 0 deletions
diff --git a/builtin/game/misc.lua b/builtin/game/misc.lua
index 341e613c2..96a0a2dda 100644
--- a/builtin/game/misc.lua
+++ b/builtin/game/misc.lua
@@ -151,6 +151,12 @@ function core.setting_get_pos(name)
end
+-- See l_env.cpp for the other functions
+function core.get_artificial_light(param1)
+ return math.floor(param1 / 16)
+end
+
+
-- To be overriden by protection mods
function core.is_protected(pos, name)
diff --git a/doc/lua_api.txt b/doc/lua_api.txt
index 9e9af20da..c4eb56374 100644
--- a/doc/lua_api.txt
+++ b/doc/lua_api.txt
@@ -4824,6 +4824,22 @@ Environment access
* `pos`: The position where to measure the light.
* `timeofday`: `nil` for current time, `0` for night, `0.5` for day
* Returns a number between `0` and `15` or `nil`
+ * `nil` is returned e.g. when the map isn't loaded at `pos`
+* `minetest.get_natural_light(pos[, timeofday])`
+ * Figures out the sunlight (or moonlight) value at pos at the given time of
+ day.
+ * `pos`: The position of the node
+ * `timeofday`: `nil` for current time, `0` for night, `0.5` for day
+ * Returns a number between `0` and `15` or `nil`
+ * This function tests 203 nodes in the worst case, which happens very
+ unlikely
+* `minetest.get_artificial_light(param1)`
+ * Calculates the artificial light (light from e.g. torches) value from the
+ `param1` value.
+ * `param1`: The param1 value of a `paramtype = "light"` node.
+ * Returns a number between `0` and `15`
+ * Currently it's the same as `math.floor(param1 / 16)`, except that it
+ ensures compatibility.
* `minetest.place_node(pos, node)`
* Place node with the same effects that a player would cause
* `minetest.dig_node(pos)`
diff --git a/games/devtest/mods/testtools/init.lua b/games/devtest/mods/testtools/init.lua
index df5bc8e55..d578b264a 100644
--- a/games/devtest/mods/testtools/init.lua
+++ b/games/devtest/mods/testtools/init.lua
@@ -1,6 +1,8 @@
local S = minetest.get_translator("testtools")
local F = minetest.formspec_escape
+dofile(minetest.get_modpath("testtools") .. "/light.lua")
+
-- TODO: Add a Node Metadata tool
minetest.register_tool("testtools:param2tool", {
diff --git a/games/devtest/mods/testtools/light.lua b/games/devtest/mods/testtools/light.lua
new file mode 100644
index 000000000..a9458ca6b
--- /dev/null
+++ b/games/devtest/mods/testtools/light.lua
@@ -0,0 +1,22 @@
+
+local S = minetest.get_translator("testtools")
+
+minetest.register_tool("testtools:lighttool", {
+ description = S("Light tool"),
+ inventory_image = "testtools_lighttool.png",
+ groups = { testtool = 1, disable_repair = 1 },
+ on_use = function(itemstack, user, pointed_thing)
+ local pos = pointed_thing.above
+ if pointed_thing.type ~= "node" or not pos then
+ return
+ end
+
+ local node = minetest.get_node(pos)
+ local time = minetest.get_timeofday()
+ local sunlight = minetest.get_natural_light(pos)
+ local artificial = minetest.get_artificial_light(node.param1)
+ local message = ("param1 0x%02x | time %.5f | sunlight %d | artificial %d")
+ :format(node.param1, time, sunlight, artificial)
+ minetest.chat_send_player(user:get_player_name(), message)
+ end
+})
diff --git a/games/devtest/mods/testtools/textures/testtools_lighttool.png b/games/devtest/mods/testtools/textures/testtools_lighttool.png
new file mode 100644
index 000000000..6f744b7fa
--- /dev/null
+++ b/games/devtest/mods/testtools/textures/testtools_lighttool.png
Binary files differ
diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp
index 138e88ae8..8d50d664d 100644
--- a/src/script/lua_api/l_env.cpp
+++ b/src/script/lua_api/l_env.cpp
@@ -407,6 +407,46 @@ int ModApiEnvMod::l_get_node_light(lua_State *L)
return 1;
}
+
+// get_natural_light(pos, timeofday)
+// pos = {x=num, y=num, z=num}
+// timeofday: nil = current time, 0 = night, 0.5 = day
+int ModApiEnvMod::l_get_natural_light(lua_State *L)
+{
+ GET_ENV_PTR;
+
+ v3s16 pos = read_v3s16(L, 1);
+
+ bool is_position_ok;
+ MapNode n = env->getMap().getNode(pos, &is_position_ok);
+ if (!is_position_ok)
+ return 0;
+
+ // If the daylight is 0, nothing needs to be calculated
+ u8 daylight = n.param1 & 0x0f;
+ if (daylight == 0) {
+ lua_pushinteger(L, 0);
+ return 1;
+ }
+
+ u32 time_of_day;
+ if (lua_isnumber(L, 2)) {
+ time_of_day = 24000.0 * lua_tonumber(L, 2);
+ time_of_day %= 24000;
+ } else {
+ time_of_day = env->getTimeOfDay();
+ }
+ u32 dnr = time_to_daynight_ratio(time_of_day, true);
+
+ // If it's the same as the artificial light, the sunlight needs to be
+ // searched for because the value may not emanate from the sun
+ if (daylight == n.param1 >> 4)
+ daylight = env->findSunlight(pos);
+
+ lua_pushinteger(L, dnr * daylight / 1000);
+ return 1;
+}
+
// place_node(pos, node)
// pos = {x=num, y=num, z=num}
int ModApiEnvMod::l_place_node(lua_State *L)
@@ -1358,6 +1398,7 @@ void ModApiEnvMod::Initialize(lua_State *L, int top)
API_FCT(get_node);
API_FCT(get_node_or_nil);
API_FCT(get_node_light);
+ API_FCT(get_natural_light);
API_FCT(place_node);
API_FCT(dig_node);
API_FCT(punch_node);
diff --git a/src/script/lua_api/l_env.h b/src/script/lua_api/l_env.h
index 07d4d2438..7f212b5fc 100644
--- a/src/script/lua_api/l_env.h
+++ b/src/script/lua_api/l_env.h
@@ -56,6 +56,11 @@ private:
// timeofday: nil = current time, 0 = night, 0.5 = day
static int l_get_node_light(lua_State *L);
+ // get_natural_light(pos, timeofday)
+ // pos = {x=num, y=num, z=num}
+ // timeofday: nil = current time, 0 = night, 0.5 = day
+ static int l_get_natural_light(lua_State *L);
+
// place_node(pos, node)
// pos = {x=num, y=num, z=num}
static int l_place_node(lua_State *L);
diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp
index 320042e19..6ef56efc8 100644
--- a/src/serverenvironment.cpp
+++ b/src/serverenvironment.cpp
@@ -1066,6 +1066,91 @@ bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
return true;
}
+u8 ServerEnvironment::findSunlight(v3s16 pos) const
+{
+ // Directions for neighbouring nodes with specified order
+ static const v3s16 dirs[] = {
+ v3s16(-1, 0, 0), v3s16(1, 0, 0), v3s16(0, 0, -1), v3s16(0, 0, 1),
+ v3s16(0, -1, 0), v3s16(0, 1, 0)
+ };
+
+ const NodeDefManager *ndef = m_server->ndef();
+
+ // found_light remembers the highest known sunlight value at pos
+ u8 found_light = 0;
+
+ struct stack_entry {
+ v3s16 pos;
+ s16 dist;
+ };
+ std::stack<stack_entry> stack;
+ stack.push({pos, 0});
+
+ std::unordered_map<s64, s8> dists;
+ dists[MapDatabase::getBlockAsInteger(pos)] = 0;
+
+ while (!stack.empty()) {
+ struct stack_entry e = stack.top();
+ stack.pop();
+
+ v3s16 currentPos = e.pos;
+ s8 dist = e.dist + 1;
+
+ for (const v3s16& off : dirs) {
+ v3s16 neighborPos = currentPos + off;
+ s64 neighborHash = MapDatabase::getBlockAsInteger(neighborPos);
+
+ // Do not walk neighborPos multiple times unless the distance to the start
+ // position is shorter
+ auto it = dists.find(neighborHash);
+ if (it != dists.end() && dist >= it->second)
+ continue;
+
+ // Position to walk
+ bool is_position_ok;
+ MapNode node = m_map->getNode(neighborPos, &is_position_ok);
+ if (!is_position_ok) {
+ // This happens very rarely because the map at currentPos is loaded
+ m_map->emergeBlock(neighborPos, false);
+ node = m_map->getNode(neighborPos, &is_position_ok);
+ if (!is_position_ok)
+ continue; // not generated
+ }
+
+ const ContentFeatures &def = ndef->get(node);
+ if (!def.sunlight_propagates) {
+ // Do not test propagation here again
+ dists[neighborHash] = -1;
+ continue;
+ }
+
+ // Sunlight could have come from here
+ dists[neighborHash] = dist;
+ u8 daylight = node.param1 & 0x0f;
+
+ // In the special case where sunlight shines from above and thus
+ // does not decrease with upwards distance, daylight is always
+ // bigger than nightlight, which never reaches 15
+ int possible_finlight = daylight - dist;
+ if (possible_finlight <= found_light) {
+ // Light from here cannot make a brighter light at currentPos than
+ // found_light
+ continue;
+ }
+
+ u8 nightlight = node.param1 >> 4;
+ if (daylight > nightlight) {
+ // Found a valid daylight
+ found_light = possible_finlight;
+ } else {
+ // Sunlight may be darker, so walk the neighbours
+ stack.push({neighborPos, dist});
+ }
+ }
+ }
+ return found_light;
+}
+
void ServerEnvironment::clearObjects(ClearObjectsMode mode)
{
infostream << "ServerEnvironment::clearObjects(): "
diff --git a/src/serverenvironment.h b/src/serverenvironment.h
index af742e290..cfd5b8f3e 100644
--- a/src/serverenvironment.h
+++ b/src/serverenvironment.h
@@ -322,6 +322,9 @@ public:
bool removeNode(v3s16 p);
bool swapNode(v3s16 p, const MapNode &n);
+ // Find the daylight value at pos with a Depth First Search
+ u8 findSunlight(v3s16 pos) const;
+
// Find all active objects inside a radius around a point
void getObjectsInsideRadius(std::vector<ServerActiveObject *> &objects, const v3f &pos, float radius,
std::function<bool(ServerActiveObject *obj)> include_obj_cb)