Skip to content

Commit 2f40377

Browse files
HybridDogrubenwardy
andauthoredOct 6, 2020
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>
1 parent e80fc22 commit 2f40377

File tree

9 files changed

+180
-0
lines changed

9 files changed

+180
-0
lines changed
 

‎builtin/game/misc.lua

+6
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,12 @@ function core.setting_get_pos(name)
151151
end
152152

153153

154+
-- See l_env.cpp for the other functions
155+
function core.get_artificial_light(param1)
156+
return math.floor(param1 / 16)
157+
end
158+
159+
154160
-- To be overriden by protection mods
155161

156162
function core.is_protected(pos, name)

‎doc/lua_api.txt

+16
Original file line numberDiff line numberDiff line change
@@ -4824,6 +4824,22 @@ Environment access
48244824
* `pos`: The position where to measure the light.
48254825
* `timeofday`: `nil` for current time, `0` for night, `0.5` for day
48264826
* Returns a number between `0` and `15` or `nil`
4827+
* `nil` is returned e.g. when the map isn't loaded at `pos`
4828+
* `minetest.get_natural_light(pos[, timeofday])`
4829+
* Figures out the sunlight (or moonlight) value at pos at the given time of
4830+
day.
4831+
* `pos`: The position of the node
4832+
* `timeofday`: `nil` for current time, `0` for night, `0.5` for day
4833+
* Returns a number between `0` and `15` or `nil`
4834+
* This function tests 203 nodes in the worst case, which happens very
4835+
unlikely
4836+
* `minetest.get_artificial_light(param1)`
4837+
* Calculates the artificial light (light from e.g. torches) value from the
4838+
`param1` value.
4839+
* `param1`: The param1 value of a `paramtype = "light"` node.
4840+
* Returns a number between `0` and `15`
4841+
* Currently it's the same as `math.floor(param1 / 16)`, except that it
4842+
ensures compatibility.
48274843
* `minetest.place_node(pos, node)`
48284844
* Place node with the same effects that a player would cause
48294845
* `minetest.dig_node(pos)`

‎games/devtest/mods/testtools/init.lua

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
local S = minetest.get_translator("testtools")
22
local F = minetest.formspec_escape
33

4+
dofile(minetest.get_modpath("testtools") .. "/light.lua")
5+
46
-- TODO: Add a Node Metadata tool
57

68
minetest.register_tool("testtools:param2tool", {
+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
2+
local S = minetest.get_translator("testtools")
3+
4+
minetest.register_tool("testtools:lighttool", {
5+
description = S("Light tool"),
6+
inventory_image = "testtools_lighttool.png",
7+
groups = { testtool = 1, disable_repair = 1 },
8+
on_use = function(itemstack, user, pointed_thing)
9+
local pos = pointed_thing.above
10+
if pointed_thing.type ~= "node" or not pos then
11+
return
12+
end
13+
14+
local node = minetest.get_node(pos)
15+
local time = minetest.get_timeofday()
16+
local sunlight = minetest.get_natural_light(pos)
17+
local artificial = minetest.get_artificial_light(node.param1)
18+
local message = ("param1 0x%02x | time %.5f | sunlight %d | artificial %d")
19+
:format(node.param1, time, sunlight, artificial)
20+
minetest.chat_send_player(user:get_player_name(), message)
21+
end
22+
})
Loading

‎src/script/lua_api/l_env.cpp

+41
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,46 @@ int ModApiEnvMod::l_get_node_light(lua_State *L)
407407
return 1;
408408
}
409409

410+
411+
// get_natural_light(pos, timeofday)
412+
// pos = {x=num, y=num, z=num}
413+
// timeofday: nil = current time, 0 = night, 0.5 = day
414+
int ModApiEnvMod::l_get_natural_light(lua_State *L)
415+
{
416+
GET_ENV_PTR;
417+
418+
v3s16 pos = read_v3s16(L, 1);
419+
420+
bool is_position_ok;
421+
MapNode n = env->getMap().getNode(pos, &is_position_ok);
422+
if (!is_position_ok)
423+
return 0;
424+
425+
// If the daylight is 0, nothing needs to be calculated
426+
u8 daylight = n.param1 & 0x0f;
427+
if (daylight == 0) {
428+
lua_pushinteger(L, 0);
429+
return 1;
430+
}
431+
432+
u32 time_of_day;
433+
if (lua_isnumber(L, 2)) {
434+
time_of_day = 24000.0 * lua_tonumber(L, 2);
435+
time_of_day %= 24000;
436+
} else {
437+
time_of_day = env->getTimeOfDay();
438+
}
439+
u32 dnr = time_to_daynight_ratio(time_of_day, true);
440+
441+
// If it's the same as the artificial light, the sunlight needs to be
442+
// searched for because the value may not emanate from the sun
443+
if (daylight == n.param1 >> 4)
444+
daylight = env->findSunlight(pos);
445+
446+
lua_pushinteger(L, dnr * daylight / 1000);
447+
return 1;
448+
}
449+
410450
// place_node(pos, node)
411451
// pos = {x=num, y=num, z=num}
412452
int ModApiEnvMod::l_place_node(lua_State *L)
@@ -1358,6 +1398,7 @@ void ModApiEnvMod::Initialize(lua_State *L, int top)
13581398
API_FCT(get_node);
13591399
API_FCT(get_node_or_nil);
13601400
API_FCT(get_node_light);
1401+
API_FCT(get_natural_light);
13611402
API_FCT(place_node);
13621403
API_FCT(dig_node);
13631404
API_FCT(punch_node);

‎src/script/lua_api/l_env.h

+5
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ class ModApiEnvMod : public ModApiBase {
5656
// timeofday: nil = current time, 0 = night, 0.5 = day
5757
static int l_get_node_light(lua_State *L);
5858

59+
// get_natural_light(pos, timeofday)
60+
// pos = {x=num, y=num, z=num}
61+
// timeofday: nil = current time, 0 = night, 0.5 = day
62+
static int l_get_natural_light(lua_State *L);
63+
5964
// place_node(pos, node)
6065
// pos = {x=num, y=num, z=num}
6166
static int l_place_node(lua_State *L);

‎src/serverenvironment.cpp

+85
Original file line numberDiff line numberDiff line change
@@ -1066,6 +1066,91 @@ bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
10661066
return true;
10671067
}
10681068

1069+
u8 ServerEnvironment::findSunlight(v3s16 pos) const
1070+
{
1071+
// Directions for neighbouring nodes with specified order
1072+
static const v3s16 dirs[] = {
1073+
v3s16(-1, 0, 0), v3s16(1, 0, 0), v3s16(0, 0, -1), v3s16(0, 0, 1),
1074+
v3s16(0, -1, 0), v3s16(0, 1, 0)
1075+
};
1076+
1077+
const NodeDefManager *ndef = m_server->ndef();
1078+
1079+
// found_light remembers the highest known sunlight value at pos
1080+
u8 found_light = 0;
1081+
1082+
struct stack_entry {
1083+
v3s16 pos;
1084+
s16 dist;
1085+
};
1086+
std::stack<stack_entry> stack;
1087+
stack.push({pos, 0});
1088+
1089+
std::unordered_map<s64, s8> dists;
1090+
dists[MapDatabase::getBlockAsInteger(pos)] = 0;
1091+
1092+
while (!stack.empty()) {
1093+
struct stack_entry e = stack.top();
1094+
stack.pop();
1095+
1096+
v3s16 currentPos = e.pos;
1097+
s8 dist = e.dist + 1;
1098+
1099+
for (const v3s16& off : dirs) {
1100+
v3s16 neighborPos = currentPos + off;
1101+
s64 neighborHash = MapDatabase::getBlockAsInteger(neighborPos);
1102+
1103+
// Do not walk neighborPos multiple times unless the distance to the start
1104+
// position is shorter
1105+
auto it = dists.find(neighborHash);
1106+
if (it != dists.end() && dist >= it->second)
1107+
continue;
1108+
1109+
// Position to walk
1110+
bool is_position_ok;
1111+
MapNode node = m_map->getNode(neighborPos, &is_position_ok);
1112+
if (!is_position_ok) {
1113+
// This happens very rarely because the map at currentPos is loaded
1114+
m_map->emergeBlock(neighborPos, false);
1115+
node = m_map->getNode(neighborPos, &is_position_ok);
1116+
if (!is_position_ok)
1117+
continue; // not generated
1118+
}
1119+
1120+
const ContentFeatures &def = ndef->get(node);
1121+
if (!def.sunlight_propagates) {
1122+
// Do not test propagation here again
1123+
dists[neighborHash] = -1;
1124+
continue;
1125+
}
1126+
1127+
// Sunlight could have come from here
1128+
dists[neighborHash] = dist;
1129+
u8 daylight = node.param1 & 0x0f;
1130+
1131+
// In the special case where sunlight shines from above and thus
1132+
// does not decrease with upwards distance, daylight is always
1133+
// bigger than nightlight, which never reaches 15
1134+
int possible_finlight = daylight - dist;
1135+
if (possible_finlight <= found_light) {
1136+
// Light from here cannot make a brighter light at currentPos than
1137+
// found_light
1138+
continue;
1139+
}
1140+
1141+
u8 nightlight = node.param1 >> 4;
1142+
if (daylight > nightlight) {
1143+
// Found a valid daylight
1144+
found_light = possible_finlight;
1145+
} else {
1146+
// Sunlight may be darker, so walk the neighbours
1147+
stack.push({neighborPos, dist});
1148+
}
1149+
}
1150+
}
1151+
return found_light;
1152+
}
1153+
10691154
void ServerEnvironment::clearObjects(ClearObjectsMode mode)
10701155
{
10711156
infostream << "ServerEnvironment::clearObjects(): "

‎src/serverenvironment.h

+3
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,9 @@ class ServerEnvironment : public Environment
322322
bool removeNode(v3s16 p);
323323
bool swapNode(v3s16 p, const MapNode &n);
324324

325+
// Find the daylight value at pos with a Depth First Search
326+
u8 findSunlight(v3s16 pos) const;
327+
325328
// Find all active objects inside a radius around a point
326329
void getObjectsInsideRadius(std::vector<ServerActiveObject *> &objects, const v3f &pos, float radius,
327330
std::function<bool(ServerActiveObject *obj)> include_obj_cb)

0 commit comments

Comments
 (0)
Please sign in to comment.