diff --git a/clientmods/preview/init.lua b/clientmods/preview/init.lua index c1358b1c1..96deae074 100644 --- a/clientmods/preview/init.lua +++ b/clientmods/preview/init.lua @@ -65,6 +65,22 @@ core.register_on_item_use(function(itemstack, pointed_thing) print("The local player used an item!") print("pointed_thing :" .. dump(pointed_thing)) print("item = " .. itemstack:get_name()) + + local pos = vector.add(core.localplayer:get_pos(), core.camera:get_offset()) + local pos2 = vector.add(pos, vector.multiply(core.camera:get_look_dir(), 100)) + + local rc = core.raycast(pos, pos2) + local i = rc:next() + print("[PREVIEW] raycast next: " .. dump(i)) + if i then + print("[PREVIEW] line of sight: " .. (core.line_of_sight(pos, i.above) and "yes" or "no")) + + local n1 = core.find_nodes_in_area(pos, i.under, {"default:stone"}) + local n2 = core.find_nodes_in_area_under_air(pos, i.under, {"default:stone"}) + print(("[PREVIEW] found %s nodes, %s nodes under air"):format( + n1 and #n1 or "?", n2 and #n2 or "?")) + end + return false end) @@ -90,11 +106,6 @@ core.register_on_damage_taken(function(hp) print("[PREVIEW] Damage taken " .. hp) end) --- This is an example function to ensure it's working properly, should be removed before merge -core.register_globalstep(function(dtime) - -- print("[PREVIEW] globalstep " .. dtime) -end) - -- This is an example function to ensure it's working properly, should be removed before merge core.register_chatcommand("dump", { func = function(param) diff --git a/doc/client_lua_api.txt b/doc/client_lua_api.txt index a71421747..78cd2ead2 100644 --- a/doc/client_lua_api.txt +++ b/doc/client_lua_api.txt @@ -742,11 +742,46 @@ Call these functions only at load time! * Returns the node at the given position as table in the format `{name="node_name", param1=0, param2=0}`, returns `nil` for unloaded areas or flavor limited areas. +* `minetest.get_node_light(pos, timeofday)` + * Gets the light value at the given position. Note that the light value + "inside" the node at the given position is returned, so you usually want + to get the light value of a neighbor. + * `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` * `minetest.find_node_near(pos, radius, nodenames, [search_center])`: returns pos or `nil` * `radius`: using a maximum metric * `nodenames`: e.g. `{"ignore", "group:tree"}` or `"default:dirt"` * `search_center` is an optional boolean (default: `false`) If true `pos` is also checked for the nodes +* `minetest.find_nodes_in_area(pos1, pos2, nodenames)`: returns a list of + positions. + * `nodenames`: e.g. `{"ignore", "group:tree"}` or `"default:dirt"` + * First return value: Table with all node positions + * Second return value: Table with the count of each node with the node name + as index. + * Area volume is limited to 4,096,000 nodes +* `minetest.find_nodes_in_area_under_air(pos1, pos2, nodenames)`: returns a + list of positions. + * `nodenames`: e.g. `{"ignore", "group:tree"}` or `"default:dirt"` + * Return value: Table with all node positions with a node air above + * Area volume is limited to 4,096,000 nodes +* `minetest.line_of_sight(pos1, pos2)`: returns `boolean, pos` + * Checks if there is anything other than air between pos1 and pos2. + * Returns false if something is blocking the sight. + * Returns the position of the blocking node when `false` + * `pos1`: First position + * `pos2`: Second position +* `minetest.raycast(pos1, pos2, objects, liquids)`: returns `Raycast` + * Creates a `Raycast` object. + * `pos1`: start of the ray + * `pos2`: end of the ray + * `objects`: if false, only nodes will be returned. Default is `true`. + * `liquids`: if false, liquid nodes won't be returned. Default is `false`. + +* `minetest.find_nodes_with_meta(pos1, pos2)` + * Get a table of positions of nodes that have metadata within a region + {pos1, pos2}. * `minetest.get_meta(pos)` * Get a `NodeMetaRef` at that position * `minetest.get_node_level(pos)` @@ -1073,6 +1108,32 @@ Can be obtained via `minetest.get_meta(pos)`. * `fields`: key-value storage * `inventory`: `{list1 = {}, ...}}` +### `Raycast` + +A raycast on the map. It works with selection boxes. +Can be used as an iterator in a for loop as: + + local ray = Raycast(...) + for pointed_thing in ray do + ... + end + +The map is loaded as the ray advances. If the map is modified after the +`Raycast` is created, the changes may or may not have an effect on the object. + +It can be created via `Raycast(pos1, pos2, objects, liquids)` or +`minetest.raycast(pos1, pos2, objects, liquids)` where: + +* `pos1`: start of the ray +* `pos2`: end of the ray +* `objects`: if false, only nodes will be returned. Default is true. +* `liquids`: if false, liquid nodes won't be returned. Default is false. + +#### Methods + +* `next()`: returns a `pointed_thing` with exact pointing location + * Returns the next thing pointed by the ray or nil. + ----------------- ### Definitions * `minetest.get_node_def(nodename)` diff --git a/src/client/client.cpp b/src/client/client.cpp index 89873ade3..36d4f8df9 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -1337,6 +1337,19 @@ int Client::CSMClampRadius(v3s16 pos, int radius) return std::min(radius, m_csm_restriction_noderange - distance); } +v3s16 Client::CSMClampPos(v3s16 pos) +{ + if (!checkCSMRestrictionFlag(CSMRestrictionFlags::CSM_RF_LOOKUP_NODES)) + return pos; + v3s16 ppos = floatToInt(m_env.getLocalPlayer()->getPosition(), BS); + const int range = m_csm_restriction_noderange; + return v3s16( + core::clamp(pos.X, (int)ppos.X - range, (int)ppos.X + range), + core::clamp(pos.Y, (int)ppos.Y - range, (int)ppos.Y + range), + core::clamp(pos.Z, (int)ppos.Z - range, (int)ppos.Z + range) + ); +} + void Client::addNode(v3s16 p, MapNode n, bool remove_metadata) { //TimeTaker timer1("Client::addNode()"); diff --git a/src/client/client.h b/src/client/client.h index 5f3362509..5144af69f 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -264,6 +264,7 @@ public: // helpers to enforce CSM restrictions MapNode CSMGetNode(v3s16 p, bool *is_valid_position); int CSMClampRadius(v3s16 pos, int radius); + v3s16 CSMClampPos(v3s16 pos); void addNode(v3s16 p, MapNode n, bool remove_metadata = true); diff --git a/src/script/cpp_api/s_item.h b/src/script/cpp_api/s_item.h index ad229f73e..25a3501f9 100644 --- a/src/script/cpp_api/s_item.h +++ b/src/script/cpp_api/s_item.h @@ -51,7 +51,6 @@ public: protected: friend class LuaItemStack; friend class ModApiItemMod; - friend class LuaRaycast; bool getItemCallback(const char *name, const char *callbackname, const v3s16 *p = nullptr); /*! diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index 203ffcfdc..762612af4 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -140,17 +140,20 @@ void LuaLBM::trigger(ServerEnvironment *env, v3s16 p, MapNode n) int LuaRaycast::l_next(lua_State *L) { MAP_LOCK_REQUIRED; - - ScriptApiItem *script = getScriptApi(L); GET_ENV_PTR; + bool csm = false; +#ifndef SERVER + csm = getClient(L) != nullptr; +#endif + LuaRaycast *o = checkobject(L, 1); PointedThing pointed; env->continueRaycast(&o->state, &pointed); if (pointed.type == POINTEDTHING_NOTHING) lua_pushnil(L); else - script->pushPointedThing(pointed, true); + push_pointed_thing(L, pointed, csm, true); return 1; } @@ -793,11 +796,20 @@ int ModApiEnvMod::l_find_nodes_in_area(lua_State *L) { GET_ENV_PTR; - const NodeDefManager *ndef = getServer(L)->ndef(); v3s16 minp = read_v3s16(L, 1); v3s16 maxp = read_v3s16(L, 2); sortBoxVerticies(minp, maxp); +#ifndef SERVER + const NodeDefManager *ndef = getClient(L) ? getClient(L)->ndef() : getServer(L)->ndef(); + if (getClient(L)) { + minp = getClient(L)->CSMClampPos(minp); + maxp = getClient(L)->CSMClampPos(maxp); + } +#else + const NodeDefManager *ndef = getServer(L)->ndef(); +#endif + v3s16 cube = maxp - minp + 1; // Volume limit equal to 8 default mapchunks, (80 * 2) ^ 3 = 4,096,000 if ((u64)cube.X * (u64)cube.Y * (u64)cube.Z > 4096000) { @@ -861,11 +873,20 @@ int ModApiEnvMod::l_find_nodes_in_area_under_air(lua_State *L) GET_ENV_PTR; - const NodeDefManager *ndef = getServer(L)->ndef(); v3s16 minp = read_v3s16(L, 1); v3s16 maxp = read_v3s16(L, 2); sortBoxVerticies(minp, maxp); +#ifndef SERVER + const NodeDefManager *ndef = getClient(L) ? getClient(L)->ndef() : getServer(L)->ndef(); + if (getClient(L)) { + minp = getClient(L)->CSMClampPos(minp); + maxp = getClient(L)->CSMClampPos(maxp); + } +#else + const NodeDefManager *ndef = getServer(L)->ndef(); +#endif + v3s16 cube = maxp - minp + 1; // Volume limit equal to 8 default mapchunks, (80 * 2) ^ 3 = 4,096,000 if ((u64)cube.X * (u64)cube.Y * (u64)cube.Z > 4096000) { @@ -1326,8 +1347,14 @@ void ModApiEnvMod::Initialize(lua_State *L, int top) void ModApiEnvMod::InitializeClient(lua_State *L, int top) { + API_FCT(get_node_light); API_FCT(get_timeofday); API_FCT(get_node_max_level); API_FCT(get_node_level); + API_FCT(find_nodes_with_meta); API_FCT(find_node_near); + API_FCT(find_nodes_in_area); + API_FCT(find_nodes_in_area_under_air); + API_FCT(line_of_sight); + API_FCT(raycast); } diff --git a/src/script/scripting_client.cpp b/src/script/scripting_client.cpp index c3e0ca373..1288b1df7 100644 --- a/src/script/scripting_client.cpp +++ b/src/script/scripting_client.cpp @@ -69,6 +69,7 @@ void ClientScripting::InitializeModApi(lua_State *L, int top) { LuaItemStack::Register(L); ItemStackMetaRef::Register(L); + LuaRaycast::Register(L); StorageRef::Register(L); LuaMinimap::Register(L); NodeMetaRef::RegisterClient(L);