forked from Mirrorlandia_minetest/minetest
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>
This commit is contained in:
parent
e80fc22dd9
commit
2f4037752b
@ -151,6 +151,12 @@ function core.setting_get_pos(name)
|
|||||||
end
|
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
|
-- To be overriden by protection mods
|
||||||
|
|
||||||
function core.is_protected(pos, name)
|
function core.is_protected(pos, name)
|
||||||
|
@ -4824,6 +4824,22 @@ Environment access
|
|||||||
* `pos`: The position where to measure the light.
|
* `pos`: The position where to measure the light.
|
||||||
* `timeofday`: `nil` for current time, `0` for night, `0.5` for day
|
* `timeofday`: `nil` for current time, `0` for night, `0.5` for day
|
||||||
* Returns a number between `0` and `15` or `nil`
|
* 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)`
|
* `minetest.place_node(pos, node)`
|
||||||
* Place node with the same effects that a player would cause
|
* Place node with the same effects that a player would cause
|
||||||
* `minetest.dig_node(pos)`
|
* `minetest.dig_node(pos)`
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
local S = minetest.get_translator("testtools")
|
local S = minetest.get_translator("testtools")
|
||||||
local F = minetest.formspec_escape
|
local F = minetest.formspec_escape
|
||||||
|
|
||||||
|
dofile(minetest.get_modpath("testtools") .. "/light.lua")
|
||||||
|
|
||||||
-- TODO: Add a Node Metadata tool
|
-- TODO: Add a Node Metadata tool
|
||||||
|
|
||||||
minetest.register_tool("testtools:param2tool", {
|
minetest.register_tool("testtools:param2tool", {
|
||||||
|
22
games/devtest/mods/testtools/light.lua
Normal file
22
games/devtest/mods/testtools/light.lua
Normal file
@ -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
|
||||||
|
})
|
BIN
games/devtest/mods/testtools/textures/testtools_lighttool.png
Normal file
BIN
games/devtest/mods/testtools/textures/testtools_lighttool.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
@ -407,6 +407,46 @@ int ModApiEnvMod::l_get_node_light(lua_State *L)
|
|||||||
return 1;
|
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)
|
// place_node(pos, node)
|
||||||
// pos = {x=num, y=num, z=num}
|
// pos = {x=num, y=num, z=num}
|
||||||
int ModApiEnvMod::l_place_node(lua_State *L)
|
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);
|
||||||
API_FCT(get_node_or_nil);
|
API_FCT(get_node_or_nil);
|
||||||
API_FCT(get_node_light);
|
API_FCT(get_node_light);
|
||||||
|
API_FCT(get_natural_light);
|
||||||
API_FCT(place_node);
|
API_FCT(place_node);
|
||||||
API_FCT(dig_node);
|
API_FCT(dig_node);
|
||||||
API_FCT(punch_node);
|
API_FCT(punch_node);
|
||||||
|
@ -56,6 +56,11 @@ private:
|
|||||||
// timeofday: nil = current time, 0 = night, 0.5 = day
|
// timeofday: nil = current time, 0 = night, 0.5 = day
|
||||||
static int l_get_node_light(lua_State *L);
|
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)
|
// place_node(pos, node)
|
||||||
// pos = {x=num, y=num, z=num}
|
// pos = {x=num, y=num, z=num}
|
||||||
static int l_place_node(lua_State *L);
|
static int l_place_node(lua_State *L);
|
||||||
|
@ -1066,6 +1066,91 @@ bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
|
|||||||
return true;
|
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)
|
void ServerEnvironment::clearObjects(ClearObjectsMode mode)
|
||||||
{
|
{
|
||||||
infostream << "ServerEnvironment::clearObjects(): "
|
infostream << "ServerEnvironment::clearObjects(): "
|
||||||
|
@ -322,6 +322,9 @@ public:
|
|||||||
bool removeNode(v3s16 p);
|
bool removeNode(v3s16 p);
|
||||||
bool swapNode(v3s16 p, const MapNode &n);
|
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
|
// Find all active objects inside a radius around a point
|
||||||
void getObjectsInsideRadius(std::vector<ServerActiveObject *> &objects, const v3f &pos, float radius,
|
void getObjectsInsideRadius(std::vector<ServerActiveObject *> &objects, const v3f &pos, float radius,
|
||||||
std::function<bool(ServerActiveObject *obj)> include_obj_cb)
|
std::function<bool(ServerActiveObject *obj)> include_obj_cb)
|
||||||
|
Loading…
Reference in New Issue
Block a user