]> git.lizzy.rs Git - dragonfireclient.git/commitdiff
Add minetest.get_artificial_light and minetest.get_natural_light (#5680)
authorHybridDog <3192173+HybridDog@users.noreply.github.com>
Tue, 6 Oct 2020 18:49:46 +0000 (20:49 +0200)
committerGitHub <noreply@github.com>
Tue, 6 Oct 2020 18:49:46 +0000 (20:49 +0200)
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>
builtin/game/misc.lua
doc/lua_api.txt
games/devtest/mods/testtools/init.lua
games/devtest/mods/testtools/light.lua [new file with mode: 0644]
games/devtest/mods/testtools/textures/testtools_lighttool.png [new file with mode: 0644]
src/script/lua_api/l_env.cpp
src/script/lua_api/l_env.h
src/serverenvironment.cpp
src/serverenvironment.h

index 341e613c255add613bb43fd551937da764428ba4..96a0a2dda6284213e5cd2e8b0c59cf67f090d5a6 100644 (file)
@@ -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)
index 9e9af20dad58dc288ca2a6d5749c22b48c87fa1f..c4eb5637466f89b582f49b2fc1e7898c3382efc6 100644 (file)
@@ -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)`
index df5bc8e55577d457644161bcb12e63c2114eb3e4..d578b264a7d8e4f097da3262993e9e3ff53ac196 100644 (file)
@@ -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 (file)
index 0000000..a9458ca
--- /dev/null
@@ -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 (file)
index 0000000..6f744b7
Binary files /dev/null and b/games/devtest/mods/testtools/textures/testtools_lighttool.png differ
index 138e88ae869e3f1c807ef8fe20265e8e7f14a7e5..8d50d664d41a860589137d525259c93c37b75f98 100644 (file)
@@ -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);
index 07d4d24387dee0e116cad324d94e33c69773d2d6..7f212b5fcd1be05b28a222ce738ecf833e10197c 100644 (file)
@@ -56,6 +56,11 @@ class ModApiEnvMod : public ModApiBase {
        // 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);
index 320042e19e76bc2e7183ac815060922c025a28ac..6ef56efc89d6090b7ffe651fc8c511906035b4a9 100644 (file)
@@ -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(): "
index af742e290acc557f087e52aedbb16a252e36d23d..cfd5b8f3eb7e3b6f5426ea0de4aa7a6b0bcaacde 100644 (file)
@@ -322,6 +322,9 @@ class ServerEnvironment : public Environment
        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)