From 808fa5ecb3ddfd5d993000cc6b4c972257e182db Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Thu, 23 Jul 2020 19:54:58 +0200 Subject: Improve default inventory+wield images of node drawtypes (#9299) --- doc/texture_packs.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'doc') diff --git a/doc/texture_packs.txt b/doc/texture_packs.txt index e7a7dfd3c..79d65a339 100644 --- a/doc/texture_packs.txt +++ b/doc/texture_packs.txt @@ -90,6 +90,7 @@ by texture packs. All existing fallback textures can be found in the directory * `minimap_mask_square.png`: mask used for the square minimap * `minimap_overlay_round.png`: overlay texture for the round minimap * `minimap_overlay_square.png`: overlay texture for the square minimap +* `no_texture_airlike.png`: fallback inventory image for airlike nodes * `object_marker_red.png`: texture for players on the minimap * `player_marker.png`: texture for the own player on the square minimap -- cgit v1.2.3 From ae83edd16581b2b5426b565e703a8766e88dbbf6 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Mon, 27 Jul 2020 19:40:33 +0200 Subject: Play place_failed sound if occupied or cannot attach (#9486) --- doc/lua_api.txt | 7 +++++-- src/client/game.cpp | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'doc') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index e0c895c97..5fe02b452 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -7217,10 +7217,13 @@ Used by `minetest.register_node`. -- Node was placed. Also played after falling place_failed = , - -- When node placement failed + -- When node placement failed. + -- Note: This happens if the _built-in_ node placement failed. + -- This sound will still be played if the node is placed in the + -- `on_place` callback manually. fall = , - -- When node starts to fall + -- When node starts to fall or is detached }, drop = "", diff --git a/src/client/game.cpp b/src/client/game.cpp index 42d60b21c..20d2c6f90 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -3379,6 +3379,7 @@ bool Game::nodePlacement(const ItemDefinition &selected_def, } else { node = map.getNode(p, &is_valid_position); if (is_valid_position && !nodedef->get(node).buildable_to) { + soundmaker->m_player_rightpunch_sound = selected_def.sound_place_failed; // Report to server client->interact(INTERACT_PLACE, pointed); return false; @@ -3451,6 +3452,7 @@ bool Game::nodePlacement(const ItemDefinition &selected_def, pp = p + v3s16(0, -1, 0); if (!nodedef->get(map.getNode(pp)).walkable) { + soundmaker->m_player_rightpunch_sound = selected_def.sound_place_failed; // Report to server client->interact(INTERACT_PLACE, pointed); return false; -- cgit v1.2.3 From 715a123a33db7b0f191259ba68cbc9c565d0d4e8 Mon Sep 17 00:00:00 2001 From: Lejo Date: Thu, 30 Jul 2020 00:16:21 +0300 Subject: Add PUT and DELETE request + specific method value to HTTP API (#9909) --- doc/lua_api.txt | 12 ++++++-- src/client/clientmedia.cpp | 3 +- src/httpfetch.cpp | 66 +++++++++++++++++++++++++------------------ src/httpfetch.h | 22 +++++++++++---- src/network/networkprotocol.h | 2 +- src/script/lua_api/l_http.cpp | 29 +++++++++++++++++-- src/script/lua_api/l_http.h | 4 +-- src/serverlist.cpp | 4 +-- 8 files changed, 98 insertions(+), 44 deletions(-) (limited to 'doc') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 5fe02b452..2d22dc899 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -8071,11 +8071,13 @@ Used by `HTTPApiTable.fetch` and `HTTPApiTable.fetch_async`. timeout = 10, -- Timeout for connection in seconds. Default is 3 seconds. - post_data = "Raw POST request data string" OR {field1 = "data1", field2 = "data2"}, - -- Optional, if specified a POST request with post_data is performed. + method = "GET", "POST", "PUT" or "DELETE" + -- The http method to use. Defaults to "GET". + + data = "Raw request data string" OR {field1 = "data1", field2 = "data2"}, + -- Data for the POST, PUT or DELETE request. -- Accepts both a string and a table. If a table is specified, encodes -- table as x-www-form-urlencoded key-value pairs. - -- If post_data is not specified, a GET request is performed instead. user_agent = "ExampleUserAgent", -- Optional, if specified replaces the default minetest user agent with @@ -8089,6 +8091,10 @@ Used by `HTTPApiTable.fetch` and `HTTPApiTable.fetch_async`. multipart = boolean -- Optional, if true performs a multipart HTTP request. -- Default is false. + -- Post only, data must be array + + post_data = "Raw POST request data string" OR {field1 = "data1", field2 = "data2"}, + -- Deprecated, use `data` instead. Forces `method = "POST"`. } `HTTPRequestResult` definition diff --git a/src/client/clientmedia.cpp b/src/client/clientmedia.cpp index 8cd3b6bcc..c4c08c05d 100644 --- a/src/client/clientmedia.cpp +++ b/src/client/clientmedia.cpp @@ -260,7 +260,8 @@ void ClientMediaDownloader::initialStep(Client *client) fetch_request.request_id = m_httpfetch_next_id; // == i fetch_request.timeout = m_httpfetch_timeout; fetch_request.connect_timeout = m_httpfetch_timeout; - fetch_request.post_data = required_hash_set; + fetch_request.method = HTTP_POST; + fetch_request.raw_data = required_hash_set; fetch_request.extra_headers.emplace_back( "Content-Type: application/octet-stream"); diff --git a/src/httpfetch.cpp b/src/httpfetch.cpp index 326b5052f..65202ce3e 100644 --- a/src/httpfetch.cpp +++ b/src/httpfetch.cpp @@ -294,13 +294,11 @@ HTTPFetchOngoing::HTTPFetchOngoing(const HTTPFetchRequest &request_, curl_easy_setopt(curl, CURLOPT_WRITEDATA, &oss); } - // Set POST (or GET) data - if (request.post_fields.empty() && request.post_data.empty()) { - curl_easy_setopt(curl, CURLOPT_HTTPGET, 1); - } else if (request.multipart) { + // Set data from fields or raw_data + if (request.multipart) { curl_httppost *last = NULL; - for (StringMap::iterator it = request.post_fields.begin(); - it != request.post_fields.end(); ++it) { + for (StringMap::iterator it = request.fields.begin(); + it != request.fields.end(); ++it) { curl_formadd(&post, &last, CURLFORM_NAMELENGTH, it->first.size(), CURLFORM_PTRNAME, it->first.c_str(), @@ -311,28 +309,42 @@ HTTPFetchOngoing::HTTPFetchOngoing(const HTTPFetchRequest &request_, curl_easy_setopt(curl, CURLOPT_HTTPPOST, post); // request.post_fields must now *never* be // modified until CURLOPT_HTTPPOST is cleared - } else if (request.post_data.empty()) { - curl_easy_setopt(curl, CURLOPT_POST, 1); - std::string str; - for (auto &post_field : request.post_fields) { - if (!str.empty()) - str += "&"; - str += urlencode(post_field.first); - str += "="; - str += urlencode(post_field.second); - } - curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, - str.size()); - curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS, - str.c_str()); } else { - curl_easy_setopt(curl, CURLOPT_POST, 1); - curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, - request.post_data.size()); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, - request.post_data.c_str()); - // request.post_data must now *never* be - // modified until CURLOPT_POSTFIELDS is cleared + switch (request.method) { + case HTTP_GET: + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1); + break; + case HTTP_POST: + curl_easy_setopt(curl, CURLOPT_POST, 1); + break; + case HTTP_PUT: + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT"); + break; + case HTTP_DELETE: + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); + break; + } + if (request.method != HTTP_GET) { + if (!request.raw_data.empty()) { + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, + request.raw_data.size()); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, + request.raw_data.c_str()); + } else if (!request.fields.empty()) { + std::string str; + for (auto &field : request.fields) { + if (!str.empty()) + str += "&"; + str += urlencode(field.first); + str += "="; + str += urlencode(field.second); + } + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, + str.size()); + curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS, + str.c_str()); + } + } } // Set additional HTTP headers for (const std::string &extra_header : request.extra_headers) { diff --git a/src/httpfetch.h b/src/httpfetch.h index ae8b5afb5..3b9f17f0a 100644 --- a/src/httpfetch.h +++ b/src/httpfetch.h @@ -28,6 +28,15 @@ with this program; if not, write to the Free Software Foundation, Inc., #define HTTPFETCH_DISCARD 0 #define HTTPFETCH_SYNC 1 +// Methods +enum HttpMethod : u8 +{ + HTTP_GET, + HTTP_POST, + HTTP_PUT, + HTTP_DELETE, +}; + struct HTTPFetchRequest { std::string url = ""; @@ -50,12 +59,15 @@ struct HTTPFetchRequest // application/x-www-form-urlencoded. POST-only. bool multipart = false; - // POST fields. Fields are escaped properly. - // If this is empty a GET request is done instead. - StringMap post_fields; + // The Method to use default = GET + // Avaible methods GET, POST, PUT, DELETE + HttpMethod method = HTTP_GET; + + // Fields of the request + StringMap fields; - // Raw POST data, overrides post_fields. - std::string post_data; + // Raw data of the request overrides fields + std::string raw_data; // If not empty, should contain entries such as "Accept: text/html" std::vector extra_headers; diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 28abf02c0..05600cda9 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -1034,7 +1034,7 @@ const static std::string accessDeniedStrings[SERVER_ACCESSDENIED_MAX] = { "This server has experienced an internal error. You will now be disconnected." }; -enum PlayerListModifer: u8 +enum PlayerListModifer : u8 { PLAYER_LIST_INIT, PLAYER_LIST_ADD, diff --git a/src/script/lua_api/l_http.cpp b/src/script/lua_api/l_http.cpp index ec43bf174..5ea3b3f99 100644 --- a/src/script/lua_api/l_http.cpp +++ b/src/script/lua_api/l_http.cpp @@ -49,17 +49,40 @@ void ModApiHttp::read_http_fetch_request(lua_State *L, HTTPFetchRequest &req) req.multipart = getboolfield_default(L, 1, "multipart", false); req.timeout = getintfield_default(L, 1, "timeout", 3) * 1000; - // post_data: if table, post form data, otherwise raw data + lua_getfield(L, 1, "method"); + if (lua_isstring(L, -1)) { + std::string mth = getstringfield_default(L, 1, "method", ""); + if (mth == "GET") + req.method = HTTP_GET; + else if (mth == "POST") + req.method = HTTP_POST; + else if (mth == "PUT") + req.method = HTTP_PUT; + else if (mth == "DELETE") + req.method = HTTP_DELETE; + } + lua_pop(L, 1); + + // post_data: if table, post form data, otherwise raw data DEPRECATED use data and method instead lua_getfield(L, 1, "post_data"); + if (lua_isnil(L, 2)) { + lua_pop(L, 1); + lua_getfield(L, 1, "data"); + } + else { + req.method = HTTP_POST; + } + if (lua_istable(L, 2)) { lua_pushnil(L); while (lua_next(L, 2) != 0) { - req.post_fields[readParam(L, -2)] = readParam(L, -1); + req.fields[readParam(L, -2)] = readParam(L, -1); lua_pop(L, 1); } } else if (lua_isstring(L, 2)) { - req.post_data = readParam(L, 2); + req.raw_data = readParam(L, 2); } + lua_pop(L, 1); lua_getfield(L, 1, "extra_headers"); diff --git a/src/script/lua_api/l_http.h b/src/script/lua_api/l_http.h index de6e51b37..c3a2a5276 100644 --- a/src/script/lua_api/l_http.h +++ b/src/script/lua_api/l_http.h @@ -32,10 +32,10 @@ private: static void read_http_fetch_request(lua_State *L, HTTPFetchRequest &req); static void push_http_fetch_result(lua_State *L, HTTPFetchResult &res, bool completed = true); - // http_fetch_sync({url=, timeout=, post_data=}) + // http_fetch_sync({url=, timeout=, data=}) static int l_http_fetch_sync(lua_State *L); - // http_fetch_async({url=, timeout=, post_data=}) + // http_fetch_async({url=, timeout=, data=}) static int l_http_fetch_async(lua_State *L); // http_fetch_async_get(handle) diff --git a/src/serverlist.cpp b/src/serverlist.cpp index 18264e933..2f6ab2e61 100644 --- a/src/serverlist.cpp +++ b/src/serverlist.cpp @@ -261,11 +261,11 @@ void sendAnnounce(AnnounceAction action, HTTPFetchRequest fetch_request; fetch_request.url = g_settings->get("serverlist_url") + std::string("/announce"); - fetch_request.post_fields["json"] = fastWriteJson(server); + fetch_request.method = HTTP_POST; + fetch_request.fields["json"] = fastWriteJson(server); fetch_request.multipart = true; httpfetch_async(fetch_request); } #endif } // namespace ServerList - -- cgit v1.2.3 From 9bba52c4000a06043f5100dbb0ef66d869707ffc Mon Sep 17 00:00:00 2001 From: sfan5 Date: Thu, 30 Jul 2020 17:39:57 +0200 Subject: content_cao: Support texture animation for upright_sprite (#10020) --- doc/lua_api.txt | 18 +++++++++--------- games/devtest/mods/testentities/visuals.lua | 16 ++++++++++++++-- src/client/content_cao.cpp | 28 +++++++++++++++++++++++++--- src/client/mesh.cpp | 9 +++++++++ src/client/mesh.h | 7 +++++++ 5 files changed, 64 insertions(+), 14 deletions(-) (limited to 'doc') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 2d22dc899..8ac3ad7f2 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -6077,15 +6077,15 @@ object you are working with still exists. * `get_yaw()`: returns number in radians * `set_texture_mod(mod)` * `get_texture_mod()` returns current texture modifier -* `set_sprite(p, num_frames, framelength, select_horiz_by_yawpitch)` - * Select sprite from spritesheet with optional animation and Dungeon Master - style texture selection based on yaw relative to camera - * `p`: {x=number, y=number}, the coordinate of the first frame - (x: column, y: row), default: `{x=0, y=0}` - * `num_frames`: number, default: `1` - * `framelength`: number, default: `0.2` - * `select_horiz_by_yawpitch`: boolean, this was once used for the Dungeon - Master mob, default: `false` +* `set_sprite(p, num_frames, framelength, select_x_by_camera)` + * Specifies and starts a sprite animation + * Animations iterate along the frame `y` position. + * `p`: {x=column number, y=row number}, the coordinate of the first frame + default: `{x=0, y=0}` + * `num_frames`: Total frames in the texture, default: `1` + * `framelength`: Time per animated frame in seconds, default: `0.2` + * `select_x_by_camera`: Only for visual = `sprite`. Changes the frame `x` + position according to the view direction. default: `false`. * `get_entity_name()` (**Deprecated**: Will be removed in a future version) * `get_luaentity()` diff --git a/games/devtest/mods/testentities/visuals.lua b/games/devtest/mods/testentities/visuals.lua index 83f361f16..8848ba49f 100644 --- a/games/devtest/mods/testentities/visuals.lua +++ b/games/devtest/mods/testentities/visuals.lua @@ -68,7 +68,7 @@ minetest.register_entity("testentities:mesh_unshaded", { -- Advanced visual tests --- A test entity for testing animated and yaw-modulated sprites +-- An entity for testing animated and yaw-modulated sprites minetest.register_entity("testentities:yawsprite", { initial_properties = { selectionbox = {-0.3, -0.5, -0.3, 0.3, 0.3, 0.3}, @@ -79,6 +79,18 @@ minetest.register_entity("testentities:yawsprite", { initial_sprite_basepos = {x=0, y=0}, }, on_activate = function(self, staticdata) - self.object:set_sprite({x=0, y=0}, 1, 0, true) + self.object:set_sprite({x=0, y=0}, 3, 0.5, true) + end, +}) + +-- An entity for testing animated upright sprites +minetest.register_entity("testentities:upright_animated", { + initial_properties = { + visual = "upright_sprite", + textures = {"testnodes_anim.png"}, + spritediv = {x = 1, y = 4}, + }, + on_activate = function(self) + self.object:set_sprite({x=0, y=0}, 4, 1.0, false) end, }) diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index 4f949f6b0..88688d18c 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -1176,6 +1176,7 @@ void GenericCAO::updateTexturePos() int row = m_tx_basepos.Y; int col = m_tx_basepos.X; + // Yawpitch goes rightwards if (m_tx_select_horiz_by_yawpitch) { if (cam_to_entity.Y > 0.75) col += 5; @@ -1206,6 +1207,27 @@ void GenericCAO::updateTexturePos() float tys = m_tx_size.Y; setBillboardTextureMatrix(m_spritenode, txs, tys, col, row); } + + else if (m_meshnode) { + if (m_prop.visual == "upright_sprite") { + int row = m_tx_basepos.Y; + int col = m_tx_basepos.X; + + // Animation goes downwards + row += m_anim_frame; + + const auto &tx = m_tx_size; + v2f t[4] = { // cf. vertices in GenericCAO::addToScene() + tx * v2f(col+1, row+1), + tx * v2f(col, row+1), + tx * v2f(col, row), + tx * v2f(col+1, row), + }; + auto mesh = m_meshnode->getMesh(); + setMeshBufferTextureCoords(mesh->getMeshBuffer(0), t, 4); + setMeshBufferTextureCoords(mesh->getMeshBuffer(1), t, 4); + } + } } // Do not pass by reference, see header. @@ -1247,7 +1269,7 @@ void GenericCAO::updateTextures(std::string mod) } } - if (m_animated_meshnode) { + else if (m_animated_meshnode) { if (m_prop.visual == "mesh") { for (u32 i = 0; i < m_prop.textures.size() && i < m_animated_meshnode->getMaterialCount(); ++i) { @@ -1296,8 +1318,8 @@ void GenericCAO::updateTextures(std::string mod) } } } - if(m_meshnode) - { + + else if (m_meshnode) { if(m_prop.visual == "cube") { for (u32 i = 0; i < 6; ++i) diff --git a/src/client/mesh.cpp b/src/client/mesh.cpp index e1ec22068..2400a374c 100644 --- a/src/client/mesh.cpp +++ b/src/client/mesh.cpp @@ -203,6 +203,15 @@ void setMeshColor(scene::IMesh *mesh, const video::SColor &color) setMeshBufferColor(mesh->getMeshBuffer(j), color); } +void setMeshBufferTextureCoords(scene::IMeshBuffer *buf, const v2f *uv, u32 count) +{ + const u32 stride = getVertexPitchFromType(buf->getVertexType()); + assert(buf->getVertexCount() >= count); + u8 *vertices = (u8 *) buf->getVertices(); + for (u32 i = 0; i < count; i++) + ((video::S3DVertex*) (vertices + i * stride))->TCoords = uv[i]; +} + template static void applyToMesh(scene::IMesh *mesh, const F &fn) { diff --git a/src/client/mesh.h b/src/client/mesh.h index 103c61e45..dbc091a06 100644 --- a/src/client/mesh.h +++ b/src/client/mesh.h @@ -58,6 +58,13 @@ void setMeshBufferColor(scene::IMeshBuffer *buf, const video::SColor &color); */ void setMeshColor(scene::IMesh *mesh, const video::SColor &color); + +/* + Sets texture coords for vertices in the mesh buffer. + `uv[]` must have `count` elements +*/ +void setMeshBufferTextureCoords(scene::IMeshBuffer *buf, const v2f *uv, u32 count); + /* Set a constant color for an animated mesh */ -- cgit v1.2.3 From 93ecc589bc49a80187705f6e06df23a71263d3d7 Mon Sep 17 00:00:00 2001 From: Hugues Ross Date: Tue, 4 Aug 2020 14:12:47 -0400 Subject: Implement override.txt support for special tiles (#10140) Add override targets for all special_tiles entries in node definitions, allowing texture packs to replace these textures. This makes overrides work properly with a variety of drawtypes. The targets are named special1 through special6, covering the the current length of the special_tiles array. --- doc/texture_packs.txt | 18 +++++++++++++++++- src/nodedef.cpp | 21 +++++++++++++++++++++ src/nodedef.h | 5 +++++ src/texture_override.cpp | 38 ++++++++++++++++++++++++++------------ src/texture_override.h | 18 +++++++++++++++--- util/ci/lint.sh | 0 6 files changed, 84 insertions(+), 16 deletions(-) mode change 100644 => 100755 util/ci/lint.sh (limited to 'doc') diff --git a/doc/texture_packs.txt b/doc/texture_packs.txt index 79d65a339..8af2cbad6 100644 --- a/doc/texture_packs.txt +++ b/doc/texture_packs.txt @@ -195,11 +195,27 @@ Here are targets you can choose from: | bottom | y- face | | sides | x-, x+, z-, z+ faces | | all | All faces. You can also use '*' instead of 'all'. | +| special1 | The first entry in the special_tiles list | +| special2 | The second entry in the special_tiles list | +| special3 | The third entry in the special_tiles list | +| special4 | The fourth entry in the special_tiles list | +| special5 | The fifth entry in the special_tiles list | +| special6 | The sixth entry in the special_tiles list | | inventory | The inventory texture | | wield | The texture used when held by the player | Nodes support all targets, but other items only support 'inventory' -and 'wield' +and 'wield'. + +### Using the special targets + +The special* targets only apply to specific drawtypes: + +* `flowingliquid`: special1 sets the top texture, special2 sets the side texture +* `allfaces_optional`: special1 is used by simple mode, see below +* `glasslike_framed`: When containing a liquid, special1 sets the liquid texture +* `glasslike_framed_optional`: Same as `glasslike_framed` +* `plantlike_rooted`: special1 sets the plant's texture Designing leaves textures for the leaves rendering options ---------------------------------------------------------- diff --git a/src/nodedef.cpp b/src/nodedef.cpp index e5cd2c2a7..82c4581fa 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -1337,6 +1337,7 @@ void NodeDefManager::applyTextureOverrides(const std::vector &o ContentFeatures &nodedef = m_content_features[id]; + // Override tiles if (texture_override.hasTarget(OverrideTarget::TOP)) nodedef.tiledef[0].name = texture_override.texture; @@ -1354,6 +1355,26 @@ void NodeDefManager::applyTextureOverrides(const std::vector &o if (texture_override.hasTarget(OverrideTarget::FRONT)) nodedef.tiledef[5].name = texture_override.texture; + + + // Override special tiles, if applicable + if (texture_override.hasTarget(OverrideTarget::SPECIAL_1)) + nodedef.tiledef_special[0].name = texture_override.texture; + + if (texture_override.hasTarget(OverrideTarget::SPECIAL_2)) + nodedef.tiledef_special[1].name = texture_override.texture; + + if (texture_override.hasTarget(OverrideTarget::SPECIAL_3)) + nodedef.tiledef_special[2].name = texture_override.texture; + + if (texture_override.hasTarget(OverrideTarget::SPECIAL_4)) + nodedef.tiledef_special[3].name = texture_override.texture; + + if (texture_override.hasTarget(OverrideTarget::SPECIAL_5)) + nodedef.tiledef_special[4].name = texture_override.texture; + + if (texture_override.hasTarget(OverrideTarget::SPECIAL_6)) + nodedef.tiledef_special[5].name = texture_override.texture; } } diff --git a/src/nodedef.h b/src/nodedef.h index ff52f976f..d0da367ee 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -261,6 +261,11 @@ struct TileDef NodeDrawType drawtype); }; +// Defines the number of special tiles per nodedef +// +// NOTE: When changing this value, the enum entries of OverrideTarget and +// parser in TextureOverrideSource must be updated so that all special +// tiles can be overridden. #define CF_SPECIAL_COUNT 6 struct ContentFeatures diff --git a/src/texture_override.cpp b/src/texture_override.cpp index 10d129b87..effdb0efd 100644 --- a/src/texture_override.cpp +++ b/src/texture_override.cpp @@ -24,6 +24,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include +#define override_cast static_cast + TextureOverrideSource::TextureOverrideSource(std::string filepath) { std::ifstream infile(filepath.c_str()); @@ -56,25 +58,37 @@ TextureOverrideSource::TextureOverrideSource(std::string filepath) std::vector targets = str_split(splitted[1], ','); for (const std::string &target : targets) { if (target == "top") - texture_override.target |= static_cast(OverrideTarget::TOP); + texture_override.target |= override_cast(OverrideTarget::TOP); else if (target == "bottom") - texture_override.target |= static_cast(OverrideTarget::BOTTOM); + texture_override.target |= override_cast(OverrideTarget::BOTTOM); else if (target == "left") - texture_override.target |= static_cast(OverrideTarget::LEFT); + texture_override.target |= override_cast(OverrideTarget::LEFT); else if (target == "right") - texture_override.target |= static_cast(OverrideTarget::RIGHT); + texture_override.target |= override_cast(OverrideTarget::RIGHT); else if (target == "front") - texture_override.target |= static_cast(OverrideTarget::FRONT); + texture_override.target |= override_cast(OverrideTarget::FRONT); else if (target == "back") - texture_override.target |= static_cast(OverrideTarget::BACK); + texture_override.target |= override_cast(OverrideTarget::BACK); else if (target == "inventory") - texture_override.target |= static_cast(OverrideTarget::INVENTORY); + texture_override.target |= override_cast(OverrideTarget::INVENTORY); else if (target == "wield") - texture_override.target |= static_cast(OverrideTarget::WIELD); + texture_override.target |= override_cast(OverrideTarget::WIELD); + else if (target == "special1") + texture_override.target |= override_cast(OverrideTarget::SPECIAL_1); + else if (target == "special2") + texture_override.target |= override_cast(OverrideTarget::SPECIAL_2); + else if (target == "special3") + texture_override.target |= override_cast(OverrideTarget::SPECIAL_3); + else if (target == "special4") + texture_override.target |= override_cast(OverrideTarget::SPECIAL_4); + else if (target == "special5") + texture_override.target |= override_cast(OverrideTarget::SPECIAL_5); + else if (target == "special6") + texture_override.target |= override_cast(OverrideTarget::SPECIAL_6); else if (target == "sides") - texture_override.target |= static_cast(OverrideTarget::SIDES); + texture_override.target |= override_cast(OverrideTarget::SIDES); else if (target == "all" || target == "*") - texture_override.target |= static_cast(OverrideTarget::ALL_FACES); + texture_override.target |= override_cast(OverrideTarget::ALL_FACES); else { // Report invalid target warningstream << filepath << ":" << line_index @@ -85,7 +99,7 @@ TextureOverrideSource::TextureOverrideSource(std::string filepath) } // If there are no valid targets, skip adding this override - if (texture_override.target == static_cast(OverrideTarget::INVALID)) { + if (texture_override.target == override_cast(OverrideTarget::INVALID)) { continue; } @@ -112,7 +126,7 @@ std::vector TextureOverrideSource::getNodeTileOverrides() std::vector found_overrides; for (const TextureOverride &texture_override : m_overrides) { - if (texture_override.hasTarget(OverrideTarget::ALL_FACES)) + if (texture_override.hasTarget(OverrideTarget::NODE_TARGETS)) found_overrides.push_back(texture_override); } diff --git a/src/texture_override.h b/src/texture_override.h index db03bd014..bdc95e732 100644 --- a/src/texture_override.h +++ b/src/texture_override.h @@ -23,8 +23,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include +typedef u16 override_t; + //! Bitmask enum specifying what a texture override should apply to -enum class OverrideTarget : u8 +enum class OverrideTarget : override_t { INVALID = 0, TOP = 1 << 0, @@ -35,23 +37,33 @@ enum class OverrideTarget : u8 BACK = 1 << 5, INVENTORY = 1 << 6, WIELD = 1 << 7, + SPECIAL_1 = 1 << 8, + SPECIAL_2 = 1 << 9, + SPECIAL_3 = 1 << 10, + SPECIAL_4 = 1 << 11, + SPECIAL_5 = 1 << 12, + SPECIAL_6 = 1 << 13, + // clang-format off SIDES = LEFT | RIGHT | FRONT | BACK, ALL_FACES = TOP | BOTTOM | SIDES, + ALL_SPECIAL = SPECIAL_1 | SPECIAL_2 | SPECIAL_3 | SPECIAL_4 | SPECIAL_5 | SPECIAL_6, + NODE_TARGETS = ALL_FACES | ALL_SPECIAL, ITEM_TARGETS = INVENTORY | WIELD, + // clang-format on }; struct TextureOverride { std::string id; std::string texture; - u8 target; + override_t target; // Helper function for checking if an OverrideTarget is found in // a TextureOverride without casting inline bool hasTarget(OverrideTarget overrideTarget) const { - return (target & static_cast(overrideTarget)) != 0; + return (target & static_cast(overrideTarget)) != 0; } }; diff --git a/util/ci/lint.sh b/util/ci/lint.sh old mode 100644 new mode 100755 -- cgit v1.2.3 From fff03931871b68e092e12bfce9056f760e8ec9dd Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 12 Aug 2020 11:52:50 +0200 Subject: Render nodeboxes with opaque material if possible (#10122) --- doc/lua_api.txt | 2 ++ src/nodedef.cpp | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--- src/nodedef.h | 12 ++++++++++++ 3 files changed, 68 insertions(+), 3 deletions(-) (limited to 'doc') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 8ac3ad7f2..6366a34c3 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -7065,6 +7065,8 @@ Used by `minetest.register_node`. use_texture_alpha = false, -- Use texture's alpha channel + -- If this is set to false, the node will be rendered fully opaque + -- regardless of any texture transparency. palette = "palette.png", -- The node's `param2` is used to select a pixel from the image. diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 82c4581fa..392f5eb98 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -695,9 +695,54 @@ static void fillTileAttribs(ITextureSource *tsrc, TileLayer *layer, } } } -#endif -#ifndef SERVER +bool ContentFeatures::textureAlphaCheck(ITextureSource *tsrc, const TileDef *tiles, int length) +{ + video::IVideoDriver *driver = RenderingEngine::get_video_driver(); + static thread_local bool long_warning_printed = false; + std::set seen; + for (int i = 0; i < length; i++) { + if (seen.find(tiles[i].name) != seen.end()) + continue; + seen.insert(tiles[i].name); + + // Load the texture and see if there's any transparent pixels + video::ITexture *texture = tsrc->getTexture(tiles[i].name); + video::IImage *image = driver->createImage(texture, + core::position2d(0, 0), texture->getOriginalSize()); + if (!image) + continue; + core::dimension2d dim = image->getDimension(); + bool ok = true; + for (u16 x = 0; x < dim.Width; x++) { + for (u16 y = 0; y < dim.Height; y++) { + if (image->getPixel(x, y).getAlpha() < 255) { + ok = false; + goto break_loop; + } + } + } + +break_loop: + image->drop(); + if (!ok) { + warningstream << "Texture \"" << tiles[i].name << "\" of " + << name << " has transparent pixels, assuming " + "use_texture_alpha = true." << std::endl; + if (!long_warning_printed) { + warningstream << " This warning can be a false-positive if " + "unused pixels in the texture are transparent. However if " + "it is meant to be transparent, you *MUST* update the " + "nodedef and set use_texture_alpha = true! This compatibility " + "code will be removed in a few releases." << std::endl; + long_warning_printed = true; + } + return true; + } + } + return false; +} + bool isWorldAligned(AlignStyle style, WorldAlignMode mode, NodeDrawType drawtype) { if (style == ALIGN_STYLE_WORLD) @@ -814,13 +859,19 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc break; case NDT_MESH: case NDT_NODEBOX: + if (alpha == 255 && textureAlphaCheck(tsrc, tdef, 6)) + alpha = 0; + solidness = 0; if (waving == 1) material_type = TILE_MATERIAL_WAVING_PLANTS; else if (waving == 2) material_type = TILE_MATERIAL_WAVING_LEAVES; else if (waving == 3) - material_type = TILE_MATERIAL_WAVING_LIQUID_BASIC; + material_type = (alpha == 255) ? TILE_MATERIAL_WAVING_LIQUID_OPAQUE : + TILE_MATERIAL_WAVING_LIQUID_BASIC; + else if (alpha == 255) + material_type = TILE_MATERIAL_OPAQUE; break; case NDT_TORCHLIKE: case NDT_SIGNLIKE: diff --git a/src/nodedef.h b/src/nodedef.h index d0da367ee..71c56bda9 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -418,6 +418,7 @@ struct ContentFeatures void reset(); void serialize(std::ostream &os, u16 protocol_version) const; void deSerialize(std::istream &is); + /*! * Since vertex alpha is no longer supported, this method * adds opacity directly to the texture pixels. @@ -427,6 +428,17 @@ struct ContentFeatures */ void correctAlpha(TileDef *tiles, int length); +#ifndef SERVER + /* + * Checks if any tile texture has any transparent pixels. + * Prints a warning and returns true if that is the case, false otherwise. + * This is supposed to be used for use_texture_alpha backwards compatibility. + */ + bool textureAlphaCheck(ITextureSource *tsrc, const TileDef *tiles, + int length); +#endif + + /* Some handy methods */ -- cgit v1.2.3 From 291a6b70d674d9003f522b5875a60f7e2753e32b Mon Sep 17 00:00:00 2001 From: ANAND Date: Fri, 5 Jun 2020 18:36:35 +0530 Subject: Allow binding dig, place actions to keys; remove LMB/RMB hardcoding Co-authored-by: Sam Caulfield --- builtin/settingtypes.txt | 14 ++- doc/client_lua_api.txt | 4 +- doc/lua_api.txt | 24 +++-- src/client/client.cpp | 2 +- src/client/content_cao.cpp | 12 +-- src/client/game.cpp | 164 ++++++++++++++++++---------------- src/client/inputhandler.cpp | 166 +++++++++++++++++------------------ src/client/inputhandler.h | 154 +++++++++++--------------------- src/client/keys.h | 2 + src/defaultsettings.cpp | 4 +- src/network/serverpackethandler.cpp | 23 ++--- src/player.h | 12 +-- src/script/lua_api/l_localplayer.cpp | 4 +- src/script/lua_api/l_object.cpp | 9 +- 14 files changed, 284 insertions(+), 310 deletions(-) (limited to 'doc') diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 01736f586..3aa113190 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -110,9 +110,9 @@ doubletap_jump (Double tap jump for fly) bool false # enabled. always_fly_fast (Always fly and fast) bool true -# The time in seconds it takes between repeated right clicks when holding the right -# mouse button. -repeat_rightclick_time (Rightclick repetition interval) float 0.25 0.001 +# The time in seconds it takes between repeated node placements when holding +# the place button. +repeat_place_time (Place repetition interval) float 0.25 0.001 # Automatically jump up single-node obstacles. autojump (Automatic jumping) bool false @@ -182,6 +182,14 @@ keymap_jump (Jump key) key KEY_SPACE # See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 keymap_sneak (Sneak key) key KEY_LSHIFT +# Key for digging. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_dig (Dig key) key KEY_LBUTTON + +# Key for placing. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_place (Place key) key KEY_RBUTTON + # Key for opening the inventory. # See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 keymap_inventory (Inventory key) key KEY_KEY_I diff --git a/doc/client_lua_api.txt b/doc/client_lua_api.txt index 3b0046b4f..4c5231b79 100644 --- a/doc/client_lua_api.txt +++ b/doc/client_lua_api.txt @@ -1100,8 +1100,8 @@ Methods: aux1 = boolean, sneak = boolean, zoom = boolean, - LMB = boolean, - RMB = boolean, + dig = boolean, + place = boolean, } ``` diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 6366a34c3..88d99fcd5 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -6163,15 +6163,23 @@ object you are working with still exists. * Only affects formspecs shown after this is called. * `get_formspec_prepend(formspec)`: returns a formspec string. * `get_player_control()`: returns table with player pressed keys - * The table consists of fields with boolean value representing the pressed - keys, the fields are jump, right, left, LMB, RMB, sneak, aux1, down, up, zoom. - * example: `{jump=false, right=true, left=false, LMB=false, RMB=false, - sneak=true, aux1=false, down=false, up=false, zoom=false}` - * The `zoom` field is available since 5.3 + * The table consists of fields with the following boolean values + representing the pressed keys: `up`, `down`, `left`, `right`, `jump`, + `aux1`, `sneak`, `dig`, `place`, `LMB`, `RMB`, and `zoom`. + * The fields `LMB` and `RMB` are equal to `dig` and `place` respectively, + and exist only to preserve backwards compatibility. * `get_player_control_bits()`: returns integer with bit packed player pressed - keys. - * bit nr/meaning: 0/up, 1/down, 2/left, 3/right, 4/jump, 5/aux1, 6/sneak, - 7/LMB, 8/RMB, 9/zoom (zoom available since 5.3) + keys. Bits: + * 0 - up + * 1 - down + * 2 - left + * 3 - right + * 4 - jump + * 5 - aux1 + * 6 - sneak + * 7 - dig + * 8 - place + * 9 - zoom * `set_physics_override(override_table)` * `override_table` is a table with the following fields: * `speed`: multiplier to default walking speed value (default: `1`) diff --git a/src/client/client.cpp b/src/client/client.cpp index 65e5b3d8c..745cce900 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -1307,7 +1307,7 @@ void Client::sendPlayerPos() player->last_pitch == player->getPitch() && player->last_yaw == player->getYaw() && player->last_keyPressed == player->keyPressed && - player->last_camera_fov == camera_fov && + player->last_camera_fov == camera_fov && player->last_wanted_range == wanted_range) return; diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index 88688d18c..599139aa3 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -975,13 +975,13 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) if (controls.sneak && walking) new_speed /= 2; - if (walking && (controls.LMB || controls.RMB)) { + if (walking && (controls.dig || controls.place)) { new_anim = player->local_animations[3]; player->last_animation = WD_ANIM; - } else if(walking) { + } else if (walking) { new_anim = player->local_animations[1]; player->last_animation = WALK_ANIM; - } else if(controls.LMB || controls.RMB) { + } else if (controls.dig || controls.place) { new_anim = player->local_animations[2]; player->last_animation = DIG_ANIM; } @@ -1004,9 +1004,9 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) // Update local player animations if ((player->last_animation != old_anim || - m_animation_speed != old_anim_speed) && - player->last_animation != NO_ANIM && allow_update) - updateAnimation(); + m_animation_speed != old_anim_speed) && + player->last_animation != NO_ANIM && allow_update) + updateAnimation(); } } diff --git a/src/client/game.cpp b/src/client/game.cpp index 20d2c6f90..0d3a0ca15 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -604,7 +604,6 @@ public: #endif /**************************************************************************** - ****************************************************************************/ const float object_hit_delay = 0.2; @@ -625,15 +624,15 @@ struct GameRunData { u16 new_playeritem; PointedThing pointed_old; bool digging; - bool ldown_for_dig; + bool punching; + bool btn_down_for_dig; bool dig_instantly; bool digging_blocked; - bool left_punch; bool reset_jump_timer; float nodig_delay_timer; float dig_time; float dig_time_complete; - float repeat_rightclick_timer; + float repeat_place_timer; float object_hit_delay_timer; float time_from_last_punch; ClientActiveObject *selected_object; @@ -787,6 +786,14 @@ protected: { return input->wasKeyDown(k); } + inline bool wasKeyPressed(GameKeyType k) + { + return input->wasKeyPressed(k); + } + inline bool wasKeyReleased(GameKeyType k) + { + return input->wasKeyReleased(k); + } #ifdef __ANDROID__ void handleAndroidChatInput(); @@ -900,7 +907,7 @@ private: bool m_cache_enable_free_move; f32 m_cache_mouse_sensitivity; f32 m_cache_joystick_frustum_sensitivity; - f32 m_repeat_right_click_time; + f32 m_repeat_place_time; f32 m_cache_cam_smoothing; f32 m_cache_fog_start; @@ -934,7 +941,7 @@ Game::Game() : &settingChangedCallback, this); g_settings->registerChangedCallback("joystick_frustum_sensitivity", &settingChangedCallback, this); - g_settings->registerChangedCallback("repeat_rightclick_time", + g_settings->registerChangedCallback("repeat_place_time", &settingChangedCallback, this); g_settings->registerChangedCallback("noclip", &settingChangedCallback, this); @@ -992,7 +999,7 @@ Game::~Game() &settingChangedCallback, this); g_settings->deregisterChangedCallback("mouse_sensitivity", &settingChangedCallback, this); - g_settings->deregisterChangedCallback("repeat_rightclick_time", + g_settings->deregisterChangedCallback("repeat_place_time", &settingChangedCallback, this); g_settings->deregisterChangedCallback("noclip", &settingChangedCallback, this); @@ -2465,8 +2472,8 @@ void Game::updatePlayerControl(const CameraOrientation &cam) isKeyDown(KeyType::SPECIAL1), isKeyDown(KeyType::SNEAK), isKeyDown(KeyType::ZOOM), - input->getLeftState(), - input->getRightState(), + isKeyDown(KeyType::DIG), + isKeyDown(KeyType::PLACE), cam.camera_pitch, cam.camera_yaw, input->joystick.getAxisWithoutDead(JA_SIDEWARD_MOVE), @@ -2481,8 +2488,8 @@ void Game::updatePlayerControl(const CameraOrientation &cam) ( (u32)(isKeyDown(KeyType::JUMP) & 0x1) << 4) | ( (u32)(isKeyDown(KeyType::SPECIAL1) & 0x1) << 5) | ( (u32)(isKeyDown(KeyType::SNEAK) & 0x1) << 6) | - ( (u32)(input->getLeftState() & 0x1) << 7) | - ( (u32)(input->getRightState() & 0x1) << 8) | + ( (u32)(isKeyDown(KeyType::DIG) & 0x1) << 7) | + ( (u32)(isKeyDown(KeyType::PLACE) & 0x1) << 8) | ( (u32)(isKeyDown(KeyType::ZOOM) & 0x1) << 9) ); @@ -3064,7 +3071,7 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug) PointedThing pointed = updatePointedThing(shootline, selected_def.liquids_pointable, - !runData.ldown_for_dig, + !runData.btn_down_for_dig, camera_offset); if (pointed != runData.pointed_old) { @@ -3072,20 +3079,18 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug) hud->updateSelectionMesh(camera_offset); } - if (runData.digging_blocked && !input->getLeftState()) { - // allow digging again if button is not pressed + // Allow digging again if button is not pressed + if (runData.digging_blocked && !isKeyDown(KeyType::DIG)) runData.digging_blocked = false; - } /* Stop digging when - - releasing left mouse button + - releasing dig button - pointing away from node */ if (runData.digging) { - if (input->getLeftReleased()) { - infostream << "Left button released" - << " (stopped digging)" << std::endl; + if (wasKeyReleased(KeyType::DIG)) { + infostream << "Dig button released (stopped digging)" << std::endl; runData.digging = false; } else if (pointed != runData.pointed_old) { if (pointed.type == POINTEDTHING_NODE @@ -3095,8 +3100,7 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug) // Still pointing to the same node, but a different face. // Don't reset. } else { - infostream << "Pointing away from node" - << " (stopped digging)" << std::endl; + infostream << "Pointing away from node (stopped digging)" << std::endl; runData.digging = false; hud->updateSelectionMesh(camera_offset); } @@ -3107,55 +3111,57 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug) client->setCrack(-1, v3s16(0, 0, 0)); runData.dig_time = 0.0; } - } else if (runData.dig_instantly && input->getLeftReleased()) { - // Remove e.g. torches faster when clicking instead of holding LMB + } else if (runData.dig_instantly && wasKeyReleased(KeyType::DIG)) { + // Remove e.g. torches faster when clicking instead of holding dig button runData.nodig_delay_timer = 0; runData.dig_instantly = false; } - if (!runData.digging && runData.ldown_for_dig && !input->getLeftState()) { - runData.ldown_for_dig = false; - } + if (!runData.digging && runData.btn_down_for_dig && !isKeyDown(KeyType::DIG)) + runData.btn_down_for_dig = false; - runData.left_punch = false; + runData.punching = false; soundmaker->m_player_leftpunch_sound.name = ""; // Prepare for repeating, unless we're not supposed to - if (input->getRightState() && !g_settings->getBool("safe_dig_and_place")) - runData.repeat_rightclick_timer += dtime; + if (isKeyDown(KeyType::PLACE) && !g_settings->getBool("safe_dig_and_place")) + runData.repeat_place_timer += dtime; else - runData.repeat_rightclick_timer = 0; + runData.repeat_place_timer = 0; - if (selected_def.usable && input->getLeftState()) { - if (input->getLeftClicked() && (!client->modsLoaded() - || !client->getScript()->on_item_use(selected_item, pointed))) + if (selected_def.usable && isKeyDown(KeyType::DIG)) { + if (wasKeyPressed(KeyType::DIG) && (!client->modsLoaded() || + !client->getScript()->on_item_use(selected_item, pointed))) client->interact(INTERACT_USE, pointed); } else if (pointed.type == POINTEDTHING_NODE) { handlePointingAtNode(pointed, selected_item, hand_item, dtime); } else if (pointed.type == POINTEDTHING_OBJECT) { v3f player_position = player->getPosition(); handlePointingAtObject(pointed, tool_item, player_position, show_debug); - } else if (input->getLeftState()) { + } else if (isKeyDown(KeyType::DIG)) { // When button is held down in air, show continuous animation - runData.left_punch = true; + runData.punching = true; // Run callback even though item is not usable - if (input->getLeftClicked() && client->modsLoaded()) + if (wasKeyPressed(KeyType::DIG) && client->modsLoaded()) client->getScript()->on_item_use(selected_item, pointed); - } else if (input->getRightClicked()) { + } else if (wasKeyPressed(KeyType::PLACE)) { handlePointingAtNothing(selected_item); } runData.pointed_old = pointed; - if (runData.left_punch || input->getLeftClicked()) - camera->setDigging(0); // left click animation + if (runData.punching || wasKeyPressed(KeyType::DIG)) + camera->setDigging(0); // dig animation - input->resetLeftClicked(); - input->resetRightClicked(); + input->clearWasKeyPressed(); + input->clearWasKeyReleased(); - input->resetLeftReleased(); - input->resetRightReleased(); + input->joystick.clearWasKeyDown(KeyType::MOUSE_L); + input->joystick.clearWasKeyDown(KeyType::MOUSE_R); + + input->joystick.clearWasKeyReleased(KeyType::MOUSE_L); + input->joystick.clearWasKeyReleased(KeyType::MOUSE_R); } @@ -3255,7 +3261,7 @@ PointedThing Game::updatePointedThing( void Game::handlePointingAtNothing(const ItemStack &playerItem) { - infostream << "Right Clicked in Air" << std::endl; + infostream << "Attempted to place item while pointing at nothing" << std::endl; PointedThing fauxPointed; fauxPointed.type = POINTEDTHING_NOTHING; client->interact(INTERACT_ACTIVATE, fauxPointed); @@ -3274,7 +3280,7 @@ void Game::handlePointingAtNode(const PointedThing &pointed, ClientMap &map = client->getEnv().getClientMap(); - if (runData.nodig_delay_timer <= 0.0 && input->getLeftState() + if (runData.nodig_delay_timer <= 0.0 && isKeyDown(KeyType::DIG) && !runData.digging_blocked && client->checkPrivilege("interact")) { handleDigging(pointed, nodepos, selected_item, hand_item, dtime); @@ -3295,13 +3301,14 @@ void Game::handlePointingAtNode(const PointedThing &pointed, } } - if ((input->getRightClicked() || - runData.repeat_rightclick_timer >= m_repeat_right_click_time) && + if ((wasKeyPressed(KeyType::PLACE) || + runData.repeat_place_timer >= m_repeat_place_time) && client->checkPrivilege("interact")) { - runData.repeat_rightclick_timer = 0; - infostream << "Ground right-clicked" << std::endl; + runData.repeat_place_timer = 0; + infostream << "Place button pressed while looking at ground" << std::endl; - camera->setDigging(1); // right click animation (always shown for feedback) + // Placing animation (always shown for feedback) + camera->setDigging(1); soundmaker->m_player_rightpunch_sound = SimpleSoundSpec(); @@ -3367,8 +3374,7 @@ bool Game::nodePlacement(const ItemDefinition &selected_def, } verbosestream << "Node placement prediction for " - << selected_def.name << " is " - << prediction << std::endl; + << selected_def.name << " is " << prediction << std::endl; v3s16 p = neighbourpos; // Place inside node itself if buildable_to @@ -3529,7 +3535,7 @@ void Game::handlePointingAtObject(const PointedThing &pointed, m_game_ui->setInfoText(infotext); - if (input->getLeftState()) { + if (isKeyDown(KeyType::DIG)) { bool do_punch = false; bool do_punch_damage = false; @@ -3539,12 +3545,12 @@ void Game::handlePointingAtObject(const PointedThing &pointed, runData.object_hit_delay_timer = object_hit_delay; } - if (input->getLeftClicked()) + if (wasKeyPressed(KeyType::DIG)) do_punch = true; if (do_punch) { - infostream << "Left-clicked object" << std::endl; - runData.left_punch = true; + infostream << "Punched object" << std::endl; + runData.punching = true; } if (do_punch_damage) { @@ -3559,8 +3565,8 @@ void Game::handlePointingAtObject(const PointedThing &pointed, if (!disable_send) client->interact(INTERACT_START_DIGGING, pointed); } - } else if (input->getRightClicked()) { - infostream << "Right-clicked object" << std::endl; + } else if (wasKeyDown(KeyType::PLACE)) { + infostream << "Pressed place button while pointing at object" << std::endl; client->interact(INTERACT_PLACE, pointed); // place } } @@ -3606,7 +3612,7 @@ void Game::handleDigging(const PointedThing &pointed, const v3s16 &nodepos, return; client->interact(INTERACT_START_DIGGING, pointed); runData.digging = true; - runData.ldown_for_dig = true; + runData.btn_down_for_dig = true; } if (!runData.dig_instantly) { @@ -3700,7 +3706,7 @@ void Game::handleDigging(const PointedThing &pointed, const v3s16 &nodepos, client->setCrack(-1, nodepos); } - camera->setDigging(0); // left click animation + camera->setDigging(0); // Dig animation } @@ -4039,7 +4045,7 @@ void Game::readSettings() m_cache_enable_fog = g_settings->getBool("enable_fog"); m_cache_mouse_sensitivity = g_settings->getFloat("mouse_sensitivity"); m_cache_joystick_frustum_sensitivity = g_settings->getFloat("joystick_frustum_sensitivity"); - m_repeat_right_click_time = g_settings->getFloat("repeat_rightclick_time"); + m_repeat_place_time = g_settings->getFloat("repeat_place_time"); m_cache_enable_noclip = g_settings->getBool("noclip"); m_cache_enable_free_move = g_settings->getBool("free_move"); @@ -4131,30 +4137,32 @@ void Game::showPauseMenu() "- %s: move backwards\n" "- %s: move left\n" "- %s: move right\n" - "- %s: jump/climb\n" - "- %s: sneak/go down\n" + "- %s: jump/climb up\n" + "- %s: dig/punch\n" + "- %s: place/use\n" + "- %s: sneak/climb down\n" "- %s: drop item\n" "- %s: inventory\n" "- Mouse: turn/look\n" - "- Mouse left: dig/punch\n" - "- Mouse right: place/use\n" "- Mouse wheel: select item\n" "- %s: chat\n" ); - char control_text_buf[600]; - - porting::mt_snprintf(control_text_buf, sizeof(control_text_buf), control_text_template.c_str(), - GET_KEY_NAME(keymap_forward), - GET_KEY_NAME(keymap_backward), - GET_KEY_NAME(keymap_left), - GET_KEY_NAME(keymap_right), - GET_KEY_NAME(keymap_jump), - GET_KEY_NAME(keymap_sneak), - GET_KEY_NAME(keymap_drop), - GET_KEY_NAME(keymap_inventory), - GET_KEY_NAME(keymap_chat) - ); + char control_text_buf[600]; + + porting::mt_snprintf(control_text_buf, sizeof(control_text_buf), control_text_template.c_str(), + GET_KEY_NAME(keymap_forward), + GET_KEY_NAME(keymap_backward), + GET_KEY_NAME(keymap_left), + GET_KEY_NAME(keymap_right), + GET_KEY_NAME(keymap_jump), + GET_KEY_NAME(keymap_dig), + GET_KEY_NAME(keymap_place), + GET_KEY_NAME(keymap_sneak), + GET_KEY_NAME(keymap_drop), + GET_KEY_NAME(keymap_inventory), + GET_KEY_NAME(keymap_chat) + ); std::string control_text = std::string(control_text_buf); str_formspec_escape(control_text); diff --git a/src/client/inputhandler.cpp b/src/client/inputhandler.cpp index a79b04a90..608a405a8 100644 --- a/src/client/inputhandler.cpp +++ b/src/client/inputhandler.cpp @@ -37,6 +37,8 @@ void KeyCache::populate() key[KeyType::JUMP] = getKeySetting("keymap_jump"); key[KeyType::SPECIAL1] = getKeySetting("keymap_special1"); key[KeyType::SNEAK] = getKeySetting("keymap_sneak"); + key[KeyType::DIG] = getKeySetting("keymap_dig"); + key[KeyType::PLACE] = getKeySetting("keymap_place"); key[KeyType::AUTOFORWARD] = getKeySetting("keymap_autoforward"); @@ -111,57 +113,81 @@ bool MyEventReceiver::OnEvent(const SEvent &event) if (event.EventType == irr::EET_KEY_INPUT_EVENT) { const KeyPress &keyCode = event.KeyInput; if (keysListenedFor[keyCode]) { + // If the key is being held down then the OS may + // send a continuous stream of keydown events. + // In this case, we don't want to let this + // stream reach the application as it will cause + // certain actions to repeat constantly. if (event.KeyInput.PressedDown) { + if (!IsKeyDown(keyCode)) { + keyWasDown.set(keyCode); + keyWasPressed.set(keyCode); + } keyIsDown.set(keyCode); - keyWasDown.set(keyCode); } else { + if (IsKeyDown(keyCode)) + keyWasReleased.set(keyCode); + keyIsDown.unset(keyCode); } + return true; } - } #ifdef HAVE_TOUCHSCREENGUI - // case of touchscreengui we have to handle different events - if (m_touchscreengui && event.EventType == irr::EET_TOUCH_INPUT_EVENT) { + } else if (m_touchscreengui && event.EventType == irr::EET_TOUCH_INPUT_EVENT) { + // In case of touchscreengui, we have to handle different events m_touchscreengui->translateEvent(event); return true; - } #endif - if (event.EventType == irr::EET_JOYSTICK_INPUT_EVENT) { + } else if (event.EventType == irr::EET_JOYSTICK_INPUT_EVENT) { /* TODO add a check like: if (event.JoystickEvent != joystick_we_listen_for) return false; */ return joystick->handleEvent(event.JoystickEvent); - } - // handle mouse events - if (event.EventType == irr::EET_MOUSE_INPUT_EVENT) { - if (isMenuActive()) { - left_active = false; - middle_active = false; - right_active = false; - } else { - left_active = event.MouseInput.isLeftPressed(); - middle_active = event.MouseInput.isMiddlePressed(); - right_active = event.MouseInput.isRightPressed(); - - if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) { - leftclicked = true; - } - if (event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN) { - rightclicked = true; - } - if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) { - leftreleased = true; - } - if (event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP) { - rightreleased = true; - } - if (event.MouseInput.Event == EMIE_MOUSE_WHEEL) { - mouse_wheel += event.MouseInput.Wheel; - } + } else if (event.EventType == irr::EET_MOUSE_INPUT_EVENT) { + // Handle mouse events + KeyPress key; + switch (event.MouseInput.Event) { + case EMIE_LMOUSE_PRESSED_DOWN: + key = "KEY_LBUTTON"; + keyIsDown.set(key); + keyWasDown.set(key); + keyWasPressed.set(key); + break; + case EMIE_MMOUSE_PRESSED_DOWN: + key = "KEY_MBUTTON"; + keyIsDown.set(key); + keyWasDown.set(key); + keyWasPressed.set(key); + break; + case EMIE_RMOUSE_PRESSED_DOWN: + key = "KEY_RBUTTON"; + keyIsDown.set(key); + keyWasDown.set(key); + keyWasPressed.set(key); + break; + case EMIE_LMOUSE_LEFT_UP: + key = "KEY_LBUTTON"; + keyIsDown.unset(key); + keyWasReleased.set(key); + break; + case EMIE_MMOUSE_LEFT_UP: + key = "KEY_MBUTTON"; + keyIsDown.unset(key); + keyWasReleased.set(key); + break; + case EMIE_RMOUSE_LEFT_UP: + key = "KEY_RBUTTON"; + keyIsDown.unset(key); + keyWasReleased.set(key); + break; + case EMIE_MOUSE_WHEEL: + mouse_wheel += event.MouseInput.Wheel; + break; + default: break; } } else if (event.EventType == irr::EET_LOG_TEXT_EVENT) { static const LogLevel irr_loglev_conv[] = { @@ -188,38 +214,28 @@ s32 RandomInputHandler::Rand(s32 min, s32 max) return (myrand() % (max - min + 1)) + min; } +struct RandomInputHandlerSimData { + std::string key; + float counter; + int time_max; +}; + void RandomInputHandler::step(float dtime) { - { - static float counter1 = 0; - counter1 -= dtime; - if (counter1 < 0.0) { - counter1 = 0.1 * Rand(1, 40); - keydown.toggle(getKeySetting("keymap_jump")); - } - } - { - static float counter1 = 0; - counter1 -= dtime; - if (counter1 < 0.0) { - counter1 = 0.1 * Rand(1, 40); - keydown.toggle(getKeySetting("keymap_special1")); - } - } - { - static float counter1 = 0; - counter1 -= dtime; - if (counter1 < 0.0) { - counter1 = 0.1 * Rand(1, 40); - keydown.toggle(getKeySetting("keymap_forward")); - } - } - { - static float counter1 = 0; - counter1 -= dtime; - if (counter1 < 0.0) { - counter1 = 0.1 * Rand(1, 40); - keydown.toggle(getKeySetting("keymap_left")); + static RandomInputHandlerSimData rnd_data[] = { + { "keymap_jump", 0.0f, 40 }, + { "keymap_special1", 0.0f, 40 }, + { "keymap_forward", 0.0f, 40 }, + { "keymap_left", 0.0f, 40 }, + { "keymap_dig", 0.0f, 30 }, + { "keymap_place", 0.0f, 15 } + }; + + for (auto &i : rnd_data) { + i.counter -= dtime; + if (i.counter < 0.0) { + i.counter = 0.1 * Rand(1, i.time_max); + keydown.toggle(getKeySetting(i.key.c_str())); } } { @@ -230,29 +246,5 @@ void RandomInputHandler::step(float dtime) mousespeed = v2s32(Rand(-20, 20), Rand(-15, 20)); } } - { - static float counter1 = 0; - counter1 -= dtime; - if (counter1 < 0.0) { - counter1 = 0.1 * Rand(1, 30); - leftdown = !leftdown; - if (leftdown) - leftclicked = true; - if (!leftdown) - leftreleased = true; - } - } - { - static float counter1 = 0; - counter1 -= dtime; - if (counter1 < 0.0) { - counter1 = 0.1 * Rand(1, 15); - rightdown = !rightdown; - if (rightdown) - rightclicked = true; - if (!rightdown) - rightreleased = true; - } - } mousepos += mousespeed; } diff --git a/src/client/inputhandler.h b/src/client/inputhandler.h index fc7998f20..def147a82 100644 --- a/src/client/inputhandler.h +++ b/src/client/inputhandler.h @@ -144,6 +144,14 @@ public: return b; } + // Checks whether a key was just pressed. State will be cleared + // in the subsequent iteration of Game::processPlayerInteraction + bool WasKeyPressed(const KeyPress &keycode) const { return keyWasPressed[keycode]; } + + // Checks whether a key was just released. State will be cleared + // in the subsequent iteration of Game::processPlayerInteraction + bool WasKeyReleased(const KeyPress &keycode) const { return keyWasReleased[keycode]; } + void listenForKey(const KeyPress &keyCode) { keysListenedFor.set(keyCode); } void dontListenForKeys() { keysListenedFor.clear(); } @@ -158,17 +166,20 @@ public: { keyIsDown.clear(); keyWasDown.clear(); + keyWasPressed.clear(); + keyWasReleased.clear(); - leftclicked = false; - rightclicked = false; - leftreleased = false; - rightreleased = false; + mouse_wheel = 0; + } - left_active = false; - middle_active = false; - right_active = false; + void clearWasKeyPressed() + { + keyWasPressed.clear(); + } - mouse_wheel = 0; + void clearWasKeyReleased() + { + keyWasReleased.clear(); } MyEventReceiver() @@ -178,15 +189,6 @@ public: #endif } - bool leftclicked = false; - bool rightclicked = false; - bool leftreleased = false; - bool rightreleased = false; - - bool left_active = false; - bool middle_active = false; - bool right_active = false; - s32 mouse_wheel = 0; JoystickController *joystick = nullptr; @@ -198,8 +200,16 @@ public: private: // The current state of keys KeyList keyIsDown; - // Whether a key has been pressed or not + + // Whether a key was down KeyList keyWasDown; + + // Whether a key has just been pressed + KeyList keyWasPressed; + + // Whether a key has just been released + KeyList keyWasReleased; + // List of keys we listen for // TODO perhaps the type of this is not really // performant as KeyList is designed for few but @@ -226,27 +236,19 @@ public: virtual bool isKeyDown(GameKeyType k) = 0; virtual bool wasKeyDown(GameKeyType k) = 0; + virtual bool wasKeyPressed(GameKeyType k) = 0; + virtual bool wasKeyReleased(GameKeyType k) = 0; virtual bool cancelPressed() = 0; + virtual void clearWasKeyPressed() {} + virtual void clearWasKeyReleased() {} + virtual void listenForKey(const KeyPress &keyCode) {} virtual void dontListenForKeys() {} virtual v2s32 getMousePos() = 0; virtual void setMousePos(s32 x, s32 y) = 0; - virtual bool getLeftState() = 0; - virtual bool getRightState() = 0; - - virtual bool getLeftClicked() = 0; - virtual bool getRightClicked() = 0; - virtual void resetLeftClicked() = 0; - virtual void resetRightClicked() = 0; - - virtual bool getLeftReleased() = 0; - virtual bool getRightReleased() = 0; - virtual void resetLeftReleased() = 0; - virtual void resetRightReleased() = 0; - virtual s32 getMouseWheel() = 0; virtual void step(float dtime) {} @@ -275,10 +277,26 @@ public: { return m_receiver->WasKeyDown(keycache.key[k]) || joystick.wasKeyDown(k); } + virtual bool wasKeyPressed(GameKeyType k) + { + return m_receiver->WasKeyPressed(keycache.key[k]) || joystick.wasKeyReleased(k); + } + virtual bool wasKeyReleased(GameKeyType k) + { + return m_receiver->WasKeyReleased(keycache.key[k]) || joystick.wasKeyReleased(k); + } virtual bool cancelPressed() { return wasKeyDown(KeyType::ESC) || m_receiver->WasKeyDown(CancelKey); } + virtual void clearWasKeyPressed() + { + m_receiver->clearWasKeyPressed(); + } + virtual void clearWasKeyReleased() + { + m_receiver->clearWasKeyReleased(); + } virtual void listenForKey(const KeyPress &keyCode) { m_receiver->listenForKey(keyCode); @@ -306,59 +324,6 @@ public: } } - virtual bool getLeftState() - { - return m_receiver->left_active || joystick.isKeyDown(KeyType::MOUSE_L); - } - virtual bool getRightState() - { - return m_receiver->right_active || joystick.isKeyDown(KeyType::MOUSE_R); - } - - virtual bool getLeftClicked() - { - return m_receiver->leftclicked || - joystick.getWasKeyDown(KeyType::MOUSE_L); - } - virtual bool getRightClicked() - { - return m_receiver->rightclicked || - joystick.getWasKeyDown(KeyType::MOUSE_R); - } - - virtual void resetLeftClicked() - { - m_receiver->leftclicked = false; - joystick.clearWasKeyDown(KeyType::MOUSE_L); - } - virtual void resetRightClicked() - { - m_receiver->rightclicked = false; - joystick.clearWasKeyDown(KeyType::MOUSE_R); - } - - virtual bool getLeftReleased() - { - return m_receiver->leftreleased || - joystick.wasKeyReleased(KeyType::MOUSE_L); - } - virtual bool getRightReleased() - { - return m_receiver->rightreleased || - joystick.wasKeyReleased(KeyType::MOUSE_R); - } - - virtual void resetLeftReleased() - { - m_receiver->leftreleased = false; - joystick.clearWasKeyReleased(KeyType::MOUSE_L); - } - virtual void resetRightReleased() - { - m_receiver->rightreleased = false; - joystick.clearWasKeyReleased(KeyType::MOUSE_R); - } - virtual s32 getMouseWheel() { return m_receiver->getMouseWheel(); } void clear() @@ -384,23 +349,12 @@ public: virtual bool isKeyDown(GameKeyType k) { return keydown[keycache.key[k]]; } virtual bool wasKeyDown(GameKeyType k) { return false; } + virtual bool wasKeyPressed(GameKeyType k) { return false; } + virtual bool wasKeyReleased(GameKeyType k) { return false; } virtual bool cancelPressed() { return false; } virtual v2s32 getMousePos() { return mousepos; } virtual void setMousePos(s32 x, s32 y) { mousepos = v2s32(x, y); } - virtual bool getLeftState() { return leftdown; } - virtual bool getRightState() { return rightdown; } - - virtual bool getLeftClicked() { return leftclicked; } - virtual bool getRightClicked() { return rightclicked; } - virtual void resetLeftClicked() { leftclicked = false; } - virtual void resetRightClicked() { rightclicked = false; } - - virtual bool getLeftReleased() { return leftreleased; } - virtual bool getRightReleased() { return rightreleased; } - virtual void resetLeftReleased() { leftreleased = false; } - virtual void resetRightReleased() { rightreleased = false; } - virtual s32 getMouseWheel() { return 0; } virtual void step(float dtime); @@ -411,10 +365,4 @@ private: KeyList keydown; v2s32 mousepos; v2s32 mousespeed; - bool leftdown = false; - bool rightdown = false; - bool leftclicked = false; - bool rightclicked = false; - bool leftreleased = false; - bool rightreleased = false; }; diff --git a/src/client/keys.h b/src/client/keys.h index 50d3d194b..b6ce59b4a 100644 --- a/src/client/keys.h +++ b/src/client/keys.h @@ -35,6 +35,8 @@ public: SPECIAL1, SNEAK, AUTOFORWARD, + DIG, + PLACE, ESC, diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 07bf0ebb8..103f0fb02 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -73,6 +73,8 @@ void set_default_settings(Settings *settings) settings->setDefault("keymap_right", "KEY_KEY_D"); settings->setDefault("keymap_jump", "KEY_SPACE"); settings->setDefault("keymap_sneak", "KEY_LSHIFT"); + settings->setDefault("keymap_dig", "KEY_LBUTTON"); + settings->setDefault("keymap_place", "KEY_RBUTTON"); settings->setDefault("keymap_drop", "KEY_KEY_Q"); settings->setDefault("keymap_zoom", "KEY_KEY_Z"); settings->setDefault("keymap_inventory", "KEY_KEY_I"); @@ -269,7 +271,7 @@ void set_default_settings(Settings *settings) // Input settings->setDefault("invert_mouse", "false"); settings->setDefault("mouse_sensitivity", "0.2"); - settings->setDefault("repeat_rightclick_time", "0.25"); + settings->setDefault("repeat_place_time", "0.25"); settings->setDefault("safe_dig_and_place", "false"); settings->setDefault("random_input", "false"); settings->setDefault("aux1_descends", "false"); diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index b3008bb50..dcbb114bf 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -491,17 +491,18 @@ void Server::process_PlayerPos(RemotePlayer *player, PlayerSAO *playersao, playersao->setPlayerYaw(yaw); playersao->setFov(fov); playersao->setWantedRange(wanted_range); + player->keyPressed = keyPressed; - player->control.up = (keyPressed & 1); - player->control.down = (keyPressed & 2); - player->control.left = (keyPressed & 4); - player->control.right = (keyPressed & 8); - player->control.jump = (keyPressed & 16); - player->control.aux1 = (keyPressed & 32); - player->control.sneak = (keyPressed & 64); - player->control.LMB = (keyPressed & 128); - player->control.RMB = (keyPressed & 256); - player->control.zoom = (keyPressed & 512); + player->control.up = (keyPressed & (0x1 << 0)); + player->control.down = (keyPressed & (0x1 << 1)); + player->control.left = (keyPressed & (0x1 << 2)); + player->control.right = (keyPressed & (0x1 << 3)); + player->control.jump = (keyPressed & (0x1 << 4)); + player->control.aux1 = (keyPressed & (0x1 << 5)); + player->control.sneak = (keyPressed & (0x1 << 6)); + player->control.dig = (keyPressed & (0x1 << 7)); + player->control.place = (keyPressed & (0x1 << 8)); + player->control.zoom = (keyPressed & (0x1 << 9)); if (playersao->checkMovementCheat()) { // Call callbacks @@ -1670,7 +1671,7 @@ void Server::handleCommand_SrpBytesM(NetworkPacket* pkt) if (client->chosen_mech != AUTH_MECHANISM_SRP && client->chosen_mech != AUTH_MECHANISM_LEGACY_PASSWORD) { actionstream << "Server: got SRP _M packet, while auth" - << "is going on with mech " << client->chosen_mech << " from " + << "is going on with mech " << client->chosen_mech << " from " << addr_s << " (wantSudo=" << wantSudo << "). Denying." << std::endl; if (wantSudo) { DenySudoAccess(peer_id); diff --git a/src/player.h b/src/player.h index 3bc7762fa..ec068a8b1 100644 --- a/src/player.h +++ b/src/player.h @@ -57,8 +57,8 @@ struct PlayerControl bool a_aux1, bool a_sneak, bool a_zoom, - bool a_LMB, - bool a_RMB, + bool a_dig, + bool a_place, float a_pitch, float a_yaw, float a_sidew_move_joystick_axis, @@ -73,8 +73,8 @@ struct PlayerControl aux1 = a_aux1; sneak = a_sneak; zoom = a_zoom; - LMB = a_LMB; - RMB = a_RMB; + dig = a_dig; + place = a_place; pitch = a_pitch; yaw = a_yaw; sidew_move_joystick_axis = a_sidew_move_joystick_axis; @@ -88,8 +88,8 @@ struct PlayerControl bool aux1 = false; bool sneak = false; bool zoom = false; - bool LMB = false; - bool RMB = false; + bool dig = false; + bool place = false; float pitch = 0.0f; float yaw = 0.0f; float sidew_move_joystick_axis = 0.0f; diff --git a/src/script/lua_api/l_localplayer.cpp b/src/script/lua_api/l_localplayer.cpp index 851ede535..33fa27c8b 100644 --- a/src/script/lua_api/l_localplayer.cpp +++ b/src/script/lua_api/l_localplayer.cpp @@ -231,8 +231,8 @@ int LuaLocalPlayer::l_get_control(lua_State *L) set("aux1", c.aux1); set("sneak", c.sneak); set("zoom", c.zoom); - set("LMB", c.LMB); - set("RMB", c.RMB); + set("dig", c.dig); + set("place", c.place); return 1; } diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index e7394133a..495d8bced 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -1455,9 +1455,14 @@ int ObjectRef::l_get_player_control(lua_State *L) lua_setfield(L, -2, "aux1"); lua_pushboolean(L, control.sneak); lua_setfield(L, -2, "sneak"); - lua_pushboolean(L, control.LMB); + lua_pushboolean(L, control.dig); + lua_setfield(L, -2, "dig"); + lua_pushboolean(L, control.place); + lua_setfield(L, -2, "place"); + // Legacy fields to ensure mod compatibility + lua_pushboolean(L, control.dig); lua_setfield(L, -2, "LMB"); - lua_pushboolean(L, control.RMB); + lua_pushboolean(L, control.place); lua_setfield(L, -2, "RMB"); lua_pushboolean(L, control.zoom); lua_setfield(L, -2, "zoom"); -- cgit v1.2.3 From 5bda36143f9bd332b6ba420d9b91be1713092eae Mon Sep 17 00:00:00 2001 From: hecks <42101236+hecktest@users.noreply.github.com> Date: Wed, 19 Aug 2020 19:26:37 +0200 Subject: Clean up sound_fade (#10119) Add proper documentation and correct gain reduction calculations. Co-authored-by: hecktest <> --- doc/lua_api.txt | 7 ++++-- src/client/sound_openal.cpp | 55 ++++++++++++++++++++++++--------------------- 2 files changed, 35 insertions(+), 27 deletions(-) (limited to 'doc') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 88d99fcd5..ba55033dd 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -5238,9 +5238,12 @@ Sounds * `minetest.sound_fade(handle, step, gain)` * `handle` is a handle returned by `minetest.sound_play` * `step` determines how fast a sound will fade. - Negative step will lower the sound volume, positive step will increase - the sound volume. + The gain will change by this much per second, + until it reaches the target gain. + Note: Older versions used a signed step. This is deprecated, but old + code will still work. (the client uses abs(step) to correct it) * `gain` the target gain for the fade. + Fading to zero will delete the sound. Timing ------ diff --git a/src/client/sound_openal.cpp b/src/client/sound_openal.cpp index 20a651c1d..c1c916e68 100644 --- a/src/client/sound_openal.cpp +++ b/src/client/sound_openal.cpp @@ -337,14 +337,12 @@ private: }; std::unordered_map m_sounds_fading; - float m_fade_delay; public: OpenALSoundManager(SoundManagerSingleton *smg, OnDemandSoundFetcher *fetcher): m_fetcher(fetcher), m_device(smg->m_device.get()), m_context(smg->m_context.get()), - m_next_id(1), - m_fade_delay(0) + m_next_id(1) { infostream << "Audio: Initialized: OpenAL " << std::endl; } @@ -616,38 +614,45 @@ public: void fadeSound(int soundid, float step, float gain) { - m_sounds_fading[soundid] = FadeState(step, getSoundGain(soundid), gain); + // Ignore the command if step isn't valid. + if (step == 0) + return; + float current_gain = getSoundGain(soundid); + step = gain - current_gain > 0 ? abs(step) : -abs(step); + if (m_sounds_fading.find(soundid) != m_sounds_fading.end()) { + auto current_fade = m_sounds_fading[soundid]; + // Do not replace the fade if it's equivalent. + if (current_fade.target_gain == gain && current_fade.step == step) + return; + m_sounds_fading.erase(soundid); + } + gain = rangelim(gain, 0, 1); + m_sounds_fading[soundid] = FadeState(step, current_gain, gain); } void doFades(float dtime) { - m_fade_delay += dtime; - - if (m_fade_delay < 0.1f) - return; + for (auto i = m_sounds_fading.begin(); i != m_sounds_fading.end();) { + FadeState& fade = i->second; + assert(fade.step != 0); + fade.current_gain += (fade.step * dtime); - float chkGain = 0; - for (auto i = m_sounds_fading.begin(); - i != m_sounds_fading.end();) { - if (i->second.step < 0.f) - chkGain = -(i->second.current_gain); + if (fade.step < 0.f) + fade.current_gain = std::max(fade.current_gain, fade.target_gain); else - chkGain = i->second.current_gain; + fade.current_gain = std::min(fade.current_gain, fade.target_gain); - if (chkGain < i->second.target_gain) { - i->second.current_gain += (i->second.step * m_fade_delay); - i->second.current_gain = rangelim(i->second.current_gain, 0, 1); - - updateSoundGain(i->first, i->second.current_gain); - ++i; - } else { - if (i->second.target_gain <= 0.f) - stopSound(i->first); + if (fade.current_gain <= 0.f) + stopSound(i->first); + else + updateSoundGain(i->first, fade.current_gain); + // The increment must happen during the erase call, or else it'll segfault. + if (fade.current_gain == fade.target_gain) m_sounds_fading.erase(i++); - } + else + i++; } - m_fade_delay = 0; } bool soundExists(int sound) -- cgit v1.2.3 From 83d0c360cc648e02650aa4d341dbf5205c1e708f Mon Sep 17 00:00:00 2001 From: v-rob Date: Wed, 19 Aug 2020 18:14:47 -0700 Subject: Add gradients and borders to FormSpec boxes (#8676) --- doc/lua_api.txt | 43 +++++++++++++++-------- src/gui/StyleSpec.h | 64 +++++++++++++++++++++++++++++++++ src/gui/guiBox.cpp | 86 ++++++++++++++++++++++++++++++++++++++++++--- src/gui/guiBox.h | 11 ++++-- src/gui/guiFormSpecMenu.cpp | 61 ++++++++++++++++++-------------- 5 files changed, 218 insertions(+), 47 deletions(-) (limited to 'doc') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index ba55033dd..49fbe0d94 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2442,6 +2442,8 @@ Elements * Simple colored box * `color` is color specified as a `ColorString`. If the alpha component is left blank, the box will be semitransparent. + If the color is not specified, the box will use the options specified by + its style. If the color is specified, all styling options will be ignored. ### `dropdown[,;;;,, ...,;;]` @@ -2708,21 +2710,23 @@ Setting a property to nothing will reset it to the default value. For example: Some types may inherit styles from parent types. * animated_image, inherits from image +* box * button * button_exit, inherits from button * checkbox -* scrollbar -* table -* textlist * dropdown * field -* pwdfield, inherits from field -* textarea -* label -* vertlabel, inherits from field +* image * image_button * item_image_button +* label +* pwdfield, inherits from field +* scrollbar * tabheader +* table +* textarea +* textlist +* vertlabel, inherits from label ### Valid Properties @@ -2731,7 +2735,18 @@ Some types may inherit styles from parent types. * noclip - boolean, set to true to allow the element to exceed formspec bounds. * box * noclip - boolean, set to true to allow the element to exceed formspec bounds. - * Default to false in formspec_version version 3 or higher + * Defaults to false in formspec_version version 3 or higher + * **Note**: `colors`, `bordercolors`, and `borderwidths` accept multiple input types: + * Single value (e.g. `#FF0`): All corners/borders. + * Two values (e.g. `red,#FFAAFF`): top-left and bottom-right,top-right and bottom-left/ + top and bottom,left and right. + * Four values (e.g. `blue,#A0F,green,#FFFA`): top-left/top and rotates clockwise. + * These work similarly to CSS borders. + * colors - `ColorString`. Sets the color(s) of the box corners. Default `black`. + * bordercolors - `ColorString`. Sets the color(s) of the borders. Default `black`. + * borderwidths - Integer. Sets the width(s) of the borders in pixels. If the width is + negative, the border will extend inside the box, whereas positive extends outside + the box. A width of zero results in no border; this is default. * button, button_exit, image_button, item_image_button * alpha - boolean, whether to draw alpha in bgimg. Default true. * bgcolor - color, sets button tint. @@ -2767,12 +2782,6 @@ Some types may inherit styles from parent types. * textcolor - color, default white. * checkbox * noclip - boolean, set to true to allow the element to exceed formspec bounds. -* scrollbar - * noclip - boolean, set to true to allow the element to exceed formspec bounds. -* table, textlist - * font - Sets font type. See button `font` property for more information. - * font_size - Sets font size. See button `font_size` property for more information. - * noclip - boolean, set to true to allow the element to exceed formspec bounds. * dropdown * noclip - boolean, set to true to allow the element to exceed formspec bounds. * field, pwdfield, textarea @@ -2797,9 +2806,15 @@ Some types may inherit styles from parent types. * fgimg_pressed - image when pressed. Defaults to fgimg when not provided. * This is deprecated, use states instead. * NOTE: The parameters of any given image_button will take precedence over fgimg/fgimg_pressed +* scrollbar + * noclip - boolean, set to true to allow the element to exceed formspec bounds. * tabheader * noclip - boolean, set to true to allow the element to exceed formspec bounds. * textcolor - color. Default white. +* table, textlist + * font - Sets font type. See button `font` property for more information. + * font_size - Sets font size. See button `font_size` property for more information. + * noclip - boolean, set to true to allow the element to exceed formspec bounds. ### Valid States diff --git a/src/gui/StyleSpec.h b/src/gui/StyleSpec.h index 67caf4f7b..36ad51a89 100644 --- a/src/gui/StyleSpec.h +++ b/src/gui/StyleSpec.h @@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/string.h" #include #include +#include #pragma once @@ -50,6 +51,9 @@ public: PADDING, FONT, FONT_SIZE, + COLORS, + BORDERCOLORS, + BORDERWIDTHS, NUM_PROPERTIES, NONE }; @@ -106,6 +110,12 @@ public: return FONT; } else if (name == "font_size") { return FONT_SIZE; + } else if (name == "colors") { + return COLORS; + } else if (name == "bordercolors") { + return BORDERCOLORS; + } else if (name == "borderwidths") { + return BORDERWIDTHS; } else { return NONE; } @@ -187,6 +197,42 @@ public: return color; } + std::array getColorArray(Property prop, + std::array def) const + { + const auto &val = properties[prop]; + if (val.empty()) + return def; + + std::vector strs; + if (!parseArray(val, strs)) + return def; + + for (size_t i = 0; i <= 3; i++) { + video::SColor color; + if (parseColorString(strs[i], color, false, 0xff)) + def[i] = color; + } + + return def; + } + + std::array getIntArray(Property prop, std::array def) const + { + const auto &val = properties[prop]; + if (val.empty()) + return def; + + std::vector strs; + if (!parseArray(val, strs)) + return def; + + for (size_t i = 0; i <= 3; i++) + def[i] = stoi(strs[i]); + + return def; + } + irr::core::rect getRect(Property prop, irr::core::rect def) const { const auto &val = properties[prop]; @@ -334,6 +380,24 @@ public: } private: + bool parseArray(const std::string &value, std::vector &arr) const + { + std::vector strs = split(value, ','); + + if (strs.size() == 1) { + arr = {strs[0], strs[0], strs[0], strs[0]}; + } else if (strs.size() == 2) { + arr = {strs[0], strs[1], strs[0], strs[1]}; + } else if (strs.size() == 4) { + arr = strs; + } else { + warningstream << "Invalid array size (" << strs.size() + << " arguments): \"" << value << "\"" << std::endl; + return false; + } + return true; + } + bool parseRect(const std::string &value, irr::core::rect *parsed_rect) const { irr::core::rect rect; diff --git a/src/gui/guiBox.cpp b/src/gui/guiBox.cpp index 7f329cc32..99a115daf 100644 --- a/src/gui/guiBox.cpp +++ b/src/gui/guiBox.cpp @@ -20,9 +20,14 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "guiBox.h" GUIBox::GUIBox(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id, - const core::rect &rectangle, const video::SColor &color) : + const core::rect &rectangle, + const std::array &colors, + const std::array &bordercolors, + const std::array &borderwidths) : gui::IGUIElement(gui::EGUIET_ELEMENT, env, parent, id, rectangle), - m_color(color) + m_colors(colors), + m_bordercolors(bordercolors), + m_borderwidths(borderwidths) { } @@ -31,8 +36,81 @@ void GUIBox::draw() if (!IsVisible) return; - Environment->getVideoDriver()->draw2DRectangle(m_color, AbsoluteRect, - &AbsoluteClippingRect); + std::array negative_borders = {0, 0, 0, 0}; + std::array positive_borders = {0, 0, 0, 0}; + + for (size_t i = 0; i <= 3; i++) { + if (m_borderwidths[i] > 0) + positive_borders[i] = m_borderwidths[i]; + else + negative_borders[i] = m_borderwidths[i]; + } + + v2s32 upperleft = AbsoluteRect.UpperLeftCorner; + v2s32 lowerright = AbsoluteRect.LowerRightCorner; + + v2s32 topleft_border = { + upperleft.X - positive_borders[3], + upperleft.Y - positive_borders[0] + }; + v2s32 topleft_rect = { + upperleft.X - negative_borders[3], + upperleft.Y - negative_borders[0] + }; + + v2s32 lowerright_border = { + lowerright.X + positive_borders[1], + lowerright.Y + positive_borders[2] + }; + v2s32 lowerright_rect = { + lowerright.X + negative_borders[1], + lowerright.Y + negative_borders[2] + }; + + core::rect main_rect( + topleft_rect.X, + topleft_rect.Y, + lowerright_rect.X, + lowerright_rect.Y + ); + + std::array, 4> border_rects; + + border_rects[0] = core::rect( + topleft_border.X, + topleft_border.Y, + lowerright_border.X, + topleft_rect.Y + ); + + border_rects[1] = core::rect( + lowerright_rect.X, + topleft_rect.Y, + lowerright_border.X, + lowerright_rect.Y + ); + + border_rects[2] = core::rect( + topleft_border.X, + lowerright_rect.Y, + lowerright_border.X, + lowerright_border.Y + ); + + border_rects[3] = core::rect( + topleft_border.X, + topleft_rect.Y, + topleft_rect.X, + lowerright_rect.Y + ); + + video::IVideoDriver *driver = Environment->getVideoDriver(); + + driver->draw2DRectangle(main_rect, m_colors[0], m_colors[1], m_colors[3], + m_colors[2], nullptr); + + for (size_t i = 0; i <= 3; i++) + driver->draw2DRectangle(m_bordercolors[i], border_rects[i], nullptr); IGUIElement::draw(); } diff --git a/src/gui/guiBox.h b/src/gui/guiBox.h index 5306fdf65..ca8f83771 100644 --- a/src/gui/guiBox.h +++ b/src/gui/guiBox.h @@ -19,16 +19,23 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once +#include +#include #include "irrlichttypes_extrabloated.h" class GUIBox : public gui::IGUIElement { public: GUIBox(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id, - const core::rect &rectangle, const video::SColor &color); + const core::rect &rectangle, + const std::array &colors, + const std::array &bordercolors, + const std::array &borderwidths); virtual void draw() override; private: - video::SColor m_color; + std::array m_colors; + std::array m_bordercolors; + std::array m_borderwidths; }; diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index f702b40bf..9bd5f27fd 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -2211,16 +2211,16 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data, const std::string & void GUIFormSpecMenu::parseBox(parserData* data, const std::string &element) { - std::vector parts = split(element,';'); + std::vector parts = split(element, ';'); if ((parts.size() == 3) || ((parts.size() > 3) && (m_formspec_version > FORMSPEC_API_VERSION))) { - std::vector v_pos = split(parts[0],','); - std::vector v_geom = split(parts[1],','); + std::vector v_pos = split(parts[0], ','); + std::vector v_geom = split(parts[1], ','); - MY_CHECKPOS("box",0); - MY_CHECKGEOM("box",1); + MY_CHECKPOS("box", 0); + MY_CHECKGEOM("box", 1); v2s32 pos; v2s32 geom; @@ -2234,36 +2234,43 @@ void GUIFormSpecMenu::parseBox(parserData* data, const std::string &element) geom.Y = stof(v_geom[1]) * spacing.Y; } - video::SColor tmp_color; - - if (parseColorString(parts[2], tmp_color, false, 0x8C)) { - FieldSpec spec( - "", - L"", - L"", - 258 + m_fields.size(), - -2 - ); - spec.ftype = f_Box; + FieldSpec spec( + "", + L"", + L"", + 258 + m_fields.size(), + -2 + ); + spec.ftype = f_Box; - core::rect rect(pos, pos + geom); + auto style = getDefaultStyleForElement("box", spec.fname); - GUIBox *e = new GUIBox(Environment, data->current_parent, spec.fid, - rect, tmp_color); + video::SColor tmp_color; + std::array colors; + std::array bordercolors = {0x0, 0x0, 0x0, 0x0}; + std::array borderwidths = {0, 0, 0, 0}; - auto style = getDefaultStyleForElement("box", spec.fname); - e->setNotClipped(style.getBool(StyleSpec::NOCLIP, m_formspec_version < 3)); + if (parseColorString(parts[2], tmp_color, true, 0x8C)) { + colors = {tmp_color, tmp_color, tmp_color, tmp_color}; + } else { + colors = style.getColorArray(StyleSpec::COLORS, {0x0, 0x0, 0x0, 0x0}); + bordercolors = style.getColorArray(StyleSpec::BORDERCOLORS, + {0x0, 0x0, 0x0, 0x0}); + borderwidths = style.getIntArray(StyleSpec::BORDERWIDTHS, {0, 0, 0, 0}); + } - e->drop(); + core::rect rect(pos, pos + geom); - m_fields.push_back(spec); + GUIBox *e = new GUIBox(Environment, data->current_parent, spec.fid, rect, + colors, bordercolors, borderwidths); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, m_formspec_version < 3)); + e->drop(); - } else { - errorstream<< "Invalid Box element(" << parts.size() << "): '" << element << "' INVALID COLOR" << std::endl; - } + m_fields.push_back(spec); return; } - errorstream<< "Invalid Box element(" << parts.size() << "): '" << element << "'" << std::endl; + errorstream << "Invalid Box element(" << parts.size() << "): '" << element + << "'" << std::endl; } void GUIFormSpecMenu::parseBackgroundColor(parserData* data, const std::string &element) -- cgit v1.2.3 From 98faeac5a7b382e5d7ce0474bf7d52fc5975a23c Mon Sep 17 00:00:00 2001 From: DS Date: Thu, 20 Aug 2020 22:25:29 +0200 Subject: Load media from subfolders (#9065) --- doc/lua_api.txt | 24 +++++++++++++-------- .../mods/basenodes/textures/default_grass_side.png | Bin 796 -> 0 bytes .../textures/dirt_with_grass/default_grass.png | Bin 0 -> 829 bytes .../dirt_with_grass/default_grass_side.png | Bin 0 -> 796 bytes .../basenodes/textures/dirt_with_grass/info.txt | 3 +++ src/server.cpp | 16 +++++++++----- src/server/mods.cpp | 10 ++++----- src/unittest/test_servermodmanager.cpp | 2 -- 8 files changed, 34 insertions(+), 21 deletions(-) delete mode 100644 games/devtest/mods/basenodes/textures/default_grass_side.png create mode 100644 games/devtest/mods/basenodes/textures/dirt_with_grass/default_grass.png create mode 100644 games/devtest/mods/basenodes/textures/dirt_with_grass/default_grass_side.png create mode 100644 games/devtest/mods/basenodes/textures/dirt_with_grass/info.txt (limited to 'doc') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 49fbe0d94..9770ae4a9 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -152,7 +152,11 @@ Mod directory structure │   ├── models │   ├── textures │   │   ├── modname_stuff.png - │   │   └── modname_something_else.png + │   │   ├── modname_something_else.png + │   │   ├── subfolder_foo + │   │   │ ├── modname_more_stuff.png + │   │   │ └── another_subfolder + │   │   └── bar_subfolder │   ├── sounds │   ├── media │   ├── locale @@ -221,18 +225,20 @@ registered callbacks. `minetest.settings` can be used to read custom or existing settings at load time, if necessary. (See [`Settings`]) -### `models` - -Models for entities or meshnodes. - -### `textures`, `sounds`, `media` +### `textures`, `sounds`, `media`, `models`, `locale` Media files (textures, sounds, whatever) that will be transferred to the -client and will be available for use by the mod. +client and will be available for use by the mod and translation files for +the clients (see [Translations]). -### `locale` +It is suggested to use the folders for the purpous they are thought for, +eg. put textures into `textures`, translation files into `locale`, +models for entities or meshnodes into `models` et cetera. -Translation files for the clients. (See [Translations]) +These folders and subfolders can contain subfolders. +Subfolders with names starting with `_` or `.` are ignored. +If a subfolder contains a media file with the same name as a media file +in one of its parents, the parent's file is used. Naming conventions ------------------ diff --git a/games/devtest/mods/basenodes/textures/default_grass_side.png b/games/devtest/mods/basenodes/textures/default_grass_side.png deleted file mode 100644 index 04770b6f6..000000000 Binary files a/games/devtest/mods/basenodes/textures/default_grass_side.png and /dev/null differ diff --git a/games/devtest/mods/basenodes/textures/dirt_with_grass/default_grass.png b/games/devtest/mods/basenodes/textures/dirt_with_grass/default_grass.png new file mode 100644 index 000000000..29fde6b26 Binary files /dev/null and b/games/devtest/mods/basenodes/textures/dirt_with_grass/default_grass.png differ diff --git a/games/devtest/mods/basenodes/textures/dirt_with_grass/default_grass_side.png b/games/devtest/mods/basenodes/textures/dirt_with_grass/default_grass_side.png new file mode 100644 index 000000000..04770b6f6 Binary files /dev/null and b/games/devtest/mods/basenodes/textures/dirt_with_grass/default_grass_side.png differ diff --git a/games/devtest/mods/basenodes/textures/dirt_with_grass/info.txt b/games/devtest/mods/basenodes/textures/dirt_with_grass/info.txt new file mode 100644 index 000000000..8db21ed9c --- /dev/null +++ b/games/devtest/mods/basenodes/textures/dirt_with_grass/info.txt @@ -0,0 +1,3 @@ +This is for testing loading textures from subfolders. +If it works correctly, the default_grass_side.png file in this folder is used but +default_grass.png is not overwritten by the file in this folder. diff --git a/src/server.cpp b/src/server.cpp index 53ee8c444..ef36aedca 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -2494,19 +2494,25 @@ void Server::fillMediaCache() // Collect all media file paths std::vector paths; - m_modmgr->getModsMediaPaths(paths); - fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures"); + // The paths are ordered in descending priority fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server"); + fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures"); + m_modmgr->getModsMediaPaths(paths); // Collect media file information from paths into cache for (const std::string &mediapath : paths) { std::vector dirlist = fs::GetDirListing(mediapath); for (const fs::DirListNode &dln : dirlist) { - if (dln.dir) // Ignore dirs + if (dln.dir) // Ignore dirs (already in paths) continue; + + const std::string &filename = dln.name; + if (m_media.find(filename) != m_media.end()) // Do not override + continue; + std::string filepath = mediapath; - filepath.append(DIR_DELIM).append(dln.name); - addMediaFile(dln.name, filepath); + filepath.append(DIR_DELIM).append(filename); + addMediaFile(filename, filepath); } } diff --git a/src/server/mods.cpp b/src/server/mods.cpp index 6ac530739..cf1467648 100644 --- a/src/server/mods.cpp +++ b/src/server/mods.cpp @@ -99,10 +99,10 @@ void ServerModManager::getModNames(std::vector &modlist) const void ServerModManager::getModsMediaPaths(std::vector &paths) const { for (const ModSpec &spec : m_sorted_mods) { - paths.push_back(spec.path + DIR_DELIM + "textures"); - paths.push_back(spec.path + DIR_DELIM + "sounds"); - paths.push_back(spec.path + DIR_DELIM + "media"); - paths.push_back(spec.path + DIR_DELIM + "models"); - paths.push_back(spec.path + DIR_DELIM + "locale"); + fs::GetRecursiveDirs(paths, spec.path + DIR_DELIM + "textures"); + fs::GetRecursiveDirs(paths, spec.path + DIR_DELIM + "sounds"); + fs::GetRecursiveDirs(paths, spec.path + DIR_DELIM + "media"); + fs::GetRecursiveDirs(paths, spec.path + DIR_DELIM + "models"); + fs::GetRecursiveDirs(paths, spec.path + DIR_DELIM + "locale"); } } diff --git a/src/unittest/test_servermodmanager.cpp b/src/unittest/test_servermodmanager.cpp index 799936757..e3edb0c32 100644 --- a/src/unittest/test_servermodmanager.cpp +++ b/src/unittest/test_servermodmanager.cpp @@ -169,6 +169,4 @@ void TestServerModManager::testGetModMediaPaths() std::vector result; sm.getModsMediaPaths(result); UASSERTEQ(bool, result.empty(), false); - // We should have 5 folders for each mod (textures, media, locale, model, sounds) - UASSERTEQ(unsigned long, result.size() % 5, 0); } -- cgit v1.2.3 From 1eaff3dfa4091c638be7a1e9777d35b5f665972c Mon Sep 17 00:00:00 2001 From: karamel59 Date: Thu, 27 Aug 2020 21:48:18 +0200 Subject: Improve set_texture_mod documentation (#10339) Properly documents the select_x_by_camera values --- doc/lua_api.txt | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'doc') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 9770ae4a9..acbaf421e 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -6100,6 +6100,9 @@ object you are working with still exists. * `set_yaw(radians)`: sets the yaw (heading). * `get_yaw()`: returns number in radians * `set_texture_mod(mod)` + * Set a texture modifier to the base texture, for sprites and meshes. + * When calling `set_texture_mod` again, the previous one is discarded. + * `mod` the texture modifier. See [Texture modifiers]. * `get_texture_mod()` returns current texture modifier * `set_sprite(p, num_frames, framelength, select_x_by_camera)` * Specifies and starts a sprite animation @@ -6110,6 +6113,12 @@ object you are working with still exists. * `framelength`: Time per animated frame in seconds, default: `0.2` * `select_x_by_camera`: Only for visual = `sprite`. Changes the frame `x` position according to the view direction. default: `false`. + * First column: subject facing the camera + * Second column: subject looking to the left + * Third column: subject backing the camera + * Fourth column: subject looking to the right + * Fifth column: subject viewed from above + * Sixth column: subject viewed from below * `get_entity_name()` (**Deprecated**: Will be removed in a future version) * `get_luaentity()` -- cgit v1.2.3 From 28e87ce9d5fdf163c1eb0daf83279e949f84765d Mon Sep 17 00:00:00 2001 From: DS Date: Sat, 29 Aug 2020 17:41:29 +0200 Subject: Add vector.offset (#10321) --- builtin/common/tests/vector_spec.lua | 4 ++++ builtin/common/vector.lua | 6 ++++++ doc/lua_api.txt | 10 ++++++---- 3 files changed, 16 insertions(+), 4 deletions(-) (limited to 'doc') diff --git a/builtin/common/tests/vector_spec.lua b/builtin/common/tests/vector_spec.lua index 6f308a4a8..0f287363a 100644 --- a/builtin/common/tests/vector_spec.lua +++ b/builtin/common/tests/vector_spec.lua @@ -44,6 +44,10 @@ describe("vector", function() assert.same({ x = 2, y = 4, z = 6 }, vector.add(vector.new(1, 2, 3), { x = 1, y = 2, z = 3 })) end) + it("offset()", function() + assert.same({ x = 41, y = 52, z = 63 }, vector.offset(vector.new(1, 2, 3), 40, 50, 60)) + end) + -- This function is needed because of floating point imprecision. local function almost_equal(a, b) if type(a) == "number" then diff --git a/builtin/common/vector.lua b/builtin/common/vector.lua index 1fd784ce2..d6437deda 100644 --- a/builtin/common/vector.lua +++ b/builtin/common/vector.lua @@ -137,6 +137,12 @@ function vector.divide(a, b) end end +function vector.offset(v, x, y, z) + return {x = v.x + x, + y = v.y + y, + z = v.z + z} +end + function vector.sort(a, b) return {x = math.min(a.x, b.x), y = math.min(a.y, b.y), z = math.min(a.z, b.z)}, {x = math.max(a.x, b.x), y = math.max(a.y, b.y), z = math.max(a.z, b.z)} diff --git a/doc/lua_api.txt b/doc/lua_api.txt index acbaf421e..5ed84fe9a 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -3062,10 +3062,12 @@ For the following functions, `v`, `v1`, `v2` are vectors, * Returns in order minp, maxp vectors of the cuboid defined by `v1`, `v2`. * `vector.angle(v1, v2)`: * Returns the angle between `v1` and `v2` in radians. -* `vector.dot(v1, v2)` - * Returns the dot product of `v1` and `v2` -* `vector.cross(v1, v2)` - * Returns the cross product of `v1` and `v2` +* `vector.dot(v1, v2)`: + * Returns the dot product of `v1` and `v2`. +* `vector.cross(v1, v2)`: + * Returns the cross product of `v1` and `v2`. +* `vector.offset(v, x, y, z)`: + * Returns the sum of the vectors `v` and `{x = x, y = y, z = z}`. For the following functions `x` can be either a vector or a number: -- cgit v1.2.3 From 386d5f778a299cc0d891eb476674f3eddb108376 Mon Sep 17 00:00:00 2001 From: HybridDog <3192173+HybridDog@users.noreply.github.com> Date: Sat, 29 Aug 2020 17:43:09 +0200 Subject: Document normalmap textures (#10096) --- doc/lua_api.txt | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'doc') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 5ed84fe9a..08bdba03e 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -152,6 +152,7 @@ Mod directory structure │   ├── models │   ├── textures │   │   ├── modname_stuff.png + │   │   ├── modname_stuff_normal.png │   │   ├── modname_something_else.png │   │   ├── subfolder_foo │   │   │ ├── modname_more_stuff.png @@ -384,6 +385,36 @@ stripping out the file extension: * e.g. `foomod_foothing.png` * e.g. `foomod_foothing` + +Normalmap Textures +------------------ + +If shaders and bumpmapping or parallax occlusion is enabled, Minetest tries +to load normalmaps. +Those image files have to end with `_normal.png` and start with the same name +as their corresponding texture. +For example a normalmap for `foomod_foothing.png` has to be called +`foomod_foothing_normal.png`. + +The sRGB R, G and B colour values of a normalmap pixel are each directly +mapped from `{0, ..., 255}` to `[-1, 1]` and, taken together, +define the normal vector. +The alpha channel defines the heightmap for parallax occlusion. +To be safe, the alpha values should always be bigger than zero +because the colour values, which define the normal vector, +may be undefined for image formats where colour is discarded in fully +transparent pixels. + +Bumpmapping and parallax occlusion are currently experimental features: + +* Bumpmapping in Minetest happens in an obscure way; there are no light sources + defined in the shaders except the sunlight direction. +* Parallax occlusion with relief-mapping mode does not yet work correctly + together with Minetest's Fastfaces. +* The normalmap files must end with `.png`, so other image files are not + supported. + + Texture modifiers ----------------- -- cgit v1.2.3 From 5c4b560b6812bbdf7a9d3202c95bad5c9df97d66 Mon Sep 17 00:00:00 2001 From: EvidenceB <49488517+EvidenceBKidscode@users.noreply.github.com> Date: Sat, 29 Aug 2020 20:13:30 +0200 Subject: Add compass HUD element (#9312) Co-authored-by: Jean-Patrick Guerrero Co-authored-by: Pierre-Yves Rollo Co-authored-by: SmallJoker --- doc/lua_api.txt | 18 ++++++- src/client/hud.cpp | 140 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/client/hud.h | 8 +++ src/hud.cpp | 1 + src/hud.h | 10 +++- 5 files changed, 175 insertions(+), 2 deletions(-) (limited to 'doc') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 08bdba03e..c81824163 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1451,7 +1451,23 @@ Same as `image`, but does not accept a `position`; the position is instead deter * `world_pos`: World position of the waypoint. * `offset`: offset in pixels from position. +### `compass` +Displays an image oriented or translated according to current heading direction. + +* `size`: The size of this element. Negative values represent percentage + of the screen; e.g. `x=-100` means 100% (width). +* `scale`: Scale of the translated image (used only for dir = 2 or dir = 3). +* `text`: The name of the texture to use. +* `alignment`: The alignment of the image. +* `offset`: Offset in pixels from position. +* `dir`: How the image is rotated/translated: + * 0 - Rotate as heading direction + * 1 - Rotate in reverse direction + * 2 - Translate as landscape direction + * 3 - Translate in reverse direction + +If translation is chosen, texture is repeated horizontally to fill the whole element. Representations of simple things ================================ @@ -7968,7 +7984,7 @@ Used by `Player:hud_add`. Returned by `Player:hud_get`. { hud_elem_type = "image", -- See HUD element types - -- Type of element, can be "image", "text", "statbar", or "inventory" + -- Type of element, can be "image", "text", "statbar", "inventory" or "compass" position = {x=0.5, y=0.5}, -- Left corner position of element diff --git a/src/client/hud.cpp b/src/client/hud.cpp index 2b347c1e0..d3038230c 100644 --- a/src/client/hud.cpp +++ b/src/client/hud.cpp @@ -114,6 +114,28 @@ Hud::Hud(gui::IGUIEnvironment *guienv, Client *client, LocalPlayer *player, } else { m_selection_material.MaterialType = video::EMT_SOLID; } + + // Prepare mesh for compass drawing + m_rotation_mesh_buffer.Vertices.set_used(4); + m_rotation_mesh_buffer.Indices.set_used(6); + + video::SColor white(255, 255, 255, 255); + v3f normal(0.f, 0.f, 1.f); + + m_rotation_mesh_buffer.Vertices[0] = video::S3DVertex(v3f(-1.f, -1.f, 0.f), normal, white, v2f(0.f, 1.f)); + m_rotation_mesh_buffer.Vertices[1] = video::S3DVertex(v3f(-1.f, 1.f, 0.f), normal, white, v2f(0.f, 0.f)); + m_rotation_mesh_buffer.Vertices[2] = video::S3DVertex(v3f( 1.f, 1.f, 0.f), normal, white, v2f(1.f, 0.f)); + m_rotation_mesh_buffer.Vertices[3] = video::S3DVertex(v3f( 1.f, -1.f, 0.f), normal, white, v2f(1.f, 1.f)); + + m_rotation_mesh_buffer.Indices[0] = 0; + m_rotation_mesh_buffer.Indices[1] = 1; + m_rotation_mesh_buffer.Indices[2] = 2; + m_rotation_mesh_buffer.Indices[3] = 2; + m_rotation_mesh_buffer.Indices[4] = 3; + m_rotation_mesh_buffer.Indices[5] = 0; + + m_rotation_mesh_buffer.getMaterial().Lighting = false; + m_rotation_mesh_buffer.getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; } Hud::~Hud() @@ -423,6 +445,54 @@ void Hud::drawLuaElements(const v3s16 &camera_offset) core::rect(core::position2d(0,0), imgsize), NULL, colors, true); break; } + case HUD_ELEM_COMPASS: { + video::ITexture *texture = tsrc->getTexture(e->text); + if (!texture) + continue; + + // Positionning : + v2s32 dstsize(e->size.X, e->size.Y); + if (e->size.X < 0) + dstsize.X = m_screensize.X * (e->size.X * -0.01); + if (e->size.Y < 0) + dstsize.Y = m_screensize.Y * (e->size.Y * -0.01); + + if (dstsize.X <= 0 || dstsize.Y <= 0) + return; // Avoid zero divides + + // Angle according to camera view + v3f fore(0.f, 0.f, 1.f); + scene::ICameraSceneNode *cam = RenderingEngine::get_scene_manager()->getActiveCamera(); + cam->getAbsoluteTransformation().rotateVect(fore); + int angle = - fore.getHorizontalAngle().Y; + + // Limit angle and ajust with given offset + angle = (angle + (int)e->number) % 360; + + core::rect dstrect(0, 0, dstsize.X, dstsize.Y); + dstrect += pos + v2s32( + (e->align.X - 1.0) * dstsize.X / 2, + (e->align.Y - 1.0) * dstsize.Y / 2) + + v2s32(e->offset.X * m_hud_scaling, e->offset.Y * m_hud_scaling); + + switch (e->dir) { + case HUD_COMPASS_ROTATE: + drawCompassRotate(e, texture, dstrect, angle); + break; + case HUD_COMPASS_ROTATE_REVERSE: + drawCompassRotate(e, texture, dstrect, -angle); + break; + case HUD_COMPASS_TRANSLATE: + drawCompassTranslate(e, texture, dstrect, angle); + break; + case HUD_COMPASS_TRANSLATE_REVERSE: + drawCompassTranslate(e, texture, dstrect, -angle); + break; + default: + break; + } + + break; } default: infostream << "Hud::drawLuaElements: ignoring drawform " << e->type << " of hud element ID " << i << " due to unrecognized type" << std::endl; @@ -430,6 +500,76 @@ void Hud::drawLuaElements(const v3s16 &camera_offset) } } +void Hud::drawCompassTranslate(HudElement *e, video::ITexture *texture, + const core::rect &rect, int angle) +{ + const video::SColor color(255, 255, 255, 255); + const video::SColor colors[] = {color, color, color, color}; + + // Compute source image scaling + core::dimension2di imgsize(texture->getOriginalSize()); + core::rect srcrect(0, 0, imgsize.Width, imgsize.Height); + + v2s32 dstsize(rect.getHeight() * e->scale.X * imgsize.Width / imgsize.Height, + rect.getHeight() * e->scale.Y); + + // Avoid infinite loop + if (dstsize.X <= 0 || dstsize.Y <= 0) + return; + + core::rect tgtrect(0, 0, dstsize.X, dstsize.Y); + tgtrect += v2s32( + (rect.getWidth() - dstsize.X) / 2, + (rect.getHeight() - dstsize.Y) / 2) + + rect.UpperLeftCorner; + + int offset = angle * dstsize.X / 360; + + tgtrect += v2s32(offset, 0); + + // Repeat image as much as needed + while (tgtrect.UpperLeftCorner.X > rect.UpperLeftCorner.X) + tgtrect -= v2s32(dstsize.X, 0); + + draw2DImageFilterScaled(driver, texture, tgtrect, srcrect, &rect, colors, true); + tgtrect += v2s32(dstsize.X, 0); + + while (tgtrect.UpperLeftCorner.X < rect.LowerRightCorner.X) { + draw2DImageFilterScaled(driver, texture, tgtrect, srcrect, &rect, colors, true); + tgtrect += v2s32(dstsize.X, 0); + } +} + +void Hud::drawCompassRotate(HudElement *e, video::ITexture *texture, + const core::rect &rect, int angle) +{ + core::dimension2di imgsize(texture->getOriginalSize()); + + core::rect oldViewPort = driver->getViewPort(); + core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION); + core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW); + + core::matrix4 Matrix; + Matrix.makeIdentity(); + Matrix.setRotationDegrees(v3f(0.f, 0.f, angle)); + + driver->setViewPort(rect); + driver->setTransform(video::ETS_PROJECTION, core::matrix4()); + driver->setTransform(video::ETS_VIEW, core::matrix4()); + driver->setTransform(video::ETS_WORLD, Matrix); + + video::SMaterial &material = m_rotation_mesh_buffer.getMaterial(); + material.TextureLayer[0].Texture = texture; + driver->setMaterial(material); + driver->drawMeshBuffer(&m_rotation_mesh_buffer); + + driver->setTransform(video::ETS_WORLD, core::matrix4()); + driver->setTransform(video::ETS_VIEW, oldViewMat); + driver->setTransform(video::ETS_PROJECTION, oldProjMat); + + // restore the view area + driver->setViewPort(oldViewPort); +} void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, const std::string &texture, const std::string &bgtexture, diff --git a/src/client/hud.h b/src/client/hud.h index ba34d479d..cf83cb16e 100644 --- a/src/client/hud.h +++ b/src/client/hud.h @@ -95,6 +95,12 @@ private: void drawItem(const ItemStack &item, const core::rect &rect, bool selected); + void drawCompassTranslate(HudElement *e, video::ITexture *texture, + const core::rect &rect, int way); + + void drawCompassRotate(HudElement *e, video::ITexture *texture, + const core::rect &rect, int way); + float m_hud_scaling; // cached minetest setting float m_scale_factor; v3s16 m_camera_offset; @@ -115,6 +121,8 @@ private: video::SMaterial m_selection_material; + scene::SMeshBuffer m_rotation_mesh_buffer; + enum { HIGHLIGHT_BOX, diff --git a/src/hud.cpp b/src/hud.cpp index 4830c56a4..175701342 100644 --- a/src/hud.cpp +++ b/src/hud.cpp @@ -28,6 +28,7 @@ const struct EnumString es_HudElementType[] = {HUD_ELEM_INVENTORY, "inventory"}, {HUD_ELEM_WAYPOINT, "waypoint"}, {HUD_ELEM_IMAGE_WAYPOINT, "image_waypoint"}, + {HUD_ELEM_COMPASS, "compass"}, {0, NULL}, }; diff --git a/src/hud.h b/src/hud.h index e015baec1..0a6993c1b 100644 --- a/src/hud.h +++ b/src/hud.h @@ -60,7 +60,8 @@ enum HudElementType { HUD_ELEM_STATBAR = 2, HUD_ELEM_INVENTORY = 3, HUD_ELEM_WAYPOINT = 4, - HUD_ELEM_IMAGE_WAYPOINT = 5 + HUD_ELEM_IMAGE_WAYPOINT = 5, + HUD_ELEM_COMPASS = 6 }; enum HudElementStat { @@ -79,6 +80,13 @@ enum HudElementStat { HUD_STAT_TEXT2, }; +enum HudCompassDir { + HUD_COMPASS_ROTATE = 0, + HUD_COMPASS_ROTATE_REVERSE, + HUD_COMPASS_TRANSLATE, + HUD_COMPASS_TRANSLATE_REVERSE, +}; + struct HudElement { HudElementType type; v2f pos; -- cgit v1.2.3 From c18dbadcb898db6d1f68b72afec56c599af317ec Mon Sep 17 00:00:00 2001 From: LoneWolfHT Date: Sat, 29 Aug 2020 16:02:21 -0700 Subject: Fix dropped craftitems/tools not using light_source values (#9438) --- builtin/game/item_entity.lua | 2 +- doc/lua_api.txt | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) (limited to 'doc') diff --git a/builtin/game/item_entity.lua b/builtin/game/item_entity.lua index 20dd18044..f380d071d 100644 --- a/builtin/game/item_entity.lua +++ b/builtin/game/item_entity.lua @@ -54,7 +54,7 @@ core.register_entity(":__builtin:item", { local max_count = stack:get_stack_max() local count = math.min(stack:get_count(), max_count) local size = 0.2 + 0.1 * (count / max_count) ^ (1 / 3) - local def = core.registered_nodes[itemname] + local def = core.registered_items[itemname] local glow = def and math.floor(def.light_source / 2 + 0.5) self.object:set_properties({ diff --git a/doc/lua_api.txt b/doc/lua_api.txt index c81824163..6341571c7 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -7026,6 +7026,13 @@ Used by `minetest.register_node`, `minetest.register_craftitem`, and liquids_pointable = false, + light_source = 0, + -- When used for nodes: Defines amount of light emitted by node. + -- Otherwise: Defines texture glow when viewed as a dropped item + -- To set the maximum (14), use the value 'minetest.LIGHT_MAX'. + -- A value outside the range 0 to minetest.LIGHT_MAX causes undefined + -- behavior. + -- See "Tools" section for an example including explanation tool_capabilities = { full_punch_interval = 1.0, @@ -7225,12 +7232,6 @@ Used by `minetest.register_node`. drowning = 0, -- Player will take this amount of damage if no bubbles are left - light_source = 0, - -- Amount of light emitted by node. - -- To set the maximum (14), use the value 'minetest.LIGHT_MAX'. - -- A value outside the range 0 to minetest.LIGHT_MAX causes undefined - -- behavior. - damage_per_second = 0, -- If player is inside node, this damage is caused -- cgit v1.2.3 From 9ed84cfa85fe9a03803f2f2aba041363db65672d Mon Sep 17 00:00:00 2001 From: DS Date: Tue, 1 Sep 2020 20:18:10 +0200 Subject: Mark multiply and divide with two vectors as deprecated (Schur product and quotient) (#10329) --- doc/lua_api.txt | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'doc') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 6341571c7..cc4af970c 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -3080,7 +3080,8 @@ Internally, it is implemented as a table with the 3 fields `x`, `y` and `z`. Example: `{x = 0, y = 1, z = 0}`. For the following functions, `v`, `v1`, `v2` are vectors, -`p1`, `p2` are positions: +`p1`, `p2` are positions, +`s` is a scalar (a number): * `vector.new(a[, b, c])`: * Returns a vector. @@ -3126,10 +3127,12 @@ For the following functions `x` can be either a vector or a number: * Returns a vector. * If `x` is a vector: Returns the difference of `v` subtracted by `x`. * If `x` is a number: Subtracts `x` from each component of `v`. -* `vector.multiply(v, x)`: - * Returns a scaled vector or Schur product. -* `vector.divide(v, x)`: - * Returns a scaled vector or Schur quotient. +* `vector.multiply(v, s)`: + * Returns a scaled vector. + * Deprecated: If `s` is a vector: Returns the Schur product. +* `vector.divide(v, s)`: + * Returns a scaled vector. + * Deprecated: If `s` is a vector: Returns the Schur quotient. For the following functions `a` is an angle in radians and `r` is a rotation vector ({x = , y = , z = }) where pitch, yaw and roll are -- cgit v1.2.3 From 050964bed6005f8d816ddf362b9fc8675581d190 Mon Sep 17 00:00:00 2001 From: Lars Müller <34514239+appgurueu@users.noreply.github.com> Date: Fri, 4 Sep 2020 20:49:07 +0200 Subject: Fix inventory swapping not calling all callbacks (#9923) "Predicts" whether something will be swapped for allow callbacks, then calls callbacks a second time with swapped properties. Co-authored-by: SmallJoker --- doc/lua_api.txt | 25 ++++ src/inventory.cpp | 9 +- src/inventorymanager.cpp | 303 ++++++++++++++++++++++++++--------------------- src/inventorymanager.h | 12 ++ 4 files changed, 210 insertions(+), 139 deletions(-) (limited to 'doc') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index cc4af970c..86627c19e 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -5888,6 +5888,31 @@ An `InvRef` is a reference to an inventory. `minetest.get_inventory(location)`. * returns `{type="undefined"}` in case location is not known +### Callbacks + +Detached & nodemeta inventories provide the following callbacks for move actions: + +#### Before + +The `allow_*` callbacks return how many items can be moved. + +* `allow_move`/`allow_metadata_inventory_move`: Moving items in the inventory +* `allow_take`/`allow_metadata_inventory_take`: Taking items from the inventory +* `allow_put`/`allow_metadata_inventory_put`: Putting items to the inventory + +#### After + +The `on_*` callbacks are called after the items have been placed in the inventories. + +* `on_move`/`on_metadata_inventory_move`: Moving items in the inventory +* `on_take`/`on_metadata_inventory_take`: Taking items from the inventory +* `on_put`/`on_metadata_inventory_put`: Putting items to the inventory + +#### Swapping + +When a player tries to put an item to a place where another item is, the items are *swapped*. +This means that all callbacks will be called twice (once for each action). + `ItemStack` ----------- diff --git a/src/inventory.cpp b/src/inventory.cpp index 349ee503d..cf72cb005 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -732,17 +732,17 @@ void InventoryList::moveItemSomewhere(u32 i, InventoryList *dest, u32 count) u32 InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i, u32 count, bool swap_if_needed, bool *did_swap) { - if(this == dest && i == dest_i) + if (this == dest && i == dest_i) return count; // Take item from source list ItemStack item1; - if(count == 0) + if (count == 0) item1 = changeItem(i, ItemStack()); else item1 = takeItem(i, count); - if(item1.empty()) + if (item1.empty()) return 0; // Try to add the item to destination list @@ -750,8 +750,7 @@ u32 InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i, item1 = dest->addItem(dest_i, item1); // If something is returned, the item was not fully added - if(!item1.empty()) - { + if (!item1.empty()) { // If olditem is returned, nothing was added. bool nothing_added = (item1.count == oldcount); diff --git a/src/inventorymanager.cpp b/src/inventorymanager.cpp index b6f464901..635bd2e4b 100644 --- a/src/inventorymanager.cpp +++ b/src/inventorymanager.cpp @@ -154,6 +154,93 @@ IMoveAction::IMoveAction(std::istream &is, bool somewhere) : } } +void IMoveAction::swapDirections() +{ + std::swap(from_inv, to_inv); + std::swap(from_list, to_list); + std::swap(from_i, to_i); +} + +void IMoveAction::onPutAndOnTake(const ItemStack &src_item, ServerActiveObject *player) const +{ + ServerScripting *sa = PLAYER_TO_SA(player); + if (to_inv.type == InventoryLocation::DETACHED) + sa->detached_inventory_OnPut(*this, src_item, player); + else if (to_inv.type == InventoryLocation::NODEMETA) + sa->nodemeta_inventory_OnPut(*this, src_item, player); + else if (to_inv.type == InventoryLocation::PLAYER) + sa->player_inventory_OnPut(*this, src_item, player); + else + assert(false); + + if (from_inv.type == InventoryLocation::DETACHED) + sa->detached_inventory_OnTake(*this, src_item, player); + else if (from_inv.type == InventoryLocation::NODEMETA) + sa->nodemeta_inventory_OnTake(*this, src_item, player); + else if (from_inv.type == InventoryLocation::PLAYER) + sa->player_inventory_OnTake(*this, src_item, player); + else + assert(false); +} + +void IMoveAction::onMove(int count, ServerActiveObject *player) const +{ + ServerScripting *sa = PLAYER_TO_SA(player); + if (from_inv.type == InventoryLocation::DETACHED) + sa->detached_inventory_OnMove(*this, count, player); + else if (from_inv.type == InventoryLocation::NODEMETA) + sa->nodemeta_inventory_OnMove(*this, count, player); + else if (from_inv.type == InventoryLocation::PLAYER) + sa->player_inventory_OnMove(*this, count, player); + else + assert(false); +} + +int IMoveAction::allowPut(const ItemStack &dst_item, ServerActiveObject *player) const +{ + ServerScripting *sa = PLAYER_TO_SA(player); + int dst_can_put_count = 0xffff; + if (to_inv.type == InventoryLocation::DETACHED) + dst_can_put_count = sa->detached_inventory_AllowPut(*this, dst_item, player); + else if (to_inv.type == InventoryLocation::NODEMETA) + dst_can_put_count = sa->nodemeta_inventory_AllowPut(*this, dst_item, player); + else if (to_inv.type == InventoryLocation::PLAYER) + dst_can_put_count = sa->player_inventory_AllowPut(*this, dst_item, player); + else + assert(false); + return dst_can_put_count; +} + +int IMoveAction::allowTake(const ItemStack &src_item, ServerActiveObject *player) const +{ + ServerScripting *sa = PLAYER_TO_SA(player); + int src_can_take_count = 0xffff; + if (from_inv.type == InventoryLocation::DETACHED) + src_can_take_count = sa->detached_inventory_AllowTake(*this, src_item, player); + else if (from_inv.type == InventoryLocation::NODEMETA) + src_can_take_count = sa->nodemeta_inventory_AllowTake(*this, src_item, player); + else if (from_inv.type == InventoryLocation::PLAYER) + src_can_take_count = sa->player_inventory_AllowTake(*this, src_item, player); + else + assert(false); + return src_can_take_count; +} + +int IMoveAction::allowMove(int try_take_count, ServerActiveObject *player) const +{ + ServerScripting *sa = PLAYER_TO_SA(player); + int src_can_take_count = 0xffff; + if (from_inv.type == InventoryLocation::DETACHED) + src_can_take_count = sa->detached_inventory_AllowMove(*this, try_take_count, player); + else if (from_inv.type == InventoryLocation::NODEMETA) + src_can_take_count = sa->nodemeta_inventory_AllowMove(*this, try_take_count, player); + else if (from_inv.type == InventoryLocation::PLAYER) + src_can_take_count = sa->player_inventory_AllowMove(*this, try_take_count, player); + else + assert(false); + return src_can_take_count; +} + void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGameDef *gamedef) { Inventory *inv_from = mgr->getInventory(from_inv); @@ -251,93 +338,80 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame Collect information of endpoints */ - int try_take_count = count; - if (try_take_count == 0) - try_take_count = list_from->getItem(from_i).count; + ItemStack src_item = list_from->getItem(from_i); + if (count > 0) + src_item.count = count; + if (src_item.empty()) + return; int src_can_take_count = 0xffff; int dst_can_put_count = 0xffff; - /* Query detached inventories */ + // this is needed for swapping items inside one inventory to work + ItemStack restitem; + bool allow_swap = !list_to->itemFits(to_i, src_item, &restitem) + && restitem.count == src_item.count + && !caused_by_move_somewhere; - // Move occurs in the same detached inventory - if (from_inv.type == InventoryLocation::DETACHED && - from_inv == to_inv) { - src_can_take_count = PLAYER_TO_SA(player)->detached_inventory_AllowMove( - *this, try_take_count, player); - dst_can_put_count = src_can_take_count; - } else { - // Destination is detached - if (to_inv.type == InventoryLocation::DETACHED) { - ItemStack src_item = list_from->getItem(from_i); - src_item.count = try_take_count; - dst_can_put_count = PLAYER_TO_SA(player)->detached_inventory_AllowPut( - *this, src_item, player); - } - // Source is detached - if (from_inv.type == InventoryLocation::DETACHED) { - ItemStack src_item = list_from->getItem(from_i); - src_item.count = try_take_count; - src_can_take_count = PLAYER_TO_SA(player)->detached_inventory_AllowTake( - *this, src_item, player); - } - } - - /* Query node metadata inventories */ + // Shift-click: Cannot fill this stack, proceed with next slot + if (caused_by_move_somewhere && restitem.count == src_item.count) + return; - // Both endpoints are nodemeta - // Move occurs in the same nodemeta inventory - if (from_inv.type == InventoryLocation::NODEMETA && - from_inv == to_inv) { - src_can_take_count = PLAYER_TO_SA(player)->nodemeta_inventory_AllowMove( - *this, try_take_count, player); - dst_can_put_count = src_can_take_count; - } else { - // Destination is nodemeta - if (to_inv.type == InventoryLocation::NODEMETA) { - ItemStack src_item = list_from->getItem(from_i); - src_item.count = try_take_count; - dst_can_put_count = PLAYER_TO_SA(player)->nodemeta_inventory_AllowPut( - *this, src_item, player); + if (allow_swap) { + // Swap will affect the entire stack if it can performed. + src_item = list_from->getItem(from_i); + count = src_item.count; + } + + if (from_inv == to_inv) { + // Move action within the same inventory + src_can_take_count = allowMove(src_item.count, player); + + bool swap_expected = allow_swap; + allow_swap = allow_swap + && (src_can_take_count == -1 || src_can_take_count >= src_item.count); + if (allow_swap) { + int try_put_count = list_to->getItem(to_i).count; + swapDirections(); + dst_can_put_count = allowMove(try_put_count, player); + allow_swap = allow_swap + && (dst_can_put_count == -1 || dst_can_put_count >= try_put_count); + swapDirections(); + } else { + dst_can_put_count = src_can_take_count; } - // Source is nodemeta - if (from_inv.type == InventoryLocation::NODEMETA) { - ItemStack src_item = list_from->getItem(from_i); - src_item.count = try_take_count; - src_can_take_count = PLAYER_TO_SA(player)->nodemeta_inventory_AllowTake( - *this, src_item, player); - } - } - - // Query player inventories - - // Move occurs in the same player inventory - if (from_inv.type == InventoryLocation::PLAYER && - from_inv == to_inv) { - src_can_take_count = PLAYER_TO_SA(player)->player_inventory_AllowMove( - *this, try_take_count, player); - dst_can_put_count = src_can_take_count; + if (swap_expected != allow_swap) + src_can_take_count = dst_can_put_count = 0; } else { - // Destination is a player - if (to_inv.type == InventoryLocation::PLAYER) { - ItemStack src_item = list_from->getItem(from_i); - src_item.count = try_take_count; - dst_can_put_count = PLAYER_TO_SA(player)->player_inventory_AllowPut( - *this, src_item, player); - } - // Source is a player - if (from_inv.type == InventoryLocation::PLAYER) { - ItemStack src_item = list_from->getItem(from_i); - src_item.count = try_take_count; - src_can_take_count = PLAYER_TO_SA(player)->player_inventory_AllowTake( - *this, src_item, player); + // Take from one inventory, put into another + dst_can_put_count = allowPut(src_item, player); + src_can_take_count = allowTake(src_item, player); + + bool swap_expected = allow_swap; + allow_swap = allow_swap + && (src_can_take_count == -1 || src_can_take_count >= src_item.count) + && (dst_can_put_count == -1 || dst_can_put_count >= src_item.count); + // A swap is expected, which means that we have to + // run the "allow" callbacks a second time with swapped inventories + if (allow_swap) { + ItemStack dst_item = list_to->getItem(to_i); + swapDirections(); + + int src_can_take = allowPut(dst_item, player); + int dst_can_put = allowTake(dst_item, player); + allow_swap = allow_swap + && (src_can_take == -1 || src_can_take >= dst_item.count) + && (dst_can_put == -1 || dst_can_put >= dst_item.count); + swapDirections(); } + if (swap_expected != allow_swap) + src_can_take_count = dst_can_put_count = 0; } int old_count = count; /* Modify count according to collected data */ - count = try_take_count; + count = src_item.count; if (src_can_take_count != -1 && count > src_can_take_count) count = src_can_take_count; if (dst_can_put_count != -1 && count > dst_can_put_count) @@ -367,7 +441,7 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame return; } - ItemStack src_item = list_from->getItem(from_i); + src_item = list_from->getItem(from_i); src_item.count = count; ItemStack from_stack_was = list_from->getItem(from_i); ItemStack to_stack_was = list_to->getItem(to_i); @@ -380,7 +454,8 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame */ bool did_swap = false; move_count = list_from->moveItem(from_i, - list_to, to_i, count, !caused_by_move_somewhere, &did_swap); + list_to, to_i, count, allow_swap, &did_swap); + assert(allow_swap == did_swap); // If source is infinite, reset it's stack if (src_can_take_count == -1) { @@ -471,69 +546,29 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame Report move to endpoints */ - /* Detached inventories */ - - // Both endpoints are same detached - if (from_inv.type == InventoryLocation::DETACHED && - from_inv == to_inv) { - PLAYER_TO_SA(player)->detached_inventory_OnMove( - *this, count, player); - } else { - // Destination is detached - if (to_inv.type == InventoryLocation::DETACHED) { - PLAYER_TO_SA(player)->detached_inventory_OnPut( - *this, src_item, player); - } - // Source is detached - if (from_inv.type == InventoryLocation::DETACHED) { - PLAYER_TO_SA(player)->detached_inventory_OnTake( - *this, src_item, player); + // Source = destination => move + if (from_inv == to_inv) { + onMove(count, player); + if (did_swap) { + // Item is now placed in source list + src_item = list_from->getItem(from_i); + swapDirections(); + onMove(src_item.count, player); + swapDirections(); } - } - - /* Node metadata inventories */ - - // Both endpoints are same nodemeta - if (from_inv.type == InventoryLocation::NODEMETA && - from_inv == to_inv) { - PLAYER_TO_SA(player)->nodemeta_inventory_OnMove( - *this, count, player); - } else { - // Destination is nodemeta - if (to_inv.type == InventoryLocation::NODEMETA) { - PLAYER_TO_SA(player)->nodemeta_inventory_OnPut( - *this, src_item, player); - } - // Source is nodemeta - if (from_inv.type == InventoryLocation::NODEMETA) { - PLAYER_TO_SA(player)->nodemeta_inventory_OnTake( - *this, src_item, player); - } - } - - // Player inventories - - // Both endpoints are same player inventory - if (from_inv.type == InventoryLocation::PLAYER && - from_inv == to_inv) { - PLAYER_TO_SA(player)->player_inventory_OnMove( - *this, count, player); + mgr->setInventoryModified(from_inv); } else { - // Destination is player inventory - if (to_inv.type == InventoryLocation::PLAYER) { - PLAYER_TO_SA(player)->player_inventory_OnPut( - *this, src_item, player); + onPutAndOnTake(src_item, player); + if (did_swap) { + // Item is now placed in source list + src_item = list_from->getItem(from_i); + swapDirections(); + onPutAndOnTake(src_item, player); + swapDirections(); } - // Source is player inventory - if (from_inv.type == InventoryLocation::PLAYER) { - PLAYER_TO_SA(player)->player_inventory_OnTake( - *this, src_item, player); - } - } - - mgr->setInventoryModified(from_inv); - if (inv_from != inv_to) mgr->setInventoryModified(to_inv); + mgr->setInventoryModified(from_inv); + } } void IMoveAction::clientApply(InventoryManager *mgr, IGameDef *gamedef) diff --git a/src/inventorymanager.h b/src/inventorymanager.h index 69bf30169..4ad5d3f49 100644 --- a/src/inventorymanager.h +++ b/src/inventorymanager.h @@ -183,6 +183,18 @@ struct IMoveAction : public InventoryAction, public MoveAction void apply(InventoryManager *mgr, ServerActiveObject *player, IGameDef *gamedef); void clientApply(InventoryManager *mgr, IGameDef *gamedef); + + void swapDirections(); + + void onPutAndOnTake(const ItemStack &src_item, ServerActiveObject *player) const; + + void onMove(int count, ServerActiveObject *player) const; + + int allowPut(const ItemStack &dst_item, ServerActiveObject *player) const; + + int allowTake(const ItemStack &src_item, ServerActiveObject *player) const; + + int allowMove(int try_take_count, ServerActiveObject *player) const; }; struct IDropAction : public InventoryAction, public MoveAction -- cgit v1.2.3 From c8303f790ccb03857d031ecb73de7f9f59a6ceba Mon Sep 17 00:00:00 2001 From: Zughy <63455151+Zughy@users.noreply.github.com> Date: Wed, 16 Sep 2020 15:47:20 +0200 Subject: lua_api.txt: author, release and title fields when downloading from ContentDB (#10129) Co-authored-by: Marco <4279489-marco_a@users.noreply.gitlab.com> Co-authored-by: rubenwardy --- doc/lua_api.txt | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) (limited to 'doc') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 86627c19e..a1baadf6d 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -62,12 +62,12 @@ Where `` is unique to each game. The game directory can contain the following files: * `game.conf`, with the following keys: - * `name`: Required, human readable name e.g. `name = Minetest` + * `name`: Required, a human readable title to address the game, e.g. `name = Minetest`. * `description`: Short description to be shown in the content tab * `allowed_mapgens = ` e.g. `allowed_mapgens = v5,v6,flat` - Mapgens not in this list are removed from the list of mapgens for - the game. + Mapgens not in this list are removed from the list of mapgens for the + game. If not specified, all mapgens are allowed. * `disallowed_mapgens = ` e.g. `disallowed_mapgens = v5,v6,flat` @@ -79,6 +79,10 @@ The game directory can contain the following files: e.g. `disallowed_mapgen_settings = mgv5_spflags` These settings are hidden for this game in the world creation dialog and game start menu. + * `author`: The author of the game. It only appears when downloaded from + ContentDB. + * `release`: Ignore this: Should only ever be set by ContentDB, as it is + an internal ID used to track versions. * `minetest.conf`: Used to set default settings when running this game. * `settingtypes.txt`: @@ -134,9 +138,15 @@ Mods can be put in a subdirectory, if the parent directory, which otherwise should be a mod, contains a file named `modpack.conf`. The file is a key-value store of modpack details. -* `name`: The modpack name. +* `name`: The modpack name. Allows Minetest to determine the modpack name even + if the folder is wrongly named. * `description`: Description of mod to be shown in the Mods tab of the main menu. +* `author`: The author of the modpack. It only appears when downloaded from + ContentDB. +* `release`: Ignore this: Should only ever be set by ContentDB, as it is an + internal ID used to track versions. +* `title`: A human-readable title to address the modpack. Note: to support 0.4.x, please also create an empty modpack.txt file. @@ -181,6 +191,11 @@ A `Settings` file that provides meta information about the mod. loaded before this mod. * `optional_depends`: A comma separated list of optional dependencies. Like a dependency, but no error if the mod doesn't exist. +* `author`: The author of the mod. It only appears when downloaded from + ContentDB. +* `release`: Ignore this: Should only ever be set by ContentDB, as it is an + internal ID used to track versions. +* `title`: A human-readable title to address the mod. Note: to support 0.4.x, please also provide depends.txt. -- cgit v1.2.3 From 34e3ede8eeb05e193e64ba3d055fc67959d87d86 Mon Sep 17 00:00:00 2001 From: tenplus1 Date: Wed, 23 Sep 2020 18:11:56 +0100 Subject: Ability to remove minetest.after once set (#10103) --- builtin/common/after.lua | 6 ++++-- doc/lua_api.txt | 5 ++++- 2 files changed, 8 insertions(+), 3 deletions(-) (limited to 'doc') diff --git a/builtin/common/after.lua b/builtin/common/after.lua index b314711c9..e20f292f0 100644 --- a/builtin/common/after.lua +++ b/builtin/common/after.lua @@ -31,11 +31,13 @@ function core.after(after, func, ...) assert(tonumber(after) and type(func) == "function", "Invalid minetest.after invocation") local expire = time + after - jobs[#jobs + 1] = { + local new_job = { func = func, expire = expire, arg = {...}, - mod_origin = core.get_last_run_mod() + mod_origin = core.get_last_run_mod(), } + jobs[#jobs + 1] = new_job time_next = math.min(time_next, expire) + return { cancel = function() new_job.func = function() end end } end diff --git a/doc/lua_api.txt b/doc/lua_api.txt index a1baadf6d..bd845aad3 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -5336,10 +5336,13 @@ Sounds Timing ------ -* `minetest.after(time, func, ...)` +* `minetest.after(time, func, ...)` : returns job table to use as below. * Call the function `func` after `time` seconds, may be fractional * Optional: Variable number of arguments that are passed to `func` +* `job:cancel()` + * Cancels the job function from being called + Server ------ -- cgit v1.2.3 From 4b423ee9b12aabe6ccd6a8e7dbc7db1d89dc365b Mon Sep 17 00:00:00 2001 From: HybridDog <3192173+HybridDog@users.noreply.github.com> Date: Sat, 3 Oct 2020 18:33:43 +0200 Subject: Chatcommand: Show help message if func returns false without message #9440 --- builtin/game/chat.lua | 13 +++++++++++-- doc/lua_api.txt | 2 ++ 2 files changed, 13 insertions(+), 2 deletions(-) (limited to 'doc') diff --git a/builtin/game/chat.lua b/builtin/game/chat.lua index 8131a6860..1d277730a 100644 --- a/builtin/game/chat.lua +++ b/builtin/game/chat.lua @@ -66,8 +66,17 @@ core.register_on_chat_message(function(name, message) local has_privs, missing_privs = core.check_player_privs(name, cmd_def.privs) if has_privs then core.set_last_run_mod(cmd_def.mod_origin) - local _, result = cmd_def.func(name, param) - if result then + local success, result = cmd_def.func(name, param) + if success == false and result == nil then + core.chat_send_player(name, "-!- Invalid command usage") + local help_def = core.registered_chatcommands["help"] + if help_def then + local _, helpmsg = help_def.func(name, cmd) + if helpmsg then + core.chat_send_player(name, helpmsg) + end + end + elseif result then core.chat_send_player(name, result) end else diff --git a/doc/lua_api.txt b/doc/lua_api.txt index bd845aad3..edacfe05f 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -7949,6 +7949,8 @@ Used by `minetest.register_chatcommand`. func = function(name, param), -- Called when command is run. Returns boolean success and text output. + -- Special case: The help message is shown to the player if `func` + -- returns false without a text output. } Note that in params, use of symbols is as follows: -- cgit v1.2.3 From 7d3641021b755be133dea58404fcb039211fbe52 Mon Sep 17 00:00:00 2001 From: Elijah Duffy Date: Sat, 3 Oct 2020 09:38:08 -0700 Subject: Lua API: Add register_on_chatcommand to SSM and CSM (#7862) Allows catching a chatcommand call just after the command and the parameters are parsed but before its existence is checked and before the corresponding function is run. Returning `true` from a callback function will prevent default handling of the command leaving mods to handle the command manually. --- builtin/client/chatcommands.lua | 5 +++++ builtin/client/register.lua | 1 + builtin/game/chat.lua | 5 +++++ builtin/game/register.lua | 1 + clientmods/preview/init.lua | 4 ++++ doc/client_lua_api.txt | 5 +++++ doc/lua_api.txt | 5 +++++ games/devtest/mods/experimental/commands.lua | 3 +++ 8 files changed, 29 insertions(+) (limited to 'doc') diff --git a/builtin/client/chatcommands.lua b/builtin/client/chatcommands.lua index 5cb1b40bb..0e8d4dd03 100644 --- a/builtin/client/chatcommands.lua +++ b/builtin/client/chatcommands.lua @@ -23,6 +23,11 @@ core.register_on_sending_chat_message(function(message) return true end + -- Run core.registered_on_chatcommand callbacks. + if core.run_callbacks(core.registered_on_chatcommand, 5, cmd, param) then + return true + end + local cmd_def = core.registered_chatcommands[cmd] if cmd_def then core.set_last_run_mod(cmd_def.mod_origin) diff --git a/builtin/client/register.lua b/builtin/client/register.lua index c1b4965c1..acd36ab36 100644 --- a/builtin/client/register.lua +++ b/builtin/client/register.lua @@ -63,6 +63,7 @@ core.registered_on_mods_loaded, core.register_on_mods_loaded = make_registration core.registered_on_shutdown, core.register_on_shutdown = make_registration() core.registered_on_receiving_chat_message, core.register_on_receiving_chat_message = make_registration() core.registered_on_sending_chat_message, core.register_on_sending_chat_message = make_registration() +core.registered_on_chatcommand, core.register_on_chatcommand = make_registration() core.registered_on_death, core.register_on_death = make_registration() core.registered_on_hp_modification, core.register_on_hp_modification = make_registration() core.registered_on_damage_taken, core.register_on_damage_taken = make_registration() diff --git a/builtin/game/chat.lua b/builtin/game/chat.lua index 1d277730a..945707623 100644 --- a/builtin/game/chat.lua +++ b/builtin/game/chat.lua @@ -58,6 +58,11 @@ core.register_on_chat_message(function(name, message) param = param or "" + -- Run core.registered_on_chatcommands callbacks. + if core.run_callbacks(core.registered_on_chatcommands, 5, name, cmd, param) then + return true + end + local cmd_def = core.registered_chatcommands[cmd] if not cmd_def then core.chat_send_player(name, "-!- Invalid command: " .. cmd) diff --git a/builtin/game/register.lua b/builtin/game/register.lua index 1034d4f2b..3de67c04b 100644 --- a/builtin/game/register.lua +++ b/builtin/game/register.lua @@ -584,6 +584,7 @@ core.unregister_biome = make_wrap_deregistration(core.register_biome, core.clear_registered_biomes, core.registered_biomes) core.registered_on_chat_messages, core.register_on_chat_message = make_registration() +core.registered_on_chatcommands, core.register_on_chatcommand = make_registration() core.registered_globalsteps, core.register_globalstep = make_registration() core.registered_playerevents, core.register_playerevent = make_registration() core.registered_on_mods_loaded, core.register_on_mods_loaded = make_registration() diff --git a/clientmods/preview/init.lua b/clientmods/preview/init.lua index 089955d2f..977ed0ec3 100644 --- a/clientmods/preview/init.lua +++ b/clientmods/preview/init.lua @@ -109,6 +109,10 @@ core.register_on_sending_chat_message(function(message) return false end) +core.register_on_chatcommand(function(command, params) + print("[PREVIEW] caught command '"..command.."'. Parameters: '"..params.."'") +end) + -- This is an example function to ensure it's working properly, should be removed before merge core.register_on_hp_modification(function(hp) print("[PREVIEW] HP modified " .. hp) diff --git a/doc/client_lua_api.txt b/doc/client_lua_api.txt index 4c5231b79..32be2fabf 100644 --- a/doc/client_lua_api.txt +++ b/doc/client_lua_api.txt @@ -686,6 +686,11 @@ Call these functions only at load time! * Adds definition to minetest.registered_chatcommands * `minetest.unregister_chatcommand(name)` * Unregisters a chatcommands registered with register_chatcommand. +* `minetest.register_on_chatcommand(function(command, params))` + * Called always when a chatcommand is triggered, before `minetest.registered_chatcommands` + is checked to see if that the command exists, but after the input is parsed. + * Return `true` to mark the command as handled, which means that the default + handlers will be prevented. * `minetest.register_on_death(function())` * Called when the local player dies * `minetest.register_on_hp_modification(function(hp))` diff --git a/doc/lua_api.txt b/doc/lua_api.txt index edacfe05f..e8eb403c4 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -4606,6 +4606,11 @@ Call these functions only at load time! * Called always when a player says something * Return `true` to mark the message as handled, which means that it will not be sent to other players. +* `minetest.register_on_chatcommand(function(name, command, params))` + * Called always when a chatcommand is triggered, before `minetest.registered_chatcommands` + is checked to see if the command exists, but after the input is parsed. + * Return `true` to mark the command as handled, which means that the default + handlers will be prevented. * `minetest.register_on_player_receive_fields(function(player, formname, fields))` * Called when the server received input from `player` in a formspec with the given `formname`. Specifically, this is called on any of the diff --git a/games/devtest/mods/experimental/commands.lua b/games/devtest/mods/experimental/commands.lua index 158e5039d..132b08b0b 100644 --- a/games/devtest/mods/experimental/commands.lua +++ b/games/devtest/mods/experimental/commands.lua @@ -214,3 +214,6 @@ minetest.register_chatcommand("test_place_nodes", { end, }) +core.register_on_chatcommand(function(name, command, params) + minetest.log("caught command '"..command.."', issued by '"..name.."'. Parameters: '"..params.."'") +end) -- cgit v1.2.3 From 962438717913498bc44ec98d37bccc7a4798bca2 Mon Sep 17 00:00:00 2001 From: LoneWolfHT Date: Sat, 3 Oct 2020 10:00:08 -0700 Subject: Add note to docs on how to override privileges (#9792) --- doc/lua_api.txt | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'doc') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index e8eb403c4..5d5e4b93a 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -5767,6 +5767,9 @@ Global tables * Map of registered tool definitions, indexed by name * `minetest.registered_entities` * Map of registered entity prototypes, indexed by name + * Values in this table may be modified directly. + Note: changes to initial properties will only affect entities spawned afterwards, + as they are only read when spawning. * `minetest.object_refs` * Map of object references, indexed by active object id * `minetest.luaentities` @@ -5797,6 +5800,7 @@ Global tables * Map of registered chat command definitions, indexed by name * `minetest.registered_privileges` * Map of registered privilege definitions, indexed by name + * Registered privileges can be modified directly in this table. ### Registered callback tables -- cgit v1.2.3 From 41a6136f774a30364b6d3fd2042c9dbeb34f2109 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Sat, 3 Oct 2020 19:01:38 +0200 Subject: Document builtin entities (#9453) This PR adds a new text file doc/builtin_entities.txt which explains how the two builtin entities __builtin:item and __builtin:falling_node work. --- doc/builtin_entities.txt | 101 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 doc/builtin_entities.txt (limited to 'doc') diff --git a/doc/builtin_entities.txt b/doc/builtin_entities.txt new file mode 100644 index 000000000..be3f73357 --- /dev/null +++ b/doc/builtin_entities.txt @@ -0,0 +1,101 @@ +# Builtin Entities +Minetest registers two entities by default: Falling nodes and dropped items. +This document describes how they behave and what you can do with them. + +## Falling node (`__builtin:falling_node`) + +This entity is created by `minetest.check_for_falling` in place of a node +with the special group `falling_node=1`. Falling nodes can also be created +artificially with `minetest.spawn_falling_node`. + +Needs manual initialization when spawned using `/spawnentity`. + +Default behaviour: + +* Falls down in a straight line (gravity = `movement_gravity` setting) +* Collides with `walkable` node +* Collides with all physical objects except players +* If the node group `float=1` is set, it also collides with liquid nodes +* When it hits a solid (=`walkable`) node, it will try to place itself as a + node, replacing the node above. + * If the falling node cannot replace the destination node, it is dropped. + * If the destination node is a leveled node (`paramtype2="leveled"`) of the + same node name, the levels of both are summed. + +### Entity fields + +* `set_node(self, node[, meta])` + * Function to initialize the falling node + * `node` and `meta` are explained below. + * The `meta` argument is optional. +* `node`: Node table of the node (`name`, `param1`, `param2`) that this + entity represents. Read-only. +* `meta`: Node metadata of the falling node. Will be used when the falling + nodes tries to place itself as a node. Read-only. + +### Rendering / supported nodes + +Falling nodes have visuals to look as close as possible to the original node. +This works for most drawtypes, but there are limitations. + +Supported drawtypes: + +* `normal` +* `signlike` +* `torchlike` +* `nodebox` +* `raillike` +* `glasslike` +* `glasslike_framed` +* `glasslike_framed_optional` +* `allfaces` +* `allfaces_optional` +* `firelike` +* `mesh` +* `fencelike` +* `liquid` +* `airlike` (not pointable) + +Other drawtypes still kinda work, but they might look weird. + +Supported `paramtype2` values: + +* `wallmounted` +* `facedir` +* `colorwallmounted` +* `colorfacedir` +* `color` + +## Dropped item stack (`__builtin:item`) + +This is an item stack in a collectable form. + +Common cases that spawn a dropped item: + +* Item dropped by player +* The root node of a node with the group `attached_node=1` is removed +* `minetest.add_item` is called + +Needs manual initialization when spawned using `/spawnentity`. + +### Behavior + +* Players can collect it by punching +* Lifespan is defined by the setting `item_entity_ttl` +* Slides on `slippery` nodes +* Subject to gravity (uses `movement_gravity` setting) +* Collides with `walkable` nodes +* Does not collide physical objects +* When it's inside a solid (`walkable=true`) node, it tries to escape to a + neighboring non-solid (`walkable=false`) node + +### Entity fields + +* `set_item(self, item)`: + * Function to initialize the dropped item + * `item` (type `ItemStack`) specifies the item to represent +* `age`: Age in seconds. Behaviour according to the setting `item_entity_ttl` +* `itemstring`: Itemstring of the item that this item entity represents. + Read-only. + +Other fields are for internal use only. -- cgit v1.2.3 From 3250b37e32f264db04c1ca449d9feefc22fd38b7 Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Sun, 4 Oct 2020 00:33:45 +0100 Subject: Deprecate get_player_velocity and add_player_velocity (#10173) --- builtin/game/features.lua | 1 + doc/lua_api.txt | 31 +++++++++------- src/script/lua_api/l_base.cpp | 5 ++- src/script/lua_api/l_object.cpp | 82 +++++++++++++++++++---------------------- src/script/lua_api/l_object.h | 6 --- 5 files changed, 58 insertions(+), 67 deletions(-) (limited to 'doc') diff --git a/builtin/game/features.lua b/builtin/game/features.lua index a15475333..4d3c90ff0 100644 --- a/builtin/game/features.lua +++ b/builtin/game/features.lua @@ -17,6 +17,7 @@ core.features = { area_store_persistent_ids = true, pathfinder_works = true, object_step_has_moveresult = true, + direct_velocity_on_players = true, } function core.has_feature(arg) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 5d5e4b93a..c21da1871 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -4340,6 +4340,8 @@ Utilities pathfinder_works = true, -- Whether Collision info is available to an objects' on_step (5.3.0) object_step_has_moveresult = true, + -- Whether get_velocity() and add_velocity() can be used on players (5.4.0) + direct_velocity_on_players = true, } * `minetest.has_feature(arg)`: returns `boolean, missing_features` @@ -6118,6 +6120,19 @@ object you are working with still exists. * `get_pos()`: returns `{x=num, y=num, z=num}` * `set_pos(pos)`: `pos`=`{x=num, y=num, z=num}` +* `get_velocity()`: returns the velocity, a vector. +* `add_velocity(vel)` + * `vel` is a vector, e.g. `{x=0.0, y=2.3, z=1.0}` + * In comparison to using get_velocity, adding the velocity and then using + set_velocity, add_velocity is supposed to avoid synchronization problems. + Additionally, players also do not support set_velocity. + * If a player: + * Does not apply during free_move. + * Note that since the player speed is normalized at each move step, + increasing e.g. Y velocity beyond what would usually be achieved + (see: physics overrides) will cause existing X/Z velocity to be reduced. + * Example: `add_velocity({x=0, y=6.5, z=0})` is equivalent to + pressing the jump key (assuming default settings) * `move_to(pos, continuous=false)` * Does an interpolated move for Lua entities for visually smooth transitions. * If `continuous` is true, the Lua entity will not be moved to the current @@ -6189,11 +6204,6 @@ object you are working with still exists. no effect and returning `nil`. * `set_velocity(vel)` * `vel` is a vector, e.g. `{x=0.0, y=2.3, z=1.0}` -* `add_velocity(vel)` - * `vel` is a vector, e.g. `{x=0.0, y=2.3, z=1.0}` - * In comparison to using get_velocity, adding the velocity and then using - set_velocity, add_velocity is supposed to avoid synchronization problems. -* `get_velocity()`: returns the velocity, a vector * `set_acceleration(acc)` * `acc` is a vector * `get_acceleration()`: returns the acceleration, a vector @@ -6229,16 +6239,9 @@ object you are working with still exists. #### Player only (no-op for other objects) * `get_player_name()`: returns `""` if is not a player -* `get_player_velocity()`: returns `nil` if is not a player, otherwise a +* `get_player_velocity()`: **DEPRECATED**, use get_velocity() instead. table {x, y, z} representing the player's instantaneous velocity in nodes/s -* `add_player_velocity(vel)` - * Adds to player velocity, this happens client-side and only once. - * Does not apply during free_move. - * Note that since the player speed is normalized at each move step, - increasing e.g. Y velocity beyond what would usually be achieved - (see: physics overrides) will cause existing X/Z velocity to be reduced. - * Example: `add_player_velocity({x=0, y=6.5, z=0})` is equivalent to - pressing the jump key (assuming default settings) +* `add_player_velocity(vel)`: **DEPRECATED**, use add_velocity(vel) instead. * `get_look_dir()`: get camera direction as a unit vector * `get_look_vertical()`: pitch in radians * Angle ranges between -pi/2 and pi/2, which are straight up and down diff --git a/src/script/lua_api/l_base.cpp b/src/script/lua_api/l_base.cpp index 2bee09436..03ef5447a 100644 --- a/src/script/lua_api/l_base.cpp +++ b/src/script/lua_api/l_base.cpp @@ -170,10 +170,11 @@ void ModApiBase::markAliasDeprecated(luaL_Reg *reg) m_deprecated_wrappers.emplace( std::pair(reg->name, original_reg)); reg->func = l_deprecated_function; + } else { + last_func = reg->func; + last_name = reg->name; } - last_func = reg->func; - last_name = reg->name; ++reg; } } diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 24667e769..303b1175b 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -863,12 +863,21 @@ int ObjectRef::l_add_velocity(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); - if (!co) + v3f vel = checkFloatPos(L, 2); + + ServerActiveObject *obj = getobject(ref); + if (obj == nullptr) return 0; - v3f pos = checkFloatPos(L, 2); - // Do it - co->addVelocity(pos); + + if (obj->getType() == ACTIVEOBJECT_TYPE_LUAENTITY) { + LuaEntitySAO *co = dynamic_cast(obj); + co->addVelocity(vel); + } else if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER) { + PlayerSAO *player = dynamic_cast(obj); + player->setMaxSpeedOverride(vel); + getServer(L)->SendPlayerSpeed(player->getPeerID(), vel); + } + return 0; } @@ -877,11 +886,23 @@ int ObjectRef::l_get_velocity(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); - if (co == NULL) return 0; - // Do it - v3f v = co->getVelocity(); - pushFloatPos(L, v); + + ServerActiveObject *obj = getobject(ref); + if (obj == nullptr) + return 0; + + if (obj->getType() == ACTIVEOBJECT_TYPE_LUAENTITY) { + LuaEntitySAO *co = dynamic_cast(obj); + v3f v = co->getVelocity(); + pushFloatPos(L, v); + return 1; + } else if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER) { + RemotePlayer *player = dynamic_cast(obj)->getPlayer(); + push_v3f(L, player->getSpeed() / BS); + return 1; + } + + lua_pushnil(L); return 1; } @@ -1082,38 +1103,6 @@ int ObjectRef::l_get_player_name(lua_State *L) return 1; } -// get_player_velocity(self) -int ObjectRef::l_get_player_velocity(lua_State *L) -{ - NO_MAP_LOCK_REQUIRED; - ObjectRef *ref = checkobject(L, 1); - RemotePlayer *player = getplayer(ref); - if (player == NULL) { - lua_pushnil(L); - return 1; - } - // Do it - push_v3f(L, player->getSpeed() / BS); - return 1; -} - -// add_player_velocity(self, {x=num, y=num, z=num}) -int ObjectRef::l_add_player_velocity(lua_State *L) -{ - NO_MAP_LOCK_REQUIRED; - ObjectRef *ref = checkobject(L, 1); - v3f vel = checkFloatPos(L, 2); - - PlayerSAO *co = getplayersao(ref); - if (!co) - return 0; - - // Do it - co->setMaxSpeedOverride(vel); - getServer(L)->SendPlayerSpeed(co->getPeerID(), vel); - return 0; -} - // get_look_dir(self) int ObjectRef::l_get_look_dir(lua_State *L) { @@ -2288,10 +2277,14 @@ luaL_Reg ObjectRef::methods[] = { luamethod(ObjectRef, get_properties), luamethod(ObjectRef, set_nametag_attributes), luamethod(ObjectRef, get_nametag_attributes), - // LuaEntitySAO-only + luamethod_aliased(ObjectRef, set_velocity, setvelocity), luamethod(ObjectRef, add_velocity), + {"add_player_velocity", ObjectRef::l_add_velocity}, luamethod_aliased(ObjectRef, get_velocity, getvelocity), + {"get_player_velocity", ObjectRef::l_get_velocity}, + + // LuaEntitySAO-only luamethod_aliased(ObjectRef, set_acceleration, setacceleration), luamethod_aliased(ObjectRef, get_acceleration, getacceleration), luamethod_aliased(ObjectRef, set_yaw, setyaw), @@ -2307,8 +2300,7 @@ luaL_Reg ObjectRef::methods[] = { luamethod(ObjectRef, is_player), luamethod(ObjectRef, is_player_connected), luamethod(ObjectRef, get_player_name), - luamethod(ObjectRef, get_player_velocity), - luamethod(ObjectRef, add_player_velocity), + luamethod(ObjectRef, get_look_dir), luamethod(ObjectRef, get_look_pitch), luamethod(ObjectRef, get_look_yaw), diff --git a/src/script/lua_api/l_object.h b/src/script/lua_api/l_object.h index a75c59fd9..126719b1f 100644 --- a/src/script/lua_api/l_object.h +++ b/src/script/lua_api/l_object.h @@ -209,12 +209,6 @@ private: // get_player_name(self) static int l_get_player_name(lua_State *L); - // get_player_velocity(self) - static int l_get_player_velocity(lua_State *L); - - // add_player_velocity(self, {x=num, y=num, z=num}) - static int l_add_player_velocity(lua_State *L); - // get_fov(self) static int l_get_fov(lua_State *L); -- cgit v1.2.3 From 3068853e8a58ccc7370a5ce977c08223601c497a Mon Sep 17 00:00:00 2001 From: Jordan Snelling Date: Sun, 4 Oct 2020 14:10:34 +0100 Subject: Add First Person Attachments (#10360) Fixes some other third person camera specific attachments. Implements a single new flag for entities to be forced visible in first person mode. Old mods do not need to be updated to use the new flag and are fully backwards compatible. --- doc/lua_api.txt | 8 +++--- src/activeobject.h | 4 +-- src/client/content_cao.cpp | 56 ++++++++++++++++++++++++++++++++++++----- src/client/content_cao.h | 8 ++++++ src/client/game.cpp | 3 ++- src/clientiface.cpp | 5 ++-- src/script/lua_api/l_camera.cpp | 3 ++- src/script/lua_api/l_object.cpp | 14 +++++++---- src/server/luaentity_sao.cpp | 4 +-- src/server/player_sao.cpp | 9 ++++--- src/server/unit_sao.cpp | 17 +++++++------ src/server/unit_sao.h | 5 ++-- 12 files changed, 101 insertions(+), 35 deletions(-) (limited to 'doc') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index c21da1871..77fb4a654 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -6167,12 +6167,14 @@ object you are working with still exists. `frame_loop`. * `set_animation_frame_speed(frame_speed)` * `frame_speed`: number, default: `15.0` -* `set_attach(parent, bone, position, rotation)` +* `set_attach(parent, bone, position, rotation, forced_visible)` * `bone`: string * `position`: `{x=num, y=num, z=num}` (relative) * `rotation`: `{x=num, y=num, z=num}` = Rotation on each axis, in degrees -* `get_attach()`: returns parent, bone, position, rotation or nil if it isn't - attached. + * `forced_visible`: Boolean to control whether the attached entity + should appear in first person. +* `get_attach()`: returns parent, bone, position, rotation, forced_visible, + or nil if it isn't attached. * `set_detach()` * `set_bone_position(bone, position, rotation)` * `bone`: string diff --git a/src/activeobject.h b/src/activeobject.h index 85e160d10..0829858ad 100644 --- a/src/activeobject.h +++ b/src/activeobject.h @@ -120,9 +120,9 @@ public: virtual void setAttachment(int parent_id, const std::string &bone, v3f position, - v3f rotation) {} + v3f rotation, bool force_visible) {} virtual void getAttachment(int *parent_id, std::string *bone, v3f *position, - v3f *rotation) const {} + v3f *rotation, bool *force_visible) const {} virtual void clearChildAttachments() {} virtual void clearParentAttachment() {} virtual void addAttachmentChild(int child_id) {} diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index c1715a289..fae06554a 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -456,7 +456,8 @@ void GenericCAO::setChildrenVisible(bool toset) for (u16 cao_id : m_attachment_child_ids) { GenericCAO *obj = m_env->getGenericCAO(cao_id); if (obj) { - obj->setVisible(toset); + // Check if the entity is forced to appear in first person. + obj->setVisible(obj->isForcedVisible() ? true : toset); } } } @@ -477,8 +478,6 @@ void GenericCAO::setAttachment(int parent_id, const std::string &bone, v3f posit if (parent) parent->addAttachmentChild(m_id); } - - updateAttachments(); } @@ -498,7 +497,7 @@ void GenericCAO::clearChildAttachments() int child_id = *m_attachment_child_ids.begin(); if (ClientActiveObject *child = m_env->getActiveObject(child_id)) - child->setAttachment(0, "", v3f(), v3f()); + child->setAttachment(0, "", v3f(), v3f(), false); removeAttachmentChild(child_id); } @@ -800,6 +799,7 @@ void GenericCAO::addToScene(ITextureSource *tsrc) updateBonePosition(); updateAttachments(); setNodeLight(m_last_light); + updateMeshCulling(); } void GenericCAO::updateLight(u32 day_night_ratio) @@ -1411,6 +1411,9 @@ void GenericCAO::updateTextures(std::string mod) setMeshColor(mesh, m_prop.colors[0]); } } + // Prevent showing the player after changing texture + if (m_is_local_player) + updateMeshCulling(); } void GenericCAO::updateAnimation() @@ -1739,12 +1742,25 @@ void GenericCAO::processMessage(const std::string &data) std::string bone = deSerializeString16(is); v3f position = readV3F32(is); v3f rotation = readV3F32(is); + m_force_visible = readU8(is); // Returns false for EOF setAttachment(parent_id, bone, position, rotation); + // Forcibly show attachments if required by set_attach + if (m_force_visible) + m_is_visible = true; // localplayer itself can't be attached to localplayer - if (!m_is_local_player) - m_is_visible = !m_attached_to_local; + else if (!m_is_local_player) { + // Objects attached to the local player should be hidden in first + // person provided the forced boolean isn't set. + m_is_visible = !m_attached_to_local || + m_client->getCamera()->getCameraMode() != CAMERA_MODE_FIRST; + m_force_visible = false; + } else { + // Local players need to have this set, + // otherwise first person attachments fail. + m_is_visible = true; + } } else if (cmd == AO_CMD_PUNCHED) { u16 result_hp = readU16(is); @@ -1858,5 +1874,33 @@ std::string GenericCAO::debugInfoText() return os.str(); } +void GenericCAO::updateMeshCulling() +{ + if (!m_is_local_player) + return; + + // Grab the active player scene node so we know there's + // at least a mesh to occlude from the camera. + irr::scene::ISceneNode *node = getSceneNode(); + if (!node) + return; + + if (m_client->getCamera()->getCameraMode() == CAMERA_MODE_FIRST) { + // Hide the mesh by culling both front and + // back faces. Serious hackyness but it works for our + // purposes. This also preserves the skeletal armature. + node->setMaterialFlag(video::EMF_BACK_FACE_CULLING, + true); + node->setMaterialFlag(video::EMF_FRONT_FACE_CULLING, + true); + } else { + // Restore mesh visibility. + node->setMaterialFlag(video::EMF_BACK_FACE_CULLING, + m_prop.backface_culling); + node->setMaterialFlag(video::EMF_FRONT_FACE_CULLING, + false); + } +} + // Prototype GenericCAO proto_GenericCAO(NULL, NULL); diff --git a/src/client/content_cao.h b/src/client/content_cao.h index 974ff9a1e..daf697767 100644 --- a/src/client/content_cao.h +++ b/src/client/content_cao.h @@ -124,6 +124,7 @@ private: float m_step_distance_counter = 0.0f; u8 m_last_light = 255; bool m_is_visible = false; + bool m_force_visible = false; s8 m_glow = 0; // Material video::E_MATERIAL_TYPE m_material_type; @@ -215,6 +216,11 @@ public: m_is_visible = toset; } + inline bool isForcedVisible() const + { + return m_force_visible; + } + void setChildrenVisible(bool toset); void setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation); void getAttachment(int *parent_id, std::string *bone, v3f *position, @@ -275,4 +281,6 @@ public: { return m_prop.infotext; } + + void updateMeshCulling(); }; diff --git a/src/client/game.cpp b/src/client/game.cpp index 8f9d51417..366464467 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -2950,7 +2950,8 @@ void Game::updateCamera(u32 busy_time, f32 dtime) camera->toggleCameraMode(); - playercao->setVisible(camera->getCameraMode() > CAMERA_MODE_FIRST); + // Make the player visible depending on camera mode. + playercao->updateMeshCulling(); playercao->setChildrenVisible(camera->getCameraMode() > CAMERA_MODE_FIRST); } diff --git a/src/clientiface.cpp b/src/clientiface.cpp index 602a44c90..28a0ee770 100644 --- a/src/clientiface.cpp +++ b/src/clientiface.cpp @@ -80,10 +80,11 @@ LuaEntitySAO *getAttachedObject(PlayerSAO *sao, ServerEnvironment *env) int id; std::string bone; v3f dummy; - sao->getAttachment(&id, &bone, &dummy, &dummy); + bool force_visible; + sao->getAttachment(&id, &bone, &dummy, &dummy, &force_visible); ServerActiveObject *ao = env->getActiveObject(id); while (id && ao) { - ao->getAttachment(&id, &bone, &dummy, &dummy); + ao->getAttachment(&id, &bone, &dummy, &dummy, &force_visible); if (id) ao = env->getActiveObject(id); } diff --git a/src/script/lua_api/l_camera.cpp b/src/script/lua_api/l_camera.cpp index bfa60be67..40251154c 100644 --- a/src/script/lua_api/l_camera.cpp +++ b/src/script/lua_api/l_camera.cpp @@ -63,7 +63,8 @@ int LuaCamera::l_set_camera_mode(lua_State *L) return 0; camera->setCameraMode((CameraMode)((int)lua_tonumber(L, 2))); - playercao->setVisible(camera->getCameraMode() > CAMERA_MODE_FIRST); + // Make the player visible depending on camera mode. + playercao->updateMeshCulling(); playercao->setChildrenVisible(camera->getCameraMode() > CAMERA_MODE_FIRST); return 0; } diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 303b1175b..fead4e849 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -664,7 +664,7 @@ int ObjectRef::l_get_bone_position(lua_State *L) return 2; } -// set_attach(self, parent, bone, position, rotation) +// set_attach(self, parent, bone, position, rotation, force_visible) int ObjectRef::l_set_attach(lua_State *L) { GET_ENV_PTR; @@ -687,7 +687,8 @@ int ObjectRef::l_set_attach(lua_State *L) std::string bone; v3f position = v3f(0, 0, 0); v3f rotation = v3f(0, 0, 0); - co->getAttachment(&parent_id, &bone, &position, &rotation); + bool force_visible; + co->getAttachment(&parent_id, &bone, &position, &rotation, &force_visible); if (parent_id) { ServerActiveObject *old_parent = env->getActiveObject(parent_id); old_parent->removeAttachmentChild(co->getId()); @@ -702,7 +703,8 @@ int ObjectRef::l_set_attach(lua_State *L) rotation = v3f(0, 0, 0); if (!lua_isnil(L, 5)) rotation = read_v3f(L, 5); - co->setAttachment(parent->getId(), bone, position, rotation); + force_visible = readParam(L, 6, false); + co->setAttachment(parent->getId(), bone, position, rotation, force_visible); parent->addAttachmentChild(co->getId()); return 0; } @@ -722,7 +724,8 @@ int ObjectRef::l_get_attach(lua_State *L) std::string bone; v3f position = v3f(0, 0, 0); v3f rotation = v3f(0, 0, 0); - co->getAttachment(&parent_id, &bone, &position, &rotation); + bool force_visible; + co->getAttachment(&parent_id, &bone, &position, &rotation, &force_visible); if (!parent_id) return 0; ServerActiveObject *parent = env->getActiveObject(parent_id); @@ -731,7 +734,8 @@ int ObjectRef::l_get_attach(lua_State *L) lua_pushlstring(L, bone.c_str(), bone.size()); push_v3f(L, position); push_v3f(L, rotation); - return 4; + lua_pushboolean(L, force_visible); + return 5; } // set_detach(self) diff --git a/src/server/luaentity_sao.cpp b/src/server/luaentity_sao.cpp index f20914f7f..b39797531 100644 --- a/src/server/luaentity_sao.cpp +++ b/src/server/luaentity_sao.cpp @@ -238,9 +238,9 @@ std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version) msg_os << serializeString32(generateUpdateAnimationCommand()); // 3 for (const auto &bone_pos : m_bone_position) { msg_os << serializeString32(generateUpdateBonePositionCommand( - bone_pos.first, bone_pos.second.X, bone_pos.second.Y)); // m_bone_position.size + bone_pos.first, bone_pos.second.X, bone_pos.second.Y)); // 3 + N } - msg_os << serializeString32(generateUpdateAttachmentCommand()); // 4 + msg_os << serializeString32(generateUpdateAttachmentCommand()); // 4 + m_bone_position.size int message_count = 4 + m_bone_position.size(); diff --git a/src/server/player_sao.cpp b/src/server/player_sao.cpp index 8d4938c3c..344d18a20 100644 --- a/src/server/player_sao.cpp +++ b/src/server/player_sao.cpp @@ -122,10 +122,10 @@ std::string PlayerSAO::getClientInitializationData(u16 protocol_version) msg_os << serializeString32(generateUpdateAnimationCommand()); // 3 for (const auto &bone_pos : m_bone_position) { msg_os << serializeString32(generateUpdateBonePositionCommand( - bone_pos.first, bone_pos.second.X, bone_pos.second.Y)); // m_bone_position.size + bone_pos.first, bone_pos.second.X, bone_pos.second.Y)); // 3 + N } - msg_os << serializeString32(generateUpdateAttachmentCommand()); // 4 - msg_os << serializeString32(generateUpdatePhysicsOverrideCommand()); // 5 + msg_os << serializeString32(generateUpdateAttachmentCommand()); // 4 + m_bone_position.size + msg_os << serializeString32(generateUpdatePhysicsOverrideCommand()); // 5 + m_bone_position.size int message_count = 5 + m_bone_position.size(); @@ -569,7 +569,8 @@ bool PlayerSAO::checkMovementCheat() int parent_id; std::string bone; v3f attachment_rot; - getAttachment(&parent_id, &bone, &attachment_pos, &attachment_rot); + bool force_visible; + getAttachment(&parent_id, &bone, &attachment_pos, &attachment_rot, &force_visible); } v3f parent_pos = parent->getBasePosition(); diff --git a/src/server/unit_sao.cpp b/src/server/unit_sao.cpp index d906e885e..2371640ca 100644 --- a/src/server/unit_sao.cpp +++ b/src/server/unit_sao.cpp @@ -121,8 +121,8 @@ void UnitSAO::sendOutdatedData() } // clang-format on -void UnitSAO::setAttachment( - int parent_id, const std::string &bone, v3f position, v3f rotation) +void UnitSAO::setAttachment(int parent_id, const std::string &bone, v3f position, + v3f rotation, bool force_visible) { // Attachments need to be handled on both the server and client. // If we just attach on the server, we can only copy the position of the parent. @@ -137,6 +137,7 @@ void UnitSAO::setAttachment( m_attachment_bone = bone; m_attachment_position = position; m_attachment_rotation = rotation; + m_force_visible = force_visible; m_attachment_sent = false; if (parent_id != old_parent) { @@ -145,13 +146,14 @@ void UnitSAO::setAttachment( } } -void UnitSAO::getAttachment( - int *parent_id, std::string *bone, v3f *position, v3f *rotation) const +void UnitSAO::getAttachment(int *parent_id, std::string *bone, v3f *position, + v3f *rotation, bool *force_visible) const { *parent_id = m_attachment_parent_id; *bone = m_attachment_bone; *position = m_attachment_position; *rotation = m_attachment_rotation; + *force_visible = m_force_visible; } void UnitSAO::clearChildAttachments() @@ -159,7 +161,7 @@ void UnitSAO::clearChildAttachments() for (int child_id : m_attachment_child_ids) { // Child can be NULL if it was deleted earlier if (ServerActiveObject *child = m_env->getActiveObject(child_id)) - child->setAttachment(0, "", v3f(0, 0, 0), v3f(0, 0, 0)); + child->setAttachment(0, "", v3f(0, 0, 0), v3f(0, 0, 0), false); } m_attachment_child_ids.clear(); } @@ -169,9 +171,9 @@ void UnitSAO::clearParentAttachment() ServerActiveObject *parent = nullptr; if (m_attachment_parent_id) { parent = m_env->getActiveObject(m_attachment_parent_id); - setAttachment(0, "", m_attachment_position, m_attachment_rotation); + setAttachment(0, "", m_attachment_position, m_attachment_rotation, false); } else { - setAttachment(0, "", v3f(0, 0, 0), v3f(0, 0, 0)); + setAttachment(0, "", v3f(0, 0, 0), v3f(0, 0, 0), false); } // Do it if (parent) @@ -245,6 +247,7 @@ std::string UnitSAO::generateUpdateAttachmentCommand() const os << serializeString16(m_attachment_bone); writeV3F32(os, m_attachment_position); writeV3F32(os, m_attachment_rotation); + writeU8(os, m_force_visible); return os.str(); } diff --git a/src/server/unit_sao.h b/src/server/unit_sao.h index 3cb7f0ad5..a21e055c5 100644 --- a/src/server/unit_sao.h +++ b/src/server/unit_sao.h @@ -64,9 +64,9 @@ public: ServerActiveObject *getParent() const; inline bool isAttached() const { return getParent(); } void setAttachment(int parent_id, const std::string &bone, v3f position, - v3f rotation); + v3f rotation, bool force_visible); void getAttachment(int *parent_id, std::string *bone, v3f *position, - v3f *rotation) const; + v3f *rotation, bool *force_visible) const; void clearChildAttachments(); void clearParentAttachment(); void addAttachmentChild(int child_id); @@ -133,4 +133,5 @@ private: v3f m_attachment_position; v3f m_attachment_rotation; bool m_attachment_sent = false; + bool m_force_visible = false; }; -- cgit v1.2.3 From 81c66d6efb9fb0ab8a03b40e2bc22aa49eff9a04 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Rollo Date: Sun, 4 Oct 2020 15:24:29 +0200 Subject: Minimap as HUD element with API control Features: * Define Minimap available modes (surface/radar, scale) from Lua, using player:set_minimap_modes() * New HUD elements for displaying minimap with custom size and placing * New minimap mode for displaying a texture instead of the map --- doc/lua_api.txt | 33 ++++- src/client/client.cpp | 1 + src/client/client.h | 1 + src/client/game.cpp | 75 +++++------- src/client/hud.cpp | 30 ++++- src/client/hud.h | 2 + src/client/mapblock_mesh.cpp | 2 +- src/client/minimap.cpp | 237 +++++++++++++++++++++++++++--------- src/client/minimap.h | 41 ++++--- src/hud.cpp | 1 + src/hud.h | 13 +- src/network/clientopcodes.cpp | 1 + src/network/clientpackethandler.cpp | 46 ++++++- src/network/networkprotocol.h | 14 ++- src/network/serveropcodes.cpp | 1 + src/script/lua_api/l_minimap.cpp | 24 ++-- src/script/lua_api/l_object.cpp | 62 ++++++++++ src/script/lua_api/l_object.h | 3 + src/server.cpp | 17 +++ src/server.h | 12 ++ 20 files changed, 471 insertions(+), 145 deletions(-) (limited to 'doc') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 77fb4a654..9e9af20da 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1484,6 +1484,15 @@ Displays an image oriented or translated according to current heading direction. If translation is chosen, texture is repeated horizontally to fill the whole element. +### `minimap` + +Displays a minimap on the HUD. + +* `size`: Size of the minimap to display. Minimap should be a square to avoid + distortion. +* `alignment`: The alignment of the minimap. +* `offset`: offset in pixels from position. + Representations of simple things ================================ @@ -6366,6 +6375,27 @@ object you are working with still exists. * `hud_set_hotbar_selected_image(texturename)` * sets image for selected item of hotbar * `hud_get_hotbar_selected_image`: returns texturename +* `set_minimap_modes({mode, mode, ...}, selected_mode)` + * Overrides the available minimap modes (and toggle order), and changes the + selected mode. + * `mode` is a table consisting of up to four fields: + * `type`: Available type: + * `off`: Minimap off + * `surface`: Minimap in surface mode + * `radar`: Minimap in radar mode + * `texture`: Texture to be displayed instead of terrain map + (texture is centered around 0,0 and can be scaled). + Texture size is limited to 512 x 512 pixel. + * `label`: Optional label to display on minimap mode toggle + The translation must be handled within the mod. + * `size`: Sidelength or diameter, in number of nodes, of the terrain + displayed in minimap + * `texture`: Only for texture type, name of the texture to display + * `scale`: Only for texture type, scale of the texture map in nodes per + pixel (for example a `scale` of 2 means each pixel represents a 2x2 + nodes square) + * `selected_mode` is the mode index to be selected after modes have been changed + (0 is the first mode). * `set_sky(parameters)` * `parameters` is a table with the following optional fields: * `base_color`: ColorSpec, changes fog in "skybox" and "plain". @@ -8047,7 +8077,8 @@ Used by `Player:hud_add`. Returned by `Player:hud_get`. { hud_elem_type = "image", -- See HUD element types - -- Type of element, can be "image", "text", "statbar", "inventory" or "compass" + -- Type of element, can be "image", "text", "statbar", "inventory", + -- "compass" or "minimap" position = {x=0.5, y=0.5}, -- Left corner position of element diff --git a/src/client/client.cpp b/src/client/client.cpp index d6e529c40..7fafe0da9 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -129,6 +129,7 @@ Client::Client( if (g_settings->getBool("enable_minimap")) { m_minimap = new Minimap(this); } + m_cache_save_interval = g_settings->getU16("server_map_save_interval"); } diff --git a/src/client/client.h b/src/client/client.h index 733634db1..8f4aac6e3 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -223,6 +223,7 @@ public: void handleCommand_CSMRestrictionFlags(NetworkPacket *pkt); void handleCommand_PlayerSpeed(NetworkPacket *pkt); void handleCommand_MediaPush(NetworkPacket *pkt); + void handleCommand_MinimapModes(NetworkPacket *pkt); void ProcessData(NetworkPacket *pkt); diff --git a/src/client/game.cpp b/src/client/game.cpp index 366464467..54e0c9f20 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -1416,11 +1416,9 @@ bool Game::createClient(const GameStartData &start_data) } mapper = client->getMinimap(); - if (mapper) { - mapper->setMinimapMode(MINIMAP_MODE_OFF); - if (client->modsLoaded()) - client->getScript()->on_minimap_ready(mapper); - } + + if (mapper && client->modsLoaded()) + client->getScript()->on_minimap_ready(mapper); return true; } @@ -2222,52 +2220,37 @@ void Game::toggleMinimap(bool shift_pressed) if (!mapper || !m_game_ui->m_flags.show_hud || !g_settings->getBool("enable_minimap")) return; - if (shift_pressed) { + if (shift_pressed) mapper->toggleMinimapShape(); - return; - } + else + mapper->nextMode(); + + // TODO: When legacy minimap is deprecated, keep only HUD minimap stuff here + // Not so satisying code to keep compatibility with old fixed mode system + // --> u32 hud_flags = client->getEnv().getLocalPlayer()->hud_flags; - MinimapMode mode = MINIMAP_MODE_OFF; - if (hud_flags & HUD_FLAG_MINIMAP_VISIBLE) { - mode = mapper->getMinimapMode(); - mode = (MinimapMode)((int)mode + 1); - // If radar is disabled and in, or switching to, radar mode - if (!(hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE) && mode > 3) - mode = MINIMAP_MODE_OFF; - } + if (!(hud_flags & HUD_FLAG_MINIMAP_VISIBLE)) { + m_game_ui->m_flags.show_minimap = false; + } else { - m_game_ui->m_flags.show_minimap = true; - switch (mode) { - case MINIMAP_MODE_SURFACEx1: - m_game_ui->showTranslatedStatusText("Minimap in surface mode, Zoom x1"); - break; - case MINIMAP_MODE_SURFACEx2: - m_game_ui->showTranslatedStatusText("Minimap in surface mode, Zoom x2"); - break; - case MINIMAP_MODE_SURFACEx4: - m_game_ui->showTranslatedStatusText("Minimap in surface mode, Zoom x4"); - break; - case MINIMAP_MODE_RADARx1: - m_game_ui->showTranslatedStatusText("Minimap in radar mode, Zoom x1"); - break; - case MINIMAP_MODE_RADARx2: - m_game_ui->showTranslatedStatusText("Minimap in radar mode, Zoom x2"); - break; - case MINIMAP_MODE_RADARx4: - m_game_ui->showTranslatedStatusText("Minimap in radar mode, Zoom x4"); - break; - default: - mode = MINIMAP_MODE_OFF; - m_game_ui->m_flags.show_minimap = false; - if (hud_flags & HUD_FLAG_MINIMAP_VISIBLE) - m_game_ui->showTranslatedStatusText("Minimap hidden"); - else - m_game_ui->showTranslatedStatusText("Minimap currently disabled by game or mod"); - } + // If radar is disabled, try to find a non radar mode or fall back to 0 + if (!(hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE)) + while (mapper->getModeIndex() && + mapper->getModeDef().type == MINIMAP_TYPE_RADAR) + mapper->nextMode(); - mapper->setMinimapMode(mode); + m_game_ui->m_flags.show_minimap = mapper->getModeDef().type != + MINIMAP_TYPE_OFF; + } + // <-- + // End of 'not so satifying code' + if ((hud_flags & HUD_FLAG_MINIMAP_VISIBLE) || + (hud && hud->hasElementOfType(HUD_ELEM_MINIMAP))) + m_game_ui->showStatusText(utf8_to_wide(mapper->getModeDef().label)); + else + m_game_ui->showTranslatedStatusText("Minimap currently disabled by game or mod"); } void Game::toggleFog() @@ -3954,7 +3937,7 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, /* Update minimap pos and rotation */ - if (mapper && m_game_ui->m_flags.show_minimap && m_game_ui->m_flags.show_hud) { + if (mapper && m_game_ui->m_flags.show_hud) { mapper->setPos(floatToInt(player->getPosition(), BS)); mapper->setAngle(player->getYaw()); } diff --git a/src/client/hud.cpp b/src/client/hud.cpp index d3038230c..f6497fe25 100644 --- a/src/client/hud.cpp +++ b/src/client/hud.cpp @@ -36,6 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mesh.h" #include "wieldmesh.h" #include "client/renderingengine.h" +#include "client/minimap.h" #ifdef HAVE_TOUCHSCREENGUI #include "gui/touchscreengui.h" @@ -297,6 +298,18 @@ void Hud::drawItems(v2s32 upperleftpos, v2s32 screen_offset, s32 itemcount, } } +bool Hud::hasElementOfType(HudElementType type) +{ + for (size_t i = 0; i != player->maxHudId(); i++) { + HudElement *e = player->getHud(i); + if (!e) + continue; + if (e->type == type) + return true; + } + return false; +} + // Calculates screen position of waypoint. Returns true if waypoint is visible (in front of the player), else false. bool Hud::calculateScreenPos(const v3s16 &camera_offset, HudElement *e, v2s32 *pos) { @@ -491,7 +504,22 @@ void Hud::drawLuaElements(const v3s16 &camera_offset) default: break; } - + break; } + case HUD_ELEM_MINIMAP: { + if (e->size.X <= 0 || e->size.Y <= 0) + break; + if (!client->getMinimap()) + break; + // Draw a minimap of size "size" + v2s32 dstsize(e->size.X * m_scale_factor, + e->size.Y * m_scale_factor); + // (no percent size as minimap would likely be anamorphosed) + v2s32 offset((e->align.X - 1.0) * dstsize.X / 2, + (e->align.Y - 1.0) * dstsize.Y / 2); + core::rect rect(0, 0, dstsize.X, dstsize.Y); + rect += pos + offset + v2s32(e->offset.X * m_scale_factor, + e->offset.Y * m_scale_factor); + client->getMinimap()->drawMinimap(rect); break; } default: infostream << "Hud::drawLuaElements: ignoring drawform " << e->type << diff --git a/src/client/hud.h b/src/client/hud.h index cf83cb16e..d46545d71 100644 --- a/src/client/hud.h +++ b/src/client/hud.h @@ -81,6 +81,8 @@ public: m_selected_face_normal = face_normal; } + bool hasElementOfType(HudElementType type); + void drawLuaElements(const v3s16 &camera_offset); private: diff --git a/src/client/mapblock_mesh.cpp b/src/client/mapblock_mesh.cpp index 2f96ca61f..b59679523 100644 --- a/src/client/mapblock_mesh.cpp +++ b/src/client/mapblock_mesh.cpp @@ -1044,7 +1044,7 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset): m_use_tangent_vertices = data->m_use_tangent_vertices; m_enable_vbo = g_settings->getBool("enable_vbo"); - if (g_settings->getBool("enable_minimap")) { + if (data->m_client->getMinimap()) { m_minimap_mapblock = new MinimapMapblock; m_minimap_mapblock->getMinimapNodes( &data->m_vmanip, data->m_blockpos * MAP_BLOCKSIZE); diff --git a/src/client/minimap.cpp b/src/client/minimap.cpp index 68770ec19..6bae408b4 100644 --- a/src/client/minimap.cpp +++ b/src/client/minimap.cpp @@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "shader.h" #include "mapblock.h" #include "client/renderingengine.h" - +#include "gettext.h" //// //// MinimapUpdateThread @@ -108,8 +108,11 @@ void MinimapUpdateThread::doUpdate() } } - if (data->map_invalidated && data->mode != MINIMAP_MODE_OFF) { - getMap(data->pos, data->map_size, data->scan_height); + + if (data->map_invalidated && ( + data->mode.type == MINIMAP_TYPE_RADAR || + data->mode.type == MINIMAP_TYPE_SURFACE)) { + getMap(data->pos, data->mode.map_size, data->mode.scan_height); data->map_invalidated = false; } } @@ -181,19 +184,26 @@ Minimap::Minimap(Client *client) this->m_ndef = client->getNodeDefManager(); m_angle = 0.f; + m_current_mode_index = 0; // Initialize static settings m_enable_shaders = g_settings->getBool("enable_shaders"); m_surface_mode_scan_height = g_settings->getBool("minimap_double_scan_height") ? 256 : 128; + // Initialize minimap modes + addMode(MINIMAP_TYPE_OFF); + addMode(MINIMAP_TYPE_SURFACE, 256); + addMode(MINIMAP_TYPE_SURFACE, 128); + addMode(MINIMAP_TYPE_SURFACE, 64); + addMode(MINIMAP_TYPE_RADAR, 512); + addMode(MINIMAP_TYPE_RADAR, 256); + addMode(MINIMAP_TYPE_RADAR, 128); + // Initialize minimap data data = new MinimapData; - data->mode = MINIMAP_MODE_OFF; - data->is_radar = false; - data->map_invalidated = true; - data->texture = NULL; - data->heightmap_texture = NULL; + data->map_invalidated = true; + data->minimap_shape_round = g_settings->getBool("minimap_shape_round"); // Get round minimap textures @@ -215,6 +225,8 @@ Minimap::Minimap(Client *client) // Create object marker texture data->object_marker_red = m_tsrc->getTexture("object_marker_red.png"); + setModeIndex(0); + // Create mesh buffer for minimap m_meshbuffer = getMinimapMeshBuffer(); @@ -280,29 +292,101 @@ MinimapShape Minimap::getMinimapShape() return MINIMAP_SHAPE_SQUARE; } -void Minimap::setMinimapMode(MinimapMode mode) +void Minimap::setModeIndex(size_t index) { - static const MinimapModeDef modedefs[MINIMAP_MODE_COUNT] = { - {false, 0, 0}, - {false, m_surface_mode_scan_height, 256}, - {false, m_surface_mode_scan_height, 128}, - {false, m_surface_mode_scan_height, 64}, - {true, 32, 128}, - {true, 32, 64}, - {true, 32, 32} - }; - - if (mode >= MINIMAP_MODE_COUNT) - return; - MutexAutoLock lock(m_mutex); - data->is_radar = modedefs[mode].is_radar; - data->scan_height = modedefs[mode].scan_height; - data->map_size = modedefs[mode].map_size; - data->mode = mode; + if (index < m_modes.size()) { + data->mode = m_modes[index]; + m_current_mode_index = index; + } else { + data->mode = MinimapModeDef{MINIMAP_TYPE_OFF, N_("Minimap hidden"), 0, 0, ""}; + m_current_mode_index = 0; + } + + data->map_invalidated = true; - m_minimap_update_thread->deferUpdate(); + if (m_minimap_update_thread) + m_minimap_update_thread->deferUpdate(); +} + +void Minimap::addMode(MinimapModeDef mode) +{ + // Check validity + if (mode.type == MINIMAP_TYPE_TEXTURE) { + if (mode.texture.empty()) + return; + if (mode.scale < 1) + mode.scale = 1; + } + + int zoom = -1; + + // Build a default standard label + if (mode.label == "") { + switch (mode.type) { + case MINIMAP_TYPE_OFF: + mode.label = N_("Minimap hidden"); + break; + case MINIMAP_TYPE_SURFACE: + mode.label = N_("Minimap in surface mode, Zoom x%d"); + if (mode.map_size > 0) + zoom = 256 / mode.map_size; + break; + case MINIMAP_TYPE_RADAR: + mode.label = N_("Minimap in radar mode, Zoom x%d"); + if (mode.map_size > 0) + zoom = 512 / mode.map_size; + break; + case MINIMAP_TYPE_TEXTURE: + mode.label = N_("Minimap in texture mode"); + break; + default: + break; + } + } + + if (zoom >= 0) { + char label_buf[1024]; + porting::mt_snprintf(label_buf, sizeof(label_buf), + mode.label.c_str(), zoom); + mode.label = label_buf; + } + + m_modes.push_back(mode); +} + +void Minimap::addMode(MinimapType type, u16 size, std::string label, + std::string texture, u16 scale) +{ + MinimapModeDef mode; + mode.type = type; + mode.label = label; + mode.map_size = size; + mode.texture = texture; + mode.scale = scale; + switch (type) { + case MINIMAP_TYPE_SURFACE: + mode.scan_height = m_surface_mode_scan_height; + break; + case MINIMAP_TYPE_RADAR: + mode.scan_height = 32; + break; + default: + mode.scan_height = 0; + } + addMode(mode); +} + +void Minimap::nextMode() +{ + if (m_modes.empty()) + return; + m_current_mode_index++; + if (m_current_mode_index >= m_modes.size()) + m_current_mode_index = 0; + + setModeIndex(m_current_mode_index); } void Minimap::setPos(v3s16 pos) @@ -331,16 +415,16 @@ void Minimap::setAngle(f32 angle) void Minimap::blitMinimapPixelsToImageRadar(video::IImage *map_image) { video::SColor c(240, 0, 0, 0); - for (s16 x = 0; x < data->map_size; x++) - for (s16 z = 0; z < data->map_size; z++) { - MinimapPixel *mmpixel = &data->minimap_scan[x + z * data->map_size]; + for (s16 x = 0; x < data->mode.map_size; x++) + for (s16 z = 0; z < data->mode.map_size; z++) { + MinimapPixel *mmpixel = &data->minimap_scan[x + z * data->mode.map_size]; if (mmpixel->air_count > 0) c.setGreen(core::clamp(core::round32(32 + mmpixel->air_count * 8), 0, 255)); else c.setGreen(0); - map_image->setPixel(x, data->map_size - z - 1, c); + map_image->setPixel(x, data->mode.map_size - z - 1, c); } } @@ -349,9 +433,9 @@ void Minimap::blitMinimapPixelsToImageSurface( { // This variable creation/destruction has a 1% cost on rendering minimap video::SColor tilecolor; - for (s16 x = 0; x < data->map_size; x++) - for (s16 z = 0; z < data->map_size; z++) { - MinimapPixel *mmpixel = &data->minimap_scan[x + z * data->map_size]; + for (s16 x = 0; x < data->mode.map_size; x++) + for (s16 z = 0; z < data->mode.map_size; z++) { + MinimapPixel *mmpixel = &data->minimap_scan[x + z * data->mode.map_size]; const ContentFeatures &f = m_ndef->get(mmpixel->n); const TileDef *tile = &f.tiledef[0]; @@ -367,10 +451,10 @@ void Minimap::blitMinimapPixelsToImageSurface( tilecolor.setBlue(tilecolor.getBlue() * f.minimap_color.getBlue() / 255); tilecolor.setAlpha(240); - map_image->setPixel(x, data->map_size - z - 1, tilecolor); + map_image->setPixel(x, data->mode.map_size - z - 1, tilecolor); u32 h = mmpixel->height; - heightmap_image->setPixel(x,data->map_size - z - 1, + heightmap_image->setPixel(x,data->mode.map_size - z - 1, video::SColor(255, h, h, h)); } } @@ -378,21 +462,46 @@ void Minimap::blitMinimapPixelsToImageSurface( video::ITexture *Minimap::getMinimapTexture() { // update minimap textures when new scan is ready - if (data->map_invalidated) + if (data->map_invalidated && data->mode.type != MINIMAP_TYPE_TEXTURE) return data->texture; // create minimap and heightmap images in memory - core::dimension2d dim(data->map_size, data->map_size); + core::dimension2d dim(data->mode.map_size, data->mode.map_size); video::IImage *map_image = driver->createImage(video::ECF_A8R8G8B8, dim); video::IImage *heightmap_image = driver->createImage(video::ECF_A8R8G8B8, dim); video::IImage *minimap_image = driver->createImage(video::ECF_A8R8G8B8, core::dimension2d(MINIMAP_MAX_SX, MINIMAP_MAX_SY)); // Blit MinimapPixels to images - if (data->is_radar) - blitMinimapPixelsToImageRadar(map_image); - else + switch(data->mode.type) { + case MINIMAP_TYPE_OFF: + break; + case MINIMAP_TYPE_SURFACE: blitMinimapPixelsToImageSurface(map_image, heightmap_image); + break; + case MINIMAP_TYPE_RADAR: + blitMinimapPixelsToImageRadar(map_image); + break; + case MINIMAP_TYPE_TEXTURE: + // Want to use texture source, to : 1 find texture, 2 cache it + video::ITexture* texture = m_tsrc->getTexture(data->mode.texture); + video::IImage* image = driver->createImageFromData( + texture->getColorFormat(), texture->getSize(), texture->lock(), true, false); + texture->unlock(); + + auto dim = image->getDimension(); + + map_image->fill(video::SColor(255, 0, 0, 0)); + + image->copyTo(map_image, + irr::core::vector2d { + ((data->mode.map_size - (static_cast(dim.Width))) >> 1) + - data->pos.X / data->mode.scale, + ((data->mode.map_size - (static_cast(dim.Height))) >> 1) + + data->pos.Z / data->mode.scale + }); + image->drop(); + } map_image->copyToScaling(minimap_image); map_image->drop(); @@ -461,21 +570,31 @@ scene::SMeshBuffer *Minimap::getMinimapMeshBuffer() void Minimap::drawMinimap() { + // Non hud managed minimap drawing (legacy minimap) + v2u32 screensize = RenderingEngine::get_instance()->getWindowSize(); + const u32 size = 0.25 * screensize.Y; + + drawMinimap(core::rect( + screensize.X - size - 10, 10, + screensize.X - 10, size + 10)); +} + +void Minimap::drawMinimap(core::rect rect) { + video::ITexture *minimap_texture = getMinimapTexture(); if (!minimap_texture) return; + if (data->mode.type == MINIMAP_TYPE_OFF) + return; + updateActiveMarkers(); - v2u32 screensize = RenderingEngine::get_instance()->getWindowSize(); - const u32 size = 0.25 * screensize.Y; core::rect oldViewPort = driver->getViewPort(); core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION); core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW); - driver->setViewPort(core::rect( - screensize.X - size - 10, 10, - screensize.X - 10, size + 10)); + driver->setViewPort(rect); driver->setTransform(video::ETS_PROJECTION, core::matrix4()); driver->setTransform(video::ETS_VIEW, core::matrix4()); @@ -488,7 +607,7 @@ void Minimap::drawMinimap() material.TextureLayer[0].Texture = minimap_texture; material.TextureLayer[1].Texture = data->heightmap_texture; - if (m_enable_shaders && !data->is_radar) { + if (m_enable_shaders && data->mode.type == MINIMAP_TYPE_SURFACE) { u16 sid = m_shdrsrc->getShader("minimap_shader", 1, 1); material.MaterialType = m_shdrsrc->getShaderInfo(sid).material; } else { @@ -529,14 +648,14 @@ void Minimap::drawMinimap() driver->setViewPort(oldViewPort); // Draw player markers - v2s32 s_pos(screensize.X - size - 10, 10); + v2s32 s_pos(rect.UpperLeftCorner.X, rect.UpperLeftCorner.Y); core::dimension2di imgsize(data->object_marker_red->getOriginalSize()); core::rect img_rect(0, 0, imgsize.Width, imgsize.Height); static const video::SColor col(255, 255, 255, 255); static const video::SColor c[4] = {col, col, col, col}; f32 sin_angle = std::sin(m_angle * core::DEGTORAD); f32 cos_angle = std::cos(m_angle * core::DEGTORAD); - s32 marker_size2 = 0.025 * (float)size; + s32 marker_size2 = 0.025 * (float)rect.getWidth();; for (std::list::const_iterator i = m_active_markers.begin(); i != m_active_markers.end(); ++i) { @@ -547,8 +666,8 @@ void Minimap::drawMinimap() posf.X = t1; posf.Y = t2; } - posf.X = (posf.X + 0.5) * (float)size; - posf.Y = (posf.Y + 0.5) * (float)size; + posf.X = (posf.X + 0.5) * (float)rect.getWidth(); + posf.Y = (posf.Y + 0.5) * (float)rect.getHeight(); core::rect dest_rect( s_pos.X + posf.X - marker_size2, s_pos.Y + posf.Y - marker_size2, @@ -571,16 +690,16 @@ void Minimap::updateActiveMarkers() for (Nametag *nametag : nametags) { v3s16 pos = floatToInt(nametag->parent_node->getAbsolutePosition() + intToFloat(client->getCamera()->getOffset(), BS), BS); - pos -= data->pos - v3s16(data->map_size / 2, - data->scan_height / 2, - data->map_size / 2); - if (pos.X < 0 || pos.X > data->map_size || - pos.Y < 0 || pos.Y > data->scan_height || - pos.Z < 0 || pos.Z > data->map_size) { + pos -= data->pos - v3s16(data->mode.map_size / 2, + data->mode.scan_height / 2, + data->mode.map_size / 2); + if (pos.X < 0 || pos.X > data->mode.map_size || + pos.Y < 0 || pos.Y > data->mode.scan_height || + pos.Z < 0 || pos.Z > data->mode.map_size) { continue; } - pos.X = ((float)pos.X / data->map_size) * MINIMAP_MAX_SX; - pos.Z = ((float)pos.Z / data->map_size) * MINIMAP_MAX_SY; + pos.X = ((float)pos.X / data->mode.map_size) * MINIMAP_MAX_SX; + pos.Z = ((float)pos.Z / data->mode.map_size) * MINIMAP_MAX_SY; const video::SColor &mask_col = minimap_mask->getPixel(pos.X, pos.Z); if (!mask_col.getAlpha()) { continue; diff --git a/src/client/minimap.h b/src/client/minimap.h index 258d5330d..11374b116 100644 --- a/src/client/minimap.h +++ b/src/client/minimap.h @@ -19,6 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once +#include "../hud.h" #include "irrlichttypes_extrabloated.h" #include "util/thread.h" #include "voxel.h" @@ -33,26 +34,18 @@ class IShaderSource; #define MINIMAP_MAX_SX 512 #define MINIMAP_MAX_SY 512 -enum MinimapMode { - MINIMAP_MODE_OFF, - MINIMAP_MODE_SURFACEx1, - MINIMAP_MODE_SURFACEx2, - MINIMAP_MODE_SURFACEx4, - MINIMAP_MODE_RADARx1, - MINIMAP_MODE_RADARx2, - MINIMAP_MODE_RADARx4, - MINIMAP_MODE_COUNT, -}; - enum MinimapShape { MINIMAP_SHAPE_SQUARE, MINIMAP_SHAPE_ROUND, }; struct MinimapModeDef { - bool is_radar; + MinimapType type; + std::string label; u16 scan_height; u16 map_size; + std::string texture; + u16 scale; }; struct MinimapPixel { @@ -69,12 +62,9 @@ struct MinimapMapblock { }; struct MinimapData { - bool is_radar; - MinimapMode mode; + MinimapModeDef mode; v3s16 pos; v3s16 old_pos; - u16 scan_height; - u16 map_size; MinimapPixel minimap_scan[MINIMAP_MAX_SX * MINIMAP_MAX_SY]; bool map_invalidated; bool minimap_shape_round; @@ -127,12 +117,22 @@ public: v3s16 getPos() const { return data->pos; } void setAngle(f32 angle); f32 getAngle() const { return m_angle; } - void setMinimapMode(MinimapMode mode); - MinimapMode getMinimapMode() const { return data->mode; } void toggleMinimapShape(); void setMinimapShape(MinimapShape shape); MinimapShape getMinimapShape(); + void clearModes() { m_modes.clear(); }; + void addMode(MinimapModeDef mode); + void addMode(MinimapType type, u16 size = 0, std::string label = "", + std::string texture = "", u16 scale = 1); + + void setModeIndex(size_t index); + size_t getModeIndex() const { return m_current_mode_index; }; + size_t getMaxModeIndex() const { return m_modes.size() - 1; }; + void nextMode(); + + void setModesFromString(std::string modes_string); + MinimapModeDef getModeDef() const { return data->mode; } video::ITexture *getMinimapTexture(); @@ -144,6 +144,7 @@ public: void updateActiveMarkers(); void drawMinimap(); + void drawMinimap(core::rect rect); video::IVideoDriver *driver; Client* client; @@ -153,9 +154,11 @@ private: ITextureSource *m_tsrc; IShaderSource *m_shdrsrc; const NodeDefManager *m_ndef; - MinimapUpdateThread *m_minimap_update_thread; + MinimapUpdateThread *m_minimap_update_thread = nullptr; scene::SMeshBuffer *m_meshbuffer; bool m_enable_shaders; + std::vector m_modes; + size_t m_current_mode_index; u16 m_surface_mode_scan_height; f32 m_angle; std::mutex m_mutex; diff --git a/src/hud.cpp b/src/hud.cpp index 175701342..1791e04df 100644 --- a/src/hud.cpp +++ b/src/hud.cpp @@ -29,6 +29,7 @@ const struct EnumString es_HudElementType[] = {HUD_ELEM_WAYPOINT, "waypoint"}, {HUD_ELEM_IMAGE_WAYPOINT, "image_waypoint"}, {HUD_ELEM_COMPASS, "compass"}, + {HUD_ELEM_MINIMAP, "minimap"}, {0, NULL}, }; diff --git a/src/hud.h b/src/hud.h index 0a6993c1b..a0613ae98 100644 --- a/src/hud.h +++ b/src/hud.h @@ -51,7 +51,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #define HUD_HOTBAR_ITEMCOUNT_DEFAULT 8 #define HUD_HOTBAR_ITEMCOUNT_MAX 32 - #define HOTBAR_IMAGE_SIZE 48 enum HudElementType { @@ -61,7 +60,8 @@ enum HudElementType { HUD_ELEM_INVENTORY = 3, HUD_ELEM_WAYPOINT = 4, HUD_ELEM_IMAGE_WAYPOINT = 5, - HUD_ELEM_COMPASS = 6 + HUD_ELEM_COMPASS = 6, + HUD_ELEM_MINIMAP = 7 }; enum HudElementStat { @@ -108,3 +108,12 @@ extern const EnumString es_HudElementType[]; extern const EnumString es_HudElementStat[]; extern const EnumString es_HudBuiltinElement[]; +// Minimap stuff + +enum MinimapType { + MINIMAP_TYPE_OFF, + MINIMAP_TYPE_SURFACE, + MINIMAP_TYPE_RADAR, + MINIMAP_TYPE_TEXTURE, +}; + diff --git a/src/network/clientopcodes.cpp b/src/network/clientopcodes.cpp index f812a08a1..55cfdd4dc 100644 --- a/src/network/clientopcodes.cpp +++ b/src/network/clientopcodes.cpp @@ -122,6 +122,7 @@ const ToClientCommandHandler toClientCommandTable[TOCLIENT_NUM_MSG_TYPES] = null_command_handler, { "TOCLIENT_SRP_BYTES_S_B", TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_SrpBytesSandB }, // 0x60 { "TOCLIENT_FORMSPEC_PREPEND", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_FormspecPrepend }, // 0x61, + { "TOCLIENT_MINIMAP_MODES", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_MinimapModes }, // 0x62, }; const static ServerCommandFactory null_command_factory = { "TOSERVER_NULL", 0, false }; diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index 5683564a0..65db02300 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -1164,15 +1164,24 @@ void Client::handleCommand_HudSetFlags(NetworkPacket* pkt) m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE); bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE); + // Not so satisying code to keep compatibility with old fixed mode system + // --> + // Hide minimap if it has been disabled by the server if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible) // defers a minimap update, therefore only call it if really // needed, by checking that minimap was visible before - m_minimap->setMinimapMode(MINIMAP_MODE_OFF); - - // Switch to surface mode if radar disabled by server - if (m_minimap && m_minimap_radar_disabled_by_server && was_minimap_radar_visible) - m_minimap->setMinimapMode(MINIMAP_MODE_SURFACEx1); + m_minimap->setModeIndex(0); + + // If radar has been disabled, try to find a non radar mode or fall back to 0 + if (m_minimap && m_minimap_radar_disabled_by_server + && was_minimap_radar_visible) { + while (m_minimap->getModeIndex() > 0 && + m_minimap->getModeDef().type == MINIMAP_TYPE_RADAR) + m_minimap->nextMode(); + } + // <-- + // End of 'not so satifying code' } void Client::handleCommand_HudSetParam(NetworkPacket* pkt) @@ -1611,3 +1620,30 @@ void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt) if (valid_signal) m_script->on_modchannel_signal(channel, signal); } + +void Client::handleCommand_MinimapModes(NetworkPacket *pkt) +{ + u16 count; // modes + u16 mode; // wanted current mode index after change + + *pkt >> count >> mode; + + if (m_minimap) + m_minimap->clearModes(); + + for (size_t index = 0; index < count; index++) { + u16 type; + std::string label; + u16 size; + std::string texture; + u16 scale; + + *pkt >> type >> label >> size >> texture >> scale; + + if (m_minimap) + m_minimap->addMode(MinimapType(type), size, label, texture, scale); + } + + if (m_minimap) + m_minimap->setModeIndex(mode); +} diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 05600cda9..666e75e45 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -204,6 +204,7 @@ with this program; if not, write to the Free Software Foundation, Inc., PROTOCOL VERSION 39: Updated set_sky packet Adds new sun, moon and stars packets + Minimap modes */ #define LATEST_PROTOCOL_VERSION 39 @@ -764,7 +765,18 @@ enum ToClientCommand u8[len] formspec */ - TOCLIENT_NUM_MSG_TYPES = 0x62, + TOCLIENT_MINIMAP_MODES = 0x62, + /* + u16 count // modes + u16 mode // wanted current mode index after change + for each mode + u16 type + std::string label + u16 size + std::string extra + */ + + TOCLIENT_NUM_MSG_TYPES = 0x63, }; enum ToServerCommand diff --git a/src/network/serveropcodes.cpp b/src/network/serveropcodes.cpp index 2fc3197c2..aea5d7174 100644 --- a/src/network/serveropcodes.cpp +++ b/src/network/serveropcodes.cpp @@ -221,4 +221,5 @@ const ClientCommandFactory clientCommandFactoryTable[TOCLIENT_NUM_MSG_TYPES] = null_command_factory, // 0x5f { "TOSERVER_SRP_BYTES_S_B", 0, true }, // 0x60 { "TOCLIENT_FORMSPEC_PREPEND", 0, true }, // 0x61 + { "TOCLIENT_MINIMAP_MODES", 0, true }, // 0x62 }; diff --git a/src/script/lua_api/l_minimap.cpp b/src/script/lua_api/l_minimap.cpp index 5fba76eb8..3bbb6e5e3 100644 --- a/src/script/lua_api/l_minimap.cpp +++ b/src/script/lua_api/l_minimap.cpp @@ -89,7 +89,7 @@ int LuaMinimap::l_get_mode(lua_State *L) LuaMinimap *ref = checkobject(L, 1); Minimap *m = getobject(ref); - lua_pushinteger(L, m->getMinimapMode()); + lua_pushinteger(L, m->getModeIndex()); return 1; } @@ -98,13 +98,11 @@ int LuaMinimap::l_set_mode(lua_State *L) LuaMinimap *ref = checkobject(L, 1); Minimap *m = getobject(ref); - s32 mode = lua_tointeger(L, 2); - if (mode < MINIMAP_MODE_OFF || - mode >= MINIMAP_MODE_COUNT) { + u32 mode = lua_tointeger(L, 2); + if (mode >= m->getMaxModeIndex()) return 0; - } - m->setMinimapMode((MinimapMode) mode); + m->setModeIndex(mode); return 1; } @@ -140,8 +138,11 @@ int LuaMinimap::l_show(lua_State *L) LuaMinimap *ref = checkobject(L, 1); Minimap *m = getobject(ref); - if (m->getMinimapMode() == MINIMAP_MODE_OFF) - m->setMinimapMode(MINIMAP_MODE_SURFACEx1); + // This is not very adapted to new minimap mode management. Btw, tried + // to do something compatible. + + if (m->getModeIndex() == 0 && m->getMaxModeIndex() > 0) + m->setModeIndex(1); client->showMinimap(true); return 1; @@ -155,8 +156,11 @@ int LuaMinimap::l_hide(lua_State *L) LuaMinimap *ref = checkobject(L, 1); Minimap *m = getobject(ref); - if (m->getMinimapMode() != MINIMAP_MODE_OFF) - m->setMinimapMode(MINIMAP_MODE_OFF); + // This is not very adapted to new minimap mode management. Btw, tried + // to do something compatible. + + if (m->getModeIndex() != 0) + m->setModeIndex(0); client->showMinimap(false); return 1; diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index fead4e849..57ed7e2cd 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -2199,6 +2199,67 @@ int ObjectRef::l_get_day_night_ratio(lua_State *L) return 1; } +// set_minimap_modes(self, modes, modeindex) +int ObjectRef::l_set_minimap_modes(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + // Arg 1 should be a player + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == NULL) + return 0; + + // Arg 2 should be a table (of tables) + if (!lua_istable(L, 2)) { + return 0; + } + + // Arg 3 should be a number + s16 wantedmode = lua_tonumber(L, 3); + + std::vector modes; + + lua_pushnil(L); + while (lua_next(L, 2) != 0) { + /* key is at index -2, value is at index -1 */ + if (lua_istable(L, -1)) { + bool ok = true; + MinimapMode mode; + std::string type = getstringfield_default(L, -1, "type", ""); + if (type == "off") + mode.type = MINIMAP_TYPE_OFF; + else if (type == "surface") + mode.type = MINIMAP_TYPE_SURFACE; + else if (type == "radar") + mode.type = MINIMAP_TYPE_RADAR; + else if (type == "texture") { + mode.type = MINIMAP_TYPE_TEXTURE; + mode.texture = getstringfield_default(L, -1, "texture", ""); + mode.scale = getintfield_default(L, -1, "scale", 1); + } else { + warningstream << "Minimap mode of unknown type \"" << type.c_str() + << "\" ignored.\n" << std::endl; + ok = false; + } + + if (ok) { + mode.label = getstringfield_default(L, -1, "label", ""); + // Size is limited to 512. Performance gets poor if size too large, and + // segfaults have been experienced. + mode.size = rangelim(getintfield_default(L, -1, "size", 0), 1, 512); + modes.push_back(mode); + } + } + /* removes 'value'; keeps 'key' for next iteration */ + lua_pop(L, 1); + } + lua_pop(L, 1); // Remove key + + getServer(L)->SendMinimapModes(player->getPeerId(), modes, wantedmode); + return 0; +} + ObjectRef::ObjectRef(ServerActiveObject *object): m_object(object) { @@ -2359,5 +2420,6 @@ luaL_Reg ObjectRef::methods[] = { luamethod(ObjectRef, set_eye_offset), luamethod(ObjectRef, get_eye_offset), luamethod(ObjectRef, send_mapblock), + luamethod(ObjectRef, set_minimap_modes), {0,0} }; diff --git a/src/script/lua_api/l_object.h b/src/script/lua_api/l_object.h index 126719b1f..ca03dfa2e 100644 --- a/src/script/lua_api/l_object.h +++ b/src/script/lua_api/l_object.h @@ -377,4 +377,7 @@ private: // send_mapblock(pos) static int l_send_mapblock(lua_State *L); + + // set_minimap_modes(self, modes, wanted_mode) + static int l_set_minimap_modes(lua_State *L); }; diff --git a/src/server.cpp b/src/server.cpp index 982f904f4..8f6257afe 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -2662,6 +2662,23 @@ void Server::sendRequestedMedia(session_t peer_id, } } +void Server::SendMinimapModes(session_t peer_id, + std::vector &modes, size_t wanted_mode) +{ + RemotePlayer *player = m_env->getPlayer(peer_id); + assert(player); + if (player->getPeerId() == PEER_ID_INEXISTENT) + return; + + NetworkPacket pkt(TOCLIENT_MINIMAP_MODES, 0, peer_id); + pkt << (u16)modes.size() << (u16)wanted_mode; + + for (auto &mode : modes) + pkt << (u16)mode.type << mode.label << mode.size << mode.texture << mode.scale; + + Send(&pkt); +} + void Server::sendDetachedInventory(Inventory *inventory, const std::string &name, session_t peer_id) { NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id); diff --git a/src/server.h b/src/server.h index 258d75eaf..4b3ac5cf7 100644 --- a/src/server.h +++ b/src/server.h @@ -118,6 +118,14 @@ struct ServerPlayingSound std::unordered_set clients; // peer ids }; +struct MinimapMode { + MinimapType type = MINIMAP_TYPE_OFF; + std::string label; + u16 size = 0; + std::string texture; + u16 scale = 1; +}; + class Server : public con::PeerHandler, public MapEventReceiver, public IGameDef { @@ -331,6 +339,10 @@ public: void SendPlayerSpeed(session_t peer_id, const v3f &added_vel); void SendPlayerFov(session_t peer_id); + void SendMinimapModes(session_t peer_id, + std::vector &modes, + size_t wanted_mode); + void sendDetachedInventories(session_t peer_id, bool incremental); virtual bool registerModStorage(ModMetadata *storage); -- cgit v1.2.3 From 2f4037752b023f87ca1f8859a8dce4f833353967 Mon Sep 17 00:00:00 2001 From: HybridDog <3192173+HybridDog@users.noreply.github.com> Date: Tue, 6 Oct 2020 20:49:46 +0200 Subject: 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 --- builtin/game/misc.lua | 6 ++ doc/lua_api.txt | 16 ++++ games/devtest/mods/testtools/init.lua | 2 + games/devtest/mods/testtools/light.lua | 22 ++++++ .../testtools/textures/testtools_lighttool.png | Bin 0 -> 1659 bytes src/script/lua_api/l_env.cpp | 41 ++++++++++ src/script/lua_api/l_env.h | 5 ++ src/serverenvironment.cpp | 85 +++++++++++++++++++++ src/serverenvironment.h | 3 + 9 files changed, 180 insertions(+) create mode 100644 games/devtest/mods/testtools/light.lua create mode 100644 games/devtest/mods/testtools/textures/testtools_lighttool.png (limited to 'doc') 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 Binary files /dev/null and b/games/devtest/mods/testtools/textures/testtools_lighttool.png 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; + stack.push({pos, 0}); + + std::unordered_map 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 &objects, const v3f &pos, float radius, std::function include_obj_cb) -- cgit v1.2.3 From f0b6f7909a6a760d946209891c3667799874b80a Mon Sep 17 00:00:00 2001 From: HybridDog <3192173+HybridDog@users.noreply.github.com> Date: Thu, 8 Oct 2020 19:44:27 +0200 Subject: Mention texture modifier colorspace (#10112) --- doc/lua_api.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'doc') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index c4eb56374..60fea3817 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -434,7 +434,9 @@ Texture modifiers ----------------- There are various texture modifiers that can be used -to generate textures on-the-fly. +to let the client generate textures on-the-fly. +The modifiers are applied directly in sRGB colorspace, +i.e. without gamma-correction. ### Texture overlaying -- cgit v1.2.3 From f3ae45b2b245dd0aeb4a7d9b76afaf078871104c Mon Sep 17 00:00:00 2001 From: DS Date: Fri, 9 Oct 2020 20:11:21 +0200 Subject: Add a short_description to be used by mods (#8980) --- builtin/game/register.lua | 6 ++++ doc/lua_api.txt | 10 ++++++ games/devtest/mods/unittests/init.lua | 2 ++ games/devtest/mods/unittests/itemdescription.lua | 44 ++++++++++++++++++++++++ src/inventory.cpp | 14 ++++++++ src/inventory.h | 1 + src/itemdef.cpp | 9 +++-- src/itemdef.h | 1 + src/script/common/c_content.cpp | 3 ++ src/script/lua_api/l_item.cpp | 11 ++++++ src/script/lua_api/l_item.h | 3 ++ 11 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 games/devtest/mods/unittests/itemdescription.lua (limited to 'doc') diff --git a/builtin/game/register.lua b/builtin/game/register.lua index 3de67c04b..f00b76494 100644 --- a/builtin/game/register.lua +++ b/builtin/game/register.lua @@ -118,6 +118,12 @@ function core.register_item(name, itemdef) end itemdef.name = name + -- default description to item name + itemdef.description = itemdef.description or name + -- default short_description to first line of description + itemdef.short_description = itemdef.short_description or + itemdef.description:gsub("\n.*","") + -- Apply defaults and add to registered_* table if itemdef.type == "node" then -- Use the nodebox as selection box if it's not set manually diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 60fea3817..c8b294149 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2054,6 +2054,8 @@ Some of the values in the key-value store are handled specially: * `description`: Set the item stack's description. Defaults to `idef.description`. +* `short_description`: Set the item stack's short description. Defaults + to `idef.short_description`. * `color`: A `ColorString`, which sets the stack's color. * `palette_index`: If the item has a palette, this is used to get the current color from the palette. @@ -5994,6 +5996,8 @@ an itemstring, a table or `nil`. stack). * `set_metadata(metadata)`: (DEPRECATED) Returns true. * `get_description()`: returns the description shown in inventory list tooltips. +* `get_short_description()`: returns the short description. + * Unlike the description, this does not include new lines. * `clear()`: removes all items from the stack, making it empty. * `replace(item)`: replace the contents of this stack. * `item` can also be an itemstring or table. @@ -7096,6 +7100,12 @@ Used by `minetest.register_node`, `minetest.register_craftitem`, and { description = "Steel Axe", + -- Can contain new lines. "\n" has to be used as new line character. + -- Defaults to the item's name. + + short_description = "Steel Axe", + -- Must not contain new lines. + -- Defaults to the first line of description. groups = {}, -- key = name, value = rating; rating = 1..3. diff --git a/games/devtest/mods/unittests/init.lua b/games/devtest/mods/unittests/init.lua index 6c1728420..12c67f78b 100644 --- a/games/devtest/mods/unittests/init.lua +++ b/games/devtest/mods/unittests/init.lua @@ -5,10 +5,12 @@ dofile(modpath .. "/random.lua") dofile(modpath .. "/player.lua") dofile(modpath .. "/crafting_prepare.lua") dofile(modpath .. "/crafting.lua") +dofile(modpath .. "/itemdescription.lua") if minetest.settings:get_bool("devtest_unittests_autostart", false) then unittests.test_random() unittests.test_crafting() + unittests.test_short_desc() minetest.register_on_joinplayer(function(player) unittests.test_player(player) end) diff --git a/games/devtest/mods/unittests/itemdescription.lua b/games/devtest/mods/unittests/itemdescription.lua new file mode 100644 index 000000000..1d0826545 --- /dev/null +++ b/games/devtest/mods/unittests/itemdescription.lua @@ -0,0 +1,44 @@ +local full_description = "Colorful Pickaxe\nThe best pick." +minetest.register_tool("unittests:colorful_pick", { + description = full_description, + inventory_image = "basetools_mesepick.png", + tool_capabilities = { + full_punch_interval = 1.0, + max_drop_level=3, + groupcaps={ + cracky={times={[1]=2.0, [2]=1.0, [3]=0.5}, uses=20, maxlevel=3}, + crumbly={times={[1]=2.0, [2]=1.0, [3]=0.5}, uses=20, maxlevel=3}, + snappy={times={[1]=2.0, [2]=1.0, [3]=0.5}, uses=20, maxlevel=3} + }, + damage_groups = {fleshy=4}, + }, +}) + +minetest.register_chatcommand("item_description", { + param = "", + description = "Show the short and full description of the wielded item.", + func = function(name) + local player = minetest.get_player_by_name(name) + local item = player:get_wielded_item() + return true, string.format("short_description: %s\ndescription: %s", + item:get_short_description(), item:get_description()) + end +}) + +function unittests.test_short_desc() + local stack = ItemStack("unittests:colorful_pick") + assert(stack:get_short_description() == "Colorful Pickaxe") + assert(stack:get_short_description() == minetest.registered_items["unittests:colorful_pick"].short_description) + assert(stack:get_description() == full_description) + assert(stack:get_description() == minetest.registered_items["unittests:colorful_pick"].description) + + stack:get_meta():set_string("description", "Hello World") + assert(stack:get_short_description() == "Colorful Pickaxe") + assert(stack:get_description() == "Hello World") + + stack:get_meta():set_string("short_description", "Foo Bar") + assert(stack:get_short_description() == "Foo Bar") + assert(stack:get_description() == "Hello World") + + return true +end diff --git a/src/inventory.cpp b/src/inventory.cpp index cf72cb005..1ef9b13cd 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -258,6 +258,20 @@ std::string ItemStack::getDescription(IItemDefManager *itemdef) const return desc.empty() ? name : desc; } +std::string ItemStack::getShortDescription(IItemDefManager *itemdef) const +{ + std::string desc = metadata.getString("short_description"); + if (desc.empty()) + desc = getDefinition(itemdef).short_description; + if (!desc.empty()) + return desc; + // no short_description because of old server version or modified builtin + // return first line of description + std::stringstream sstr(getDescription(itemdef)); + std::getline(sstr, desc, '\n'); + return desc; +} + ItemStack ItemStack::addItem(ItemStack newitem, IItemDefManager *itemdef) { diff --git a/src/inventory.h b/src/inventory.h index 67a7859ed..f36bc57cf 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -49,6 +49,7 @@ struct ItemStack std::string getItemString(bool include_meta = true) const; // Returns the tooltip std::string getDescription(IItemDefManager *itemdef) const; + std::string getShortDescription(IItemDefManager *itemdef) const; /* Quantity methods diff --git a/src/itemdef.cpp b/src/itemdef.cpp index df20bdf15..5fb1e4c47 100644 --- a/src/itemdef.cpp +++ b/src/itemdef.cpp @@ -62,6 +62,7 @@ ItemDefinition& ItemDefinition::operator=(const ItemDefinition &def) type = def.type; name = def.name; description = def.description; + short_description = def.short_description; inventory_image = def.inventory_image; inventory_overlay = def.inventory_overlay; wield_image = def.wield_image; @@ -102,6 +103,7 @@ void ItemDefinition::reset() type = ITEM_NONE; name = ""; description = ""; + short_description = ""; inventory_image = ""; inventory_overlay = ""; wield_image = ""; @@ -162,6 +164,8 @@ void ItemDefinition::serialize(std::ostream &os, u16 protocol_version) const writeARGB8(os, color); os << serializeString16(inventory_overlay); os << serializeString16(wield_overlay); + + os << serializeString16(short_description); } void ItemDefinition::deSerialize(std::istream &is) @@ -213,8 +217,9 @@ void ItemDefinition::deSerialize(std::istream &is) // If you add anything here, insert it primarily inside the try-catch // block to not need to increase the version. - //try { - //} catch(SerializationError &e) {}; + try { + short_description = deSerializeString16(is); + } catch(SerializationError &e) {}; } diff --git a/src/itemdef.h b/src/itemdef.h index f47e6cbe7..ebf0d3527 100644 --- a/src/itemdef.h +++ b/src/itemdef.h @@ -56,6 +56,7 @@ struct ItemDefinition ItemType type; std::string name; // "" = hand std::string description; // Shown in tooltip. + std::string short_description; /* Visual properties diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 774b6a326..147f6e3ed 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -56,6 +56,7 @@ void read_item_definition(lua_State* L, int index, es_ItemType, ITEM_NONE); getstringfield(L, index, "name", def.name); getstringfield(L, index, "description", def.description); + getstringfield(L, index, "short_description", def.short_description); getstringfield(L, index, "inventory_image", def.inventory_image); getstringfield(L, index, "inventory_overlay", def.inventory_overlay); getstringfield(L, index, "wield_image", def.wield_image); @@ -142,6 +143,8 @@ void push_item_definition_full(lua_State *L, const ItemDefinition &i) lua_setfield(L, -2, "name"); lua_pushstring(L, i.description.c_str()); lua_setfield(L, -2, "description"); + lua_pushstring(L, i.short_description.c_str()); + lua_setfield(L, -2, "short_description"); lua_pushstring(L, type.c_str()); lua_setfield(L, -2, "type"); lua_pushstring(L, i.inventory_image.c_str()); diff --git a/src/script/lua_api/l_item.cpp b/src/script/lua_api/l_item.cpp index d67cab76f..2d1124a4d 100644 --- a/src/script/lua_api/l_item.cpp +++ b/src/script/lua_api/l_item.cpp @@ -193,6 +193,16 @@ int LuaItemStack::l_get_description(lua_State *L) return 1; } +// get_short_description(self) +int LuaItemStack::l_get_short_description(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + LuaItemStack *o = checkobject(L, 1); + std::string desc = o->m_stack.getShortDescription(getGameDef(L)->idef()); + lua_pushstring(L, desc.c_str()); + return 1; +} + // clear(self) -> true int LuaItemStack::l_clear(lua_State *L) { @@ -493,6 +503,7 @@ const luaL_Reg LuaItemStack::methods[] = { luamethod(LuaItemStack, get_metadata), luamethod(LuaItemStack, set_metadata), luamethod(LuaItemStack, get_description), + luamethod(LuaItemStack, get_short_description), luamethod(LuaItemStack, clear), luamethod(LuaItemStack, replace), luamethod(LuaItemStack, to_string), diff --git a/src/script/lua_api/l_item.h b/src/script/lua_api/l_item.h index 98744c071..16878c101 100644 --- a/src/script/lua_api/l_item.h +++ b/src/script/lua_api/l_item.h @@ -72,6 +72,9 @@ private: // get_description(self) static int l_get_description(lua_State *L); + // get_short_description(self) + static int l_get_short_description(lua_State *L); + // clear(self) -> true static int l_clear(lua_State *L); -- cgit v1.2.3 From 2341a4aff1242e978d6fad3772d4d4fb015c040d Mon Sep 17 00:00:00 2001 From: Zughy <63455151+Zughy@users.noreply.github.com> Date: Tue, 13 Oct 2020 21:27:52 +0200 Subject: Add ObjectRef:get_children() (#10480) Co-authored-by: Zughy <4279489-marco_a@users.noreply.gitlab.com> --- doc/lua_api.txt | 2 ++ src/script/lua_api/l_object.cpp | 22 ++++++++++++++++++++++ src/script/lua_api/l_object.h | 3 +++ 3 files changed, 27 insertions(+) (limited to 'doc') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index c8b294149..1631d564c 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -6206,6 +6206,8 @@ object you are working with still exists. should appear in first person. * `get_attach()`: returns parent, bone, position, rotation, forced_visible, or nil if it isn't attached. +* `get_children()`: returns a list of ObjectRefs that are attached to the + object. * `set_detach()` * `set_bone_position(bone, position, rotation)` * `bone`: string diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 57ed7e2cd..96c8c687c 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -738,6 +738,27 @@ int ObjectRef::l_get_attach(lua_State *L) return 5; } +// get_children(self) +int ObjectRef::l_get_children(lua_State *L) +{ + GET_ENV_PTR; + + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + + const std::unordered_set child_ids = sao->getAttachmentChildIds(); + int i = 0; + lua_createtable(L, child_ids.size(), 0); + for (const int id : child_ids) { + ServerActiveObject *child = env->getActiveObject(id); + getScriptApiBase(L)->objectrefGetOrCreate(L, child); + lua_rawseti(L, -2, ++i); + } + return 1; +} + // set_detach(self) int ObjectRef::l_set_detach(lua_State *L) { @@ -2337,6 +2358,7 @@ luaL_Reg ObjectRef::methods[] = { luamethod(ObjectRef, get_bone_position), luamethod(ObjectRef, set_attach), luamethod(ObjectRef, get_attach), + luamethod(ObjectRef, get_children), luamethod(ObjectRef, set_detach), luamethod(ObjectRef, set_properties), luamethod(ObjectRef, get_properties), diff --git a/src/script/lua_api/l_object.h b/src/script/lua_api/l_object.h index ca03dfa2e..097a74cae 100644 --- a/src/script/lua_api/l_object.h +++ b/src/script/lua_api/l_object.h @@ -143,6 +143,9 @@ private: // get_attach(self) static int l_get_attach(lua_State *L); + // get_children(self) + static int l_get_children(lua_State *L); + // set_detach(self) static int l_set_detach(lua_State *L); -- cgit v1.2.3 From ed22260822086f84016aa8384c3174bfc6d1739d Mon Sep 17 00:00:00 2001 From: Lars Date: Mon, 12 Oct 2020 13:29:31 -0700 Subject: Remove all bump mapping and parallax occlusion related code. --- builtin/mainmenu/tab_settings.lua | 30 ++----- builtin/settingtypes.txt | 25 ------ client/shaders/nodes_shader/opengl_fragment.glsl | 99 ----------------------- client/shaders/nodes_shader/opengl_vertex.glsl | 45 +---------- client/shaders/object_shader/opengl_fragment.glsl | 54 ------------- client/shaders/object_shader/opengl_vertex.glsl | 5 -- doc/lua_api.txt | 40 +-------- src/client/game.cpp | 10 +-- src/client/mapblock_mesh.cpp | 41 ++-------- src/client/mapblock_mesh.h | 5 +- src/client/mesh_generator_thread.cpp | 6 +- src/client/mesh_generator_thread.h | 1 - src/client/shader.cpp | 29 ------- src/client/wieldmesh.cpp | 2 +- src/defaultsettings.cpp | 6 -- src/nodedef.cpp | 9 --- src/nodedef.h | 1 - 17 files changed, 24 insertions(+), 384 deletions(-) (limited to 'doc') diff --git a/builtin/mainmenu/tab_settings.lua b/builtin/mainmenu/tab_settings.lua index 510346f8d..8a7445394 100644 --- a/builtin/mainmenu/tab_settings.lua +++ b/builtin/mainmenu/tab_settings.lua @@ -187,31 +187,23 @@ local function formspec(tabview, name, tabdata) if shaders_enabled then tab_string = tab_string .. - "checkbox[8.25,0.5;cb_bumpmapping;" .. fgettext("Bump Mapping") .. ";" - .. dump(core.settings:get_bool("enable_bumpmapping")) .. "]" .. - "checkbox[8.25,1;cb_tonemapping;" .. fgettext("Tone Mapping") .. ";" + "checkbox[8.25,0.5;cb_tonemapping;" .. fgettext("Tone Mapping") .. ";" .. dump(core.settings:get_bool("tone_mapping")) .. "]" .. - "checkbox[8.25,1.5;cb_parallax;" .. fgettext("Parallax Occlusion") .. ";" - .. dump(core.settings:get_bool("enable_parallax_occlusion")) .. "]" .. - "checkbox[8.25,2;cb_waving_water;" .. fgettext("Waving Liquids") .. ";" + "checkbox[8.25,1;cb_waving_water;" .. fgettext("Waving Liquids") .. ";" .. dump(core.settings:get_bool("enable_waving_water")) .. "]" .. - "checkbox[8.25,2.5;cb_waving_leaves;" .. fgettext("Waving Leaves") .. ";" + "checkbox[8.25,1.5;cb_waving_leaves;" .. fgettext("Waving Leaves") .. ";" .. dump(core.settings:get_bool("enable_waving_leaves")) .. "]" .. - "checkbox[8.25,3;cb_waving_plants;" .. fgettext("Waving Plants") .. ";" + "checkbox[8.25,2;cb_waving_plants;" .. fgettext("Waving Plants") .. ";" .. dump(core.settings:get_bool("enable_waving_plants")) .. "]" else tab_string = tab_string .. "label[8.38,0.7;" .. core.colorize("#888888", - fgettext("Bump Mapping")) .. "]" .. - "label[8.38,1.2;" .. core.colorize("#888888", fgettext("Tone Mapping")) .. "]" .. - "label[8.38,1.7;" .. core.colorize("#888888", - fgettext("Parallax Occlusion")) .. "]" .. - "label[8.38,2.2;" .. core.colorize("#888888", + "label[8.38,1.2;" .. core.colorize("#888888", fgettext("Waving Liquids")) .. "]" .. - "label[8.38,2.7;" .. core.colorize("#888888", + "label[8.38,1.7;" .. core.colorize("#888888", fgettext("Waving Leaves")) .. "]" .. - "label[8.38,3.2;" .. core.colorize("#888888", + "label[8.38,2.2;" .. core.colorize("#888888", fgettext("Waving Plants")) .. "]" end @@ -263,18 +255,10 @@ local function handle_settings_buttons(this, fields, tabname, tabdata) end return true end - if fields["cb_bumpmapping"] then - core.settings:set("enable_bumpmapping", fields["cb_bumpmapping"]) - return true - end if fields["cb_tonemapping"] then core.settings:set("tone_mapping", fields["cb_tonemapping"]) return true end - if fields["cb_parallax"] then - core.settings:set("enable_parallax_occlusion", fields["cb_parallax"]) - return true - end if fields["cb_waving_water"] then core.settings:set("enable_waving_water", fields["cb_waving_water"]) return true diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 1c28470d5..27f375693 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -546,31 +546,6 @@ shader_path (Shader path) path # enhanced, highlights and shadows are gradually compressed. tone_mapping (Filmic tone mapping) bool false -[***Bumpmapping] - -# Enables bumpmapping for textures. Normalmaps need to be supplied by the texture pack. -# Requires shaders to be enabled. -enable_bumpmapping (Bumpmapping) bool false - -[***Parallax Occlusion] - -# Enables parallax occlusion mapping. -# Requires shaders to be enabled. -enable_parallax_occlusion (Parallax occlusion) bool false - -# 0 = parallax occlusion with slope information (faster). -# 1 = relief mapping (slower, more accurate). -parallax_occlusion_mode (Parallax occlusion mode) int 1 0 1 - -# Number of parallax occlusion iterations. -parallax_occlusion_iterations (Parallax occlusion iterations) int 4 - -# Overall scale of parallax occlusion effect. -parallax_occlusion_scale (Parallax occlusion scale) float 0.08 - -# Overall bias of parallax occlusion effect, usually scale/2. -parallax_occlusion_bias (Parallax occlusion bias) float 0.04 - [***Waving Nodes] # Set to true to enable waving liquids (like water). diff --git a/client/shaders/nodes_shader/opengl_fragment.glsl b/client/shaders/nodes_shader/opengl_fragment.glsl index 437b325d3..36d47d1f5 100644 --- a/client/shaders/nodes_shader/opengl_fragment.glsl +++ b/client/shaders/nodes_shader/opengl_fragment.glsl @@ -1,6 +1,4 @@ uniform sampler2D baseTexture; -uniform sampler2D normalTexture; -uniform sampler2D textureFlags; uniform vec4 skyBgColor; uniform float fogDistance; @@ -17,17 +15,9 @@ varying vec3 vPosition; // cameraOffset + worldPosition (for large coordinates the limits of float // precision must be considered). varying vec3 worldPosition; -varying float area_enable_parallax; varying vec3 eyeVec; -varying vec3 tsEyeVec; -varying vec3 lightVec; -varying vec3 tsLightVec; -bool normalTexturePresent = false; - -const float e = 2.718281828459; -const float BS = 10.0; const float fogStart = FOG_START; const float fogShadingParameter = 1 / ( 1 - fogStart); @@ -63,87 +53,10 @@ vec4 applyToneMapping(vec4 color) } #endif -void get_texture_flags() -{ - vec4 flags = texture2D(textureFlags, vec2(0.0, 0.0)); - if (flags.r > 0.5) { - normalTexturePresent = true; - } -} - -vec4 get_normal_map(vec2 uv) -{ - vec4 bump = texture2D(normalTexture, uv).rgba; - bump.xyz = normalize(bump.xyz * 2.0 - 1.0); - return bump; -} - -float find_intersection(vec2 dp, vec2 ds) -{ - float depth = 1.0; - float best_depth = 0.0; - float size = 0.0625; - for (int i = 0; i < 15; i++) { - depth -= size; - float h = texture2D(normalTexture, dp + ds * depth).a; - if (depth <= h) { - best_depth = depth; - break; - } - } - depth = best_depth; - for (int i = 0; i < 4; i++) { - size *= 0.5; - float h = texture2D(normalTexture,dp + ds * depth).a; - if (depth <= h) { - best_depth = depth; - depth += size; - } else { - depth -= size; - } - } - return best_depth; -} - void main(void) { vec3 color; - vec4 bump; vec2 uv = gl_TexCoord[0].st; - bool use_normalmap = false; - get_texture_flags(); - -#ifdef ENABLE_PARALLAX_OCCLUSION - vec2 eyeRay = vec2 (tsEyeVec.x, -tsEyeVec.y); - const float scale = PARALLAX_OCCLUSION_SCALE / PARALLAX_OCCLUSION_ITERATIONS; - const float bias = PARALLAX_OCCLUSION_BIAS / PARALLAX_OCCLUSION_ITERATIONS; - -#if PARALLAX_OCCLUSION_MODE == 0 - // Parallax occlusion with slope information - if (normalTexturePresent && area_enable_parallax > 0.0) { - for (int i = 0; i < PARALLAX_OCCLUSION_ITERATIONS; i++) { - vec4 normal = texture2D(normalTexture, uv.xy); - float h = normal.a * scale - bias; - uv += h * normal.z * eyeRay; - } - } -#endif -#if PARALLAX_OCCLUSION_MODE == 1 - // Relief mapping - if (normalTexturePresent && area_enable_parallax > 0.0) { - vec2 ds = eyeRay * PARALLAX_OCCLUSION_SCALE; - float dist = find_intersection(uv, ds); - uv += dist * ds; - } -#endif -#endif - -#if USE_NORMALMAPS == 1 - if (normalTexturePresent) { - bump = get_normal_map(uv); - use_normalmap = true; - } -#endif vec4 base = texture2D(baseTexture, uv).rgba; @@ -155,19 +68,7 @@ void main(void) } #endif -#ifdef ENABLE_BUMPMAPPING - if (use_normalmap) { - vec3 L = normalize(lightVec); - vec3 E = normalize(eyeVec); - float specular = pow(clamp(dot(reflect(L, bump.xyz), E), 0.0, 1.0), 1.0); - float diffuse = dot(-E,bump.xyz); - color = (diffuse + 0.1 * specular) * base.rgb; - } else { - color = base.rgb; - } -#else color = base.rgb; -#endif vec4 col = vec4(color.rgb * gl_Color.rgb, 1.0); diff --git a/client/shaders/nodes_shader/opengl_vertex.glsl b/client/shaders/nodes_shader/opengl_vertex.glsl index 0d8d0a2a5..56bff09a8 100644 --- a/client/shaders/nodes_shader/opengl_vertex.glsl +++ b/client/shaders/nodes_shader/opengl_vertex.glsl @@ -18,10 +18,6 @@ varying vec3 vPosition; varying vec3 worldPosition; varying vec3 eyeVec; -varying vec3 lightVec; -varying vec3 tsEyeVec; -varying vec3 tsLightVec; -varying float area_enable_parallax; // Color of the light emitted by the light sources. const vec3 artificialLight = vec3(1.04, 1.04, 1.04); @@ -86,21 +82,9 @@ float snoise(vec3 p) void main(void) { gl_TexCoord[0] = gl_MultiTexCoord0; - //TODO: make offset depending on view angle and parallax uv displacement - //thats for textures that doesnt align vertically, like dirt with grass - //gl_TexCoord[0].y += 0.008; - - //Allow parallax/relief mapping only for certain kind of nodes - //Variable is also used to control area of the effect -#if (DRAW_TYPE == NDT_NORMAL || DRAW_TYPE == NDT_LIQUID || DRAW_TYPE == NDT_FLOWINGLIQUID) - area_enable_parallax = 1.0; -#else - area_enable_parallax = 0.0; -#endif - -float disp_x; -float disp_z; + float disp_x; + float disp_z; // OpenGL < 4.3 does not support continued preprocessor lines #if (MATERIAL_TYPE == TILE_MATERIAL_WAVING_LEAVES && ENABLE_WAVING_LEAVES) || (MATERIAL_TYPE == TILE_MATERIAL_WAVING_PLANTS && ENABLE_WAVING_PLANTS) vec4 pos2 = mWorld * gl_Vertex; @@ -148,32 +132,7 @@ float disp_z; vPosition = gl_Position.xyz; - // Don't generate heightmaps when too far from the eye - float dist = distance (vec3(0.0, 0.0, 0.0), vPosition); - if (dist > 150.0) { - area_enable_parallax = 0.0; - } - - vec3 sunPosition = vec3 (0.0, eyePosition.y * BS + 900.0, 0.0); - - vec3 normal, tangent, binormal; - normal = normalize(gl_NormalMatrix * gl_Normal); - tangent = normalize(gl_NormalMatrix * gl_MultiTexCoord1.xyz); - binormal = normalize(gl_NormalMatrix * gl_MultiTexCoord2.xyz); - - vec3 v; - - lightVec = sunPosition - worldPosition; - v.x = dot(lightVec, tangent); - v.y = dot(lightVec, binormal); - v.z = dot(lightVec, normal); - tsLightVec = normalize (v); - eyeVec = -(gl_ModelViewMatrix * gl_Vertex).xyz; - v.x = dot(eyeVec, tangent); - v.y = dot(eyeVec, binormal); - v.z = dot(eyeVec, normal); - tsEyeVec = normalize (v); // Calculate color. // Red, green and blue components are pre-multiplied with diff --git a/client/shaders/object_shader/opengl_fragment.glsl b/client/shaders/object_shader/opengl_fragment.glsl index a8a43e1e2..86d5c1c92 100644 --- a/client/shaders/object_shader/opengl_fragment.glsl +++ b/client/shaders/object_shader/opengl_fragment.glsl @@ -1,6 +1,4 @@ uniform sampler2D baseTexture; -uniform sampler2D normalTexture; -uniform sampler2D textureFlags; uniform vec4 emissiveColor; uniform vec4 skyBgColor; @@ -12,14 +10,8 @@ varying vec3 vPosition; varying vec3 worldPosition; varying vec3 eyeVec; -varying vec3 lightVec; varying float vIDiff; -bool normalTexturePresent = false; -bool texTileableHorizontal = false; -bool texTileableVertical = false; -bool texSeamless = false; - const float e = 2.718281828459; const float BS = 10.0; const float fogStart = FOG_START; @@ -57,44 +49,10 @@ vec4 applyToneMapping(vec4 color) } #endif -void get_texture_flags() -{ - vec4 flags = texture2D(textureFlags, vec2(0.0, 0.0)); - if (flags.r > 0.5) { - normalTexturePresent = true; - } - if (flags.g > 0.5) { - texTileableHorizontal = true; - } - if (flags.b > 0.5) { - texTileableVertical = true; - } - if (texTileableHorizontal && texTileableVertical) { - texSeamless = true; - } -} - -vec4 get_normal_map(vec2 uv) -{ - vec4 bump = texture2D(normalTexture, uv).rgba; - bump.xyz = normalize(bump.xyz * 2.0 - 1.0); - return bump; -} - void main(void) { vec3 color; - vec4 bump; vec2 uv = gl_TexCoord[0].st; - bool use_normalmap = false; - get_texture_flags(); - -#if USE_NORMALMAPS == 1 - if (normalTexturePresent) { - bump = get_normal_map(uv); - use_normalmap = true; - } -#endif vec4 base = texture2D(baseTexture, uv).rgba; @@ -106,19 +64,7 @@ void main(void) } #endif -#ifdef ENABLE_BUMPMAPPING - if (use_normalmap) { - vec3 L = normalize(lightVec); - vec3 E = normalize(eyeVec); - float specular = pow(clamp(dot(reflect(L, bump.xyz), E), 0.0, 1.0), 1.0); - float diffuse = dot(-E,bump.xyz); - color = (diffuse + 0.1 * specular) * base.rgb; - } else { - color = base.rgb; - } -#else color = base.rgb; -#endif vec4 col = vec4(color.rgb, base.a); diff --git a/client/shaders/object_shader/opengl_vertex.glsl b/client/shaders/object_shader/opengl_vertex.glsl index 968a07e22..f8c1cd932 100644 --- a/client/shaders/object_shader/opengl_vertex.glsl +++ b/client/shaders/object_shader/opengl_vertex.glsl @@ -9,7 +9,6 @@ varying vec3 vPosition; varying vec3 worldPosition; varying vec3 eyeVec; -varying vec3 lightVec; varying float vIDiff; const float e = 2.718281828459; @@ -33,10 +32,6 @@ void main(void) vPosition = gl_Position.xyz; vNormal = gl_Normal; worldPosition = (mWorld * gl_Vertex).xyz; - - vec3 sunPosition = vec3 (0.0, eyePosition.y * BS + 900.0, 0.0); - - lightVec = sunPosition - worldPosition; eyeVec = -(gl_ModelViewMatrix * gl_Vertex).xyz; #if (MATERIAL_TYPE == TILE_MATERIAL_PLAIN) || (MATERIAL_TYPE == TILE_MATERIAL_PLAIN_ALPHA) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 1631d564c..d3aaa309c 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -401,35 +401,6 @@ stripping out the file extension: * e.g. `foomod_foothing` -Normalmap Textures ------------------- - -If shaders and bumpmapping or parallax occlusion is enabled, Minetest tries -to load normalmaps. -Those image files have to end with `_normal.png` and start with the same name -as their corresponding texture. -For example a normalmap for `foomod_foothing.png` has to be called -`foomod_foothing_normal.png`. - -The sRGB R, G and B colour values of a normalmap pixel are each directly -mapped from `{0, ..., 255}` to `[-1, 1]` and, taken together, -define the normal vector. -The alpha channel defines the heightmap for parallax occlusion. -To be safe, the alpha values should always be bigger than zero -because the colour values, which define the normal vector, -may be undefined for image formats where colour is discarded in fully -transparent pixels. - -Bumpmapping and parallax occlusion are currently experimental features: - -* Bumpmapping in Minetest happens in an obscure way; there are no light sources - defined in the shaders except the sunlight direction. -* Parallax occlusion with relief-mapping mode does not yet work correctly - together with Minetest's Fastfaces. -* The normalmap files must end with `.png`, so other image files are not - supported. - - Texture modifiers ----------------- @@ -834,7 +805,7 @@ Example (colored grass block): -- Overlay tiles: define them in the same style -- The top and bottom tile does not have overlay overlay_tiles = {"", "", - {name = "default_grass_side.png", tileable_vertical = false}}, + {name = "default_grass_side.png"}}, -- Global color, used in inventory color = "green", -- Palette in the world @@ -1204,7 +1175,7 @@ Look for examples in `games/devtest` or `games/minetest_game`. base cube without affecting them. * The base cube texture tiles are defined as normal, the `plantlike` extension uses the defined special tile, for example: - `special_tiles = {{name = "default_papyrus.png", tileable_vertical = true}},` + `special_tiles = {{name = "default_papyrus.png"}},` `*_optional` drawtypes need less rendering time if deactivated (always client-side). @@ -7043,13 +7014,8 @@ Tile definition * `"image.png"` * `{name="image.png", animation={Tile Animation definition}}` -* `{name="image.png", backface_culling=bool, tileable_vertical=bool, - tileable_horizontal=bool, align_style="node"/"world"/"user", scale=int}` +* `{name="image.png", backface_culling=bool, align_style="node"/"world"/"user", scale=int}` * backface culling enabled by default for most nodes - * tileable flags are info for shaders, how they should treat texture - when displacement mapping is used. - Directions are from the point of view of the tile texture, - not the node it's on. * align style determines whether the texture will be rotated with the node or kept aligned with its surroundings. "user" means that client setting will be used, similar to `glasslike_framed_optional`. diff --git a/src/client/game.cpp b/src/client/game.cpp index 54e0c9f20..309a8e194 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -430,8 +430,6 @@ class GameGlobalShaderConstantSetter : public IShaderConstantSetter CachedPixelShaderSetting m_camera_offset_pixel; CachedPixelShaderSetting m_camera_offset_vertex; CachedPixelShaderSetting m_base_texture; - CachedPixelShaderSetting m_normal_texture; - CachedPixelShaderSetting m_texture_flags; Client *m_client; public: @@ -464,8 +462,6 @@ public: m_camera_offset_pixel("cameraOffset"), m_camera_offset_vertex("cameraOffset"), m_base_texture("baseTexture"), - m_normal_texture("normalTexture"), - m_texture_flags("textureFlags"), m_client(client) { g_settings->registerChangedCallback("enable_fog", settingsCallback, this); @@ -553,12 +549,8 @@ public: m_camera_offset_pixel.set(camera_offset_array, services); m_camera_offset_vertex.set(camera_offset_array, services); - SamplerLayer_t base_tex = 0, - normal_tex = 1, - flags_tex = 2; + SamplerLayer_t base_tex = 0; m_base_texture.set(&base_tex, services); - m_normal_texture.set(&normal_tex, services); - m_texture_flags.set(&flags_tex, services); } }; diff --git a/src/client/mapblock_mesh.cpp b/src/client/mapblock_mesh.cpp index c7790f1e4..6a59fabe3 100644 --- a/src/client/mapblock_mesh.cpp +++ b/src/client/mapblock_mesh.cpp @@ -35,11 +35,9 @@ with this program; if not, write to the Free Software Foundation, Inc., MeshMakeData */ -MeshMakeData::MeshMakeData(Client *client, bool use_shaders, - bool use_tangent_vertices): +MeshMakeData::MeshMakeData(Client *client, bool use_shaders): m_client(client), - m_use_shaders(use_shaders), - m_use_tangent_vertices(use_tangent_vertices) + m_use_shaders(use_shaders) {} void MeshMakeData::fillBlockDataBegin(const v3s16 &blockpos) @@ -1016,7 +1014,6 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset): for (auto &m : m_mesh) m = new scene::SMesh(); m_enable_shaders = data->m_use_shaders; - m_use_tangent_vertices = data->m_use_tangent_vertices; m_enable_vbo = g_settings->getBool("enable_vbo"); if (data->m_client->getMinimap()) { @@ -1170,28 +1167,12 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset): scene::SMesh *mesh = (scene::SMesh *)m_mesh[layer]; - // Create meshbuffer, add to mesh - if (m_use_tangent_vertices) { - scene::SMeshBufferTangents *buf = - new scene::SMeshBufferTangents(); - buf->Material = material; - buf->Vertices.reallocate(p.vertices.size()); - buf->Indices.reallocate(p.indices.size()); - for (const video::S3DVertex &v: p.vertices) - buf->Vertices.push_back(video::S3DVertexTangents(v.Pos, v.Color, v.TCoords)); - for (u16 i: p.indices) - buf->Indices.push_back(i); - buf->recalculateBoundingBox(); - mesh->addMeshBuffer(buf); - buf->drop(); - } else { - scene::SMeshBuffer *buf = new scene::SMeshBuffer(); - buf->Material = material; - buf->append(&p.vertices[0], p.vertices.size(), - &p.indices[0], p.indices.size()); - mesh->addMeshBuffer(buf); - buf->drop(); - } + scene::SMeshBuffer *buf = new scene::SMeshBuffer(); + buf->Material = material; + buf->append(&p.vertices[0], p.vertices.size(), + &p.indices[0], p.indices.size()); + mesh->addMeshBuffer(buf); + buf->drop(); } /* @@ -1201,12 +1182,6 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset): translateMesh(m_mesh[layer], intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS)); - if (m_use_tangent_vertices) { - scene::IMeshManipulator* meshmanip = - RenderingEngine::get_scene_manager()->getMeshManipulator(); - meshmanip->recalculateTangents(m_mesh[layer], true, false, false); - } - if (m_mesh[layer]) { #if 0 // Usually 1-700 faces and 1-7 materials diff --git a/src/client/mapblock_mesh.h b/src/client/mapblock_mesh.h index f8aed40dc..6a9c00ed5 100644 --- a/src/client/mapblock_mesh.h +++ b/src/client/mapblock_mesh.h @@ -45,10 +45,8 @@ struct MeshMakeData Client *m_client; bool m_use_shaders; - bool m_use_tangent_vertices; - MeshMakeData(Client *client, bool use_shaders, - bool use_tangent_vertices = false); + MeshMakeData(Client *client, bool use_shaders); /* Copy block data manually (to allow optimizations by the caller) @@ -136,7 +134,6 @@ private: IShaderSource *m_shdrsrc; bool m_enable_shaders; - bool m_use_tangent_vertices; bool m_enable_vbo; // Must animate() be called before rendering? diff --git a/src/client/mesh_generator_thread.cpp b/src/client/mesh_generator_thread.cpp index 53b980eeb..c8d1cba26 100644 --- a/src/client/mesh_generator_thread.cpp +++ b/src/client/mesh_generator_thread.cpp @@ -52,9 +52,6 @@ MeshUpdateQueue::MeshUpdateQueue(Client *client): m_client(client) { m_cache_enable_shaders = g_settings->getBool("enable_shaders"); - m_cache_use_tangent_vertices = m_cache_enable_shaders && ( - g_settings->getBool("enable_bumpmapping") || - g_settings->getBool("enable_parallax_occlusion")); m_cache_smooth_lighting = g_settings->getBool("smooth_lighting"); m_meshgen_block_cache_size = g_settings->getS32("meshgen_block_cache_size"); } @@ -207,8 +204,7 @@ CachedMapBlockData* MeshUpdateQueue::getCachedBlock(const v3s16 &p) void MeshUpdateQueue::fillDataFromMapBlockCache(QueuedMeshUpdate *q) { - MeshMakeData *data = new MeshMakeData(m_client, m_cache_enable_shaders, - m_cache_use_tangent_vertices); + MeshMakeData *data = new MeshMakeData(m_client, m_cache_enable_shaders); q->data = data; data->fillBlockDataBegin(q->p); diff --git a/src/client/mesh_generator_thread.h b/src/client/mesh_generator_thread.h index 9a42852a3..f3c5e7da8 100644 --- a/src/client/mesh_generator_thread.h +++ b/src/client/mesh_generator_thread.h @@ -88,7 +88,6 @@ private: // TODO: Add callback to update these when g_settings changes bool m_cache_enable_shaders; - bool m_cache_use_tangent_vertices; bool m_cache_smooth_lighting; int m_meshgen_block_cache_size; diff --git a/src/client/shader.cpp b/src/client/shader.cpp index c5fe5dfe0..e2eeb4ab0 100644 --- a/src/client/shader.cpp +++ b/src/client/shader.cpp @@ -688,35 +688,6 @@ ShaderInfo generate_shader(const std::string &name, u8 material_type, u8 drawtyp shaders_header += itos(drawtype); shaders_header += "\n"; - if (g_settings->getBool("enable_bumpmapping")) - shaders_header += "#define ENABLE_BUMPMAPPING\n"; - - if (g_settings->getBool("enable_parallax_occlusion")){ - int mode = g_settings->getFloat("parallax_occlusion_mode"); - float scale = g_settings->getFloat("parallax_occlusion_scale"); - float bias = g_settings->getFloat("parallax_occlusion_bias"); - int iterations = g_settings->getFloat("parallax_occlusion_iterations"); - shaders_header += "#define ENABLE_PARALLAX_OCCLUSION\n"; - shaders_header += "#define PARALLAX_OCCLUSION_MODE "; - shaders_header += itos(mode); - shaders_header += "\n"; - shaders_header += "#define PARALLAX_OCCLUSION_SCALE "; - shaders_header += ftos(scale); - shaders_header += "\n"; - shaders_header += "#define PARALLAX_OCCLUSION_BIAS "; - shaders_header += ftos(bias); - shaders_header += "\n"; - shaders_header += "#define PARALLAX_OCCLUSION_ITERATIONS "; - shaders_header += itos(iterations); - shaders_header += "\n"; - } - - shaders_header += "#define USE_NORMALMAPS "; - if (g_settings->getBool("enable_bumpmapping") || g_settings->getBool("enable_parallax_occlusion")) - shaders_header += "1\n"; - else - shaders_header += "0\n"; - if (g_settings->getBool("enable_waving_water")){ shaders_header += "#define ENABLE_WAVING_WATER 1\n"; shaders_header += "#define WATER_WAVE_HEIGHT "; diff --git a/src/client/wieldmesh.cpp b/src/client/wieldmesh.cpp index 285cc38e5..ad583210a 100644 --- a/src/client/wieldmesh.cpp +++ b/src/client/wieldmesh.cpp @@ -305,7 +305,7 @@ void WieldMeshSceneNode::setExtruded(const std::string &imagename, scene::SMesh *createSpecialNodeMesh(Client *client, content_t id, std::vector *colors, const ContentFeatures &f) { - MeshMakeData mesh_make_data(client, false, false); + MeshMakeData mesh_make_data(client, false); MeshCollector collector; mesh_make_data.setSmoothLighting(false); MapblockMeshGenerator gen(&mesh_make_data, &collector); diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 8f5ed3e17..6c7d4be97 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -251,12 +251,6 @@ void set_default_settings(Settings *settings) settings->setDefault("bilinear_filter", "false"); settings->setDefault("trilinear_filter", "false"); settings->setDefault("tone_mapping", "false"); - settings->setDefault("enable_bumpmapping", "false"); - settings->setDefault("enable_parallax_occlusion", "false"); - settings->setDefault("parallax_occlusion_mode", "1"); - settings->setDefault("parallax_occlusion_iterations", "4"); - settings->setDefault("parallax_occlusion_scale", "0.08"); - settings->setDefault("parallax_occlusion_bias", "0.04"); settings->setDefault("enable_waving_water", "false"); settings->setDefault("water_wave_height", "1.0"); settings->setDefault("water_wave_length", "20.0"); diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 3a5934cf3..5c2e5cd09 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -266,9 +266,6 @@ void TextureSettings::readSettings() { connected_glass = g_settings->getBool("connected_glass"); opaque_water = g_settings->getBool("opaque_water"); - bool enable_shaders = g_settings->getBool("enable_shaders"); - bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping"); - bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion"); bool smooth_lighting = g_settings->getBool("smooth_lighting"); enable_mesh_cache = g_settings->getBool("enable_mesh_cache"); enable_minimap = g_settings->getBool("enable_minimap"); @@ -281,8 +278,6 @@ void TextureSettings::readSettings() if (smooth_lighting) enable_mesh_cache = false; - use_normal_texture = enable_shaders && - (enable_bumpmapping || enable_parallax_occlusion); if (leaves_style_str == "fancy") { leaves_style = LEAVES_FANCY; } else if (leaves_style_str == "simple") { @@ -635,10 +630,6 @@ static void fillTileAttribs(ITextureSource *tsrc, TileLayer *layer, if (!tile.world_aligned) layer->scale = 1; - // Normal texture and shader flags texture - if (tsettings.use_normal_texture) { - layer->normal_texture = tsrc->getNormalTexture(tiledef.name); - } layer->flags_texture = tsrc->getShaderFlagsTexture(layer->normal_texture ? true : false); // Material flags diff --git a/src/nodedef.h b/src/nodedef.h index 71c56bda9..66c21cc07 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -158,7 +158,6 @@ public: int node_texture_size; bool opaque_water; bool connected_glass; - bool use_normal_texture; bool enable_mesh_cache; bool enable_minimap; -- cgit v1.2.3 From 660115c1abc76f3d4f6a6597ed0c4737465c6c55 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Mon, 19 Oct 2020 20:38:15 +0200 Subject: Decouple entity minimap markers from nametags replacing with show_on_minimap property (#10443) --- doc/lua_api.txt | 4 ++++ src/client/camera.h | 2 -- src/client/client.cpp | 2 ++ src/client/content_cao.cpp | 27 +++++++++++++++++++++++++++ src/client/content_cao.h | 4 ++++ src/client/minimap.cpp | 35 ++++++++++++++++++++++++++--------- src/client/minimap.h | 11 +++++++++++ src/object_properties.cpp | 8 +++++++- src/object_properties.h | 1 + src/script/common/c_content.cpp | 3 +++ src/server/player_sao.cpp | 1 + 11 files changed, 86 insertions(+), 12 deletions(-) (limited to 'doc') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index d3aaa309c..9a46c7b57 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -6880,6 +6880,10 @@ Player properties need to be saved manually. shaded = true, -- Setting this to 'false' disables diffuse lighting of entity + + show_on_minimap = false, + -- Defaults to true for players, false for other entities. + -- If set to true the entity will show as a marker on the minimap. } Entity definition diff --git a/src/client/camera.h b/src/client/camera.h index 3a59637bc..16a1961be 100644 --- a/src/client/camera.h +++ b/src/client/camera.h @@ -169,8 +169,6 @@ public: void removeNametag(Nametag *nametag); - const std::list &getNametags() { return m_nametags; } - void drawNametags(); inline void addArmInertia(f32 player_yaw); diff --git a/src/client/client.cpp b/src/client/client.cpp index 0bd98b256..af69d0ec9 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -326,6 +326,8 @@ Client::~Client() } delete m_minimap; + m_minimap = nullptr; + delete m_media_downloader; } diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index fae06554a..42184b08f 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -47,6 +47,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include "client/shader.h" +#include "client/minimap.h" class Settings; struct ToolCapabilities; @@ -566,6 +567,9 @@ void GenericCAO::removeFromScene(bool permanent) m_client->getCamera()->removeNametag(m_nametag); m_nametag = nullptr; } + + if (m_marker && m_client->getMinimap()) + m_client->getMinimap()->removeMarker(&m_marker); } void GenericCAO::addToScene(ITextureSource *tsrc) @@ -794,6 +798,7 @@ void GenericCAO::addToScene(ITextureSource *tsrc) node->setParent(m_matrixnode); updateNametag(); + updateMarker(); updateNodePos(); updateAnimation(); updateBonePosition(); @@ -885,6 +890,23 @@ u16 GenericCAO::getLightPosition(v3s16 *pos) return 3; } +void GenericCAO::updateMarker() +{ + if (!m_prop.show_on_minimap) { + if (m_marker) + m_client->getMinimap()->removeMarker(&m_marker); + return; + } + + if (m_marker) + return; + + scene::ISceneNode *node = getSceneNode(); + if (!node) + return; + m_marker = m_client->getMinimap()->addMarker(node); +} + void GenericCAO::updateNametag() { if (m_is_local_player) // No nametag for local player @@ -1576,6 +1598,8 @@ void GenericCAO::processMessage(const std::string &data) u8 cmd = readU8(is); if (cmd == AO_CMD_SET_PROPERTIES) { ObjectProperties newprops; + newprops.show_on_minimap = m_is_player; // default + newprops.deSerialize(is); // Check what exactly changed @@ -1609,6 +1633,8 @@ void GenericCAO::processMessage(const std::string &data) if ((m_is_player && !m_is_local_player) && m_prop.nametag.empty()) m_prop.nametag = m_name; + if (m_is_local_player) + m_prop.show_on_minimap = false; if (expire_visuals) { expireVisuals(); @@ -1621,6 +1647,7 @@ void GenericCAO::processMessage(const std::string &data) updateTextures(m_current_texture_modifier); } updateNametag(); + updateMarker(); } } else if (cmd == AO_CMD_UPDATE_POSITION) { // Not sent by the server if this object is an attachment. diff --git a/src/client/content_cao.h b/src/client/content_cao.h index daf697767..435fc2931 100644 --- a/src/client/content_cao.h +++ b/src/client/content_cao.h @@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc., class Camera; class Client; struct Nametag; +struct MinimapMarker; /* SmoothTranslator @@ -84,6 +85,7 @@ private: scene::IBillboardSceneNode *m_spritenode = nullptr; scene::IDummyTransformationSceneNode *m_matrixnode = nullptr; Nametag *m_nametag = nullptr; + MinimapMarker *m_marker = nullptr; v3f m_position = v3f(0.0f, 10.0f * BS, 0); v3f m_velocity; v3f m_acceleration; @@ -254,6 +256,8 @@ public: void updateNametag(); + void updateMarker(); + void updateNodePos(); void step(float dtime, ClientEnvironment *env); diff --git a/src/client/minimap.cpp b/src/client/minimap.cpp index 6bae408b4..d068ad5f7 100644 --- a/src/client/minimap.cpp +++ b/src/client/minimap.cpp @@ -252,6 +252,10 @@ Minimap::~Minimap() driver->removeTexture(data->minimap_overlay_square); driver->removeTexture(data->object_marker_red); + for (MinimapMarker *m : m_markers) + delete m; + m_markers.clear(); + delete data; delete m_minimap_update_thread; } @@ -678,21 +682,34 @@ void Minimap::drawMinimap(core::rect rect) { } } +MinimapMarker* Minimap::addMarker(scene::ISceneNode *parent_node) +{ + MinimapMarker *m = new MinimapMarker(parent_node); + m_markers.push_back(m); + return m; +} + +void Minimap::removeMarker(MinimapMarker **m) +{ + m_markers.remove(*m); + delete *m; + *m = nullptr; +} + void Minimap::updateActiveMarkers() { video::IImage *minimap_mask = data->minimap_shape_round ? data->minimap_mask_round : data->minimap_mask_square; - const std::list &nametags = client->getCamera()->getNametags(); - m_active_markers.clear(); - - for (Nametag *nametag : nametags) { - v3s16 pos = floatToInt(nametag->parent_node->getAbsolutePosition() + - intToFloat(client->getCamera()->getOffset(), BS), BS); - pos -= data->pos - v3s16(data->mode.map_size / 2, - data->mode.scan_height / 2, - data->mode.map_size / 2); + v3f cam_offset = intToFloat(client->getCamera()->getOffset(), BS); + v3s16 pos_offset = data->pos - v3s16(data->mode.map_size / 2, + data->mode.scan_height / 2, + data->mode.map_size / 2); + + for (MinimapMarker *marker : m_markers) { + v3s16 pos = floatToInt(marker->parent_node->getAbsolutePosition() + + cam_offset, BS) - pos_offset; if (pos.X < 0 || pos.X > data->mode.map_size || pos.Y < 0 || pos.Y > data->mode.scan_height || pos.Z < 0 || pos.Z > data->mode.map_size) { diff --git a/src/client/minimap.h b/src/client/minimap.h index 11374b116..4a2c462f8 100644 --- a/src/client/minimap.h +++ b/src/client/minimap.h @@ -48,6 +48,13 @@ struct MinimapModeDef { u16 scale; }; +struct MinimapMarker { + MinimapMarker(scene::ISceneNode *parent_node): + parent_node(parent_node) + { + } + scene::ISceneNode *parent_node; +}; struct MinimapPixel { //! The topmost node that the minimap displays. MapNode n; @@ -142,6 +149,9 @@ public: scene::SMeshBuffer *getMinimapMeshBuffer(); + MinimapMarker* addMarker(scene::ISceneNode *parent_node); + void removeMarker(MinimapMarker **marker); + void updateActiveMarkers(); void drawMinimap(); void drawMinimap(core::rect rect); @@ -162,5 +172,6 @@ private: u16 m_surface_mode_scan_height; f32 m_angle; std::mutex m_mutex; + std::list m_markers; std::list m_active_markers; }; diff --git a/src/object_properties.cpp b/src/object_properties.cpp index c31c667e7..f31773060 100644 --- a/src/object_properties.cpp +++ b/src/object_properties.cpp @@ -70,6 +70,7 @@ std::string ObjectProperties::dump() os << ", use_texture_alpha=" << use_texture_alpha; os << ", damage_texture_modifier=" << damage_texture_modifier; os << ", shaded=" << shaded; + os << ", show_on_minimap=" << show_on_minimap; return os.str(); } @@ -118,6 +119,7 @@ void ObjectProperties::serialize(std::ostream &os) const writeU8(os, use_texture_alpha); os << serializeString16(damage_texture_modifier); writeU8(os, shaded); + writeU8(os, show_on_minimap); // Add stuff only at the bottom. // Never remove anything, because we don't want new versions of this @@ -174,7 +176,11 @@ void ObjectProperties::deSerialize(std::istream &is) damage_texture_modifier = deSerializeString16(is); u8 tmp = readU8(is); if (is.eof()) - throw SerializationError(""); + return; shaded = tmp; + tmp = readU8(is); + if (is.eof()) + return; + show_on_minimap = tmp; } catch (SerializationError &e) {} } diff --git a/src/object_properties.h b/src/object_properties.h index f010c1daf..adb483527 100644 --- a/src/object_properties.h +++ b/src/object_properties.h @@ -62,6 +62,7 @@ struct ObjectProperties float zoom_fov = 0.0f; bool use_texture_alpha = false; bool shaded = true; + bool show_on_minimap = false; ObjectProperties(); std::string dump(); diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index a95cf94a1..e3cb9042e 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -331,6 +331,7 @@ void read_object_properties(lua_State *L, int index, getfloatfield(L, -1, "zoom_fov", prop->zoom_fov); getboolfield(L, -1, "use_texture_alpha", prop->use_texture_alpha); getboolfield(L, -1, "shaded", prop->shaded); + getboolfield(L, -1, "show_on_minimap", prop->show_on_minimap); getstringfield(L, -1, "damage_texture_modifier", prop->damage_texture_modifier); } @@ -419,6 +420,8 @@ void push_object_properties(lua_State *L, ObjectProperties *prop) lua_setfield(L, -2, "shaded"); lua_pushlstring(L, prop->damage_texture_modifier.c_str(), prop->damage_texture_modifier.size()); lua_setfield(L, -2, "damage_texture_modifier"); + lua_pushboolean(L, prop->show_on_minimap); + lua_setfield(L, -2, "show_on_minimap"); } /******************************************************************************/ diff --git a/src/server/player_sao.cpp b/src/server/player_sao.cpp index 344d18a20..9fb53380c 100644 --- a/src/server/player_sao.cpp +++ b/src/server/player_sao.cpp @@ -55,6 +55,7 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t p m_prop.backface_culling = false; m_prop.makes_footstep_sound = true; m_prop.stepheight = PLAYER_DEFAULT_STEPHEIGHT * BS; + m_prop.show_on_minimap = true; m_hp = m_prop.hp_max; m_breath = m_prop.breath_max; // Disable zoom in survival mode using a value of 0 -- cgit v1.2.3 From 33b2c5f5b1c565171296f26395d803b08f4575e9 Mon Sep 17 00:00:00 2001 From: Zughy <63455151+Zughy@users.noreply.github.com> Date: Thu, 22 Oct 2020 17:18:01 +0200 Subject: Clean up l_object.cpp (#10512) Co-authored-by: Zughy <4279489-marco_a@users.noreply.gitlab.com> --- doc/lua_api.txt | 48 +- src/script/lua_api/l_object.cpp | 1163 +++++++++++++++++++-------------------- src/script/lua_api/l_object.h | 56 +- 3 files changed, 602 insertions(+), 665 deletions(-) (limited to 'doc') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 9a46c7b57..daf0da3d2 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -6217,18 +6217,18 @@ object you are working with still exists. * `rot` is a vector (radians). X is pitch (elevation), Y is yaw (heading) and Z is roll (bank). * `get_rotation()`: returns the rotation, a vector (radians) -* `set_yaw(radians)`: sets the yaw (heading). +* `set_yaw(yaw)`: sets the yaw in radians (heading). * `get_yaw()`: returns number in radians * `set_texture_mod(mod)` * Set a texture modifier to the base texture, for sprites and meshes. * When calling `set_texture_mod` again, the previous one is discarded. * `mod` the texture modifier. See [Texture modifiers]. * `get_texture_mod()` returns current texture modifier -* `set_sprite(p, num_frames, framelength, select_x_by_camera)` +* `set_sprite(start_frame, num_frames, framelength, select_x_by_camera)` * Specifies and starts a sprite animation * Animations iterate along the frame `y` position. - * `p`: {x=column number, y=row number}, the coordinate of the first frame - default: `{x=0, y=0}` + * `start_frame`: {x=column number, y=row number}, the coordinate of the + first frame, default: `{x=0, y=0}` * `num_frames`: Total frames in the texture, default: `1` * `framelength`: Time per animated frame in seconds, default: `0.2` * `select_x_by_camera`: Only for visual = `sprite`. Changes the frame `x` @@ -6391,8 +6391,8 @@ object you are working with still exists. nodes square) * `selected_mode` is the mode index to be selected after modes have been changed (0 is the first mode). -* `set_sky(parameters)` - * `parameters` is a table with the following optional fields: +* `set_sky(sky_parameters)` + * `sky_parameters` is a table with the following optional fields: * `base_color`: ColorSpec, changes fog in "skybox" and "plain". * `type`: Available types: * `"regular"`: Uses 0 textures, `base_color` ignored @@ -6436,8 +6436,8 @@ object you are working with still exists. * `get_sky()`: returns base_color, type, table of textures, clouds. * `get_sky_color()`: returns a table with the `sky_color` parameters as in `set_sky`. -* `set_sun(parameters)`: - * `parameters` is a table with the following optional fields: +* `set_sun(sun_parameters)`: + * `sun_parameters` is a table with the following optional fields: * `visible`: Boolean for whether the sun is visible. (default: `true`) * `texture`: A regular texture for the sun. Setting to `""` @@ -6451,8 +6451,8 @@ object you are working with still exists. * `scale`: Float controlling the overall size of the sun. (default: `1`) * `get_sun()`: returns a table with the current sun parameters as in `set_sun`. -* `set_moon(parameters)`: - * `parameters` is a table with the following optional fields: +* `set_moon(moon_parameters)`: + * `moon_parameters` is a table with the following optional fields: * `visible`: Boolean for whether the moon is visible. (default: `true`) * `texture`: A regular texture for the moon. Setting to `""` @@ -6462,8 +6462,8 @@ object you are working with still exists. * `scale`: Float controlling the overall size of the moon (default: `1`) * `get_moon()`: returns a table with the current moon parameters as in `set_moon`. -* `set_stars(parameters)`: - * `parameters` is a table with the following optional fields: +* `set_stars(star_parameters)`: + * `star_parameters` is a table with the following optional fields: * `visible`: Boolean for whether the stars are visible. (default: `true`) * `count`: Integer number to set the number of stars in @@ -6475,8 +6475,8 @@ object you are working with still exists. * `scale`: Float controlling the overall size of the stars (default: `1`) * `get_stars()`: returns a table with the current stars parameters as in `set_stars`. -* `set_clouds(parameters)`: set cloud parameters - * `parameters` is a table with the following optional fields: +* `set_clouds(cloud_parameters)`: set cloud parameters + * `cloud_parameters` is a table with the following optional fields: * `density`: from `0` (no clouds) to `1` (full clouds) (default `0.4`) * `color`: basic cloud color with alpha channel, ColorSpec (default `#fff0f0e5`). @@ -6494,21 +6494,17 @@ object you are working with still exists. amount. * `nil`: Disables override, defaulting to sunlight based on day-night cycle * `get_day_night_ratio()`: returns the ratio or nil if it isn't overridden -* `set_local_animation(stand/idle, walk, dig, walk+dig, frame_speed=frame_speed)`: - set animation for player model in third person view - - set_local_animation({x=0, y=79}, -- stand/idle animation key frames - {x=168, y=187}, -- walk animation key frames - {x=189, y=198}, -- dig animation key frames - {x=200, y=219}, -- walk+dig animation key frames - frame_speed=30) -- animation frame speed -* `get_local_animation()`: returns stand, walk, dig, dig+walk tables and +* `set_local_animation(idle, walk, dig, walk_while_dig, frame_speed)`: + set animation for player model in third person view. + * Every animation equals to a `{x=starting frame, y=ending frame}` table. + * `frame_speed` sets the animations frame speed. Default is 30. +* `get_local_animation()`: returns idle, walk, dig, walk_while_dig tables and `frame_speed`. -* `set_eye_offset({x=0,y=0,z=0},{x=0,y=0,z=0})`: defines offset value for - camera per player. +* `set_eye_offset(firstperson, thirdperson)`: defines offset vectors for camera + per player. * in first person view * in third person view (max. values `{x=-10/10,y=-10,15,z=-5/5}`) -* `get_eye_offset()`: returns `offset_first` and `offset_third` +* `get_eye_offset()`: returns first and third person offsets. * `send_mapblock(blockpos)`: * Sends a server-side loaded mapblock to the player. * Returns `false` if failed. diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 96c8c687c..c3f0ec8e0 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -44,43 +44,44 @@ ObjectRef* ObjectRef::checkobject(lua_State *L, int narg) { luaL_checktype(L, narg, LUA_TUSERDATA); void *ud = luaL_checkudata(L, narg, className); - if (!ud) luaL_typerror(L, narg, className); + if (ud == nullptr) + luaL_typerror(L, narg, className); return *(ObjectRef**)ud; // unbox pointer } ServerActiveObject* ObjectRef::getobject(ObjectRef *ref) { - ServerActiveObject *co = ref->m_object; - if (co && co->isGone()) - return NULL; - return co; + ServerActiveObject *sao = ref->m_object; + if (sao && sao->isGone()) + return nullptr; + return sao; } LuaEntitySAO* ObjectRef::getluaobject(ObjectRef *ref) { - ServerActiveObject *obj = getobject(ref); - if (obj == NULL) - return NULL; - if (obj->getType() != ACTIVEOBJECT_TYPE_LUAENTITY) - return NULL; - return (LuaEntitySAO*)obj; + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return nullptr; + if (sao->getType() != ACTIVEOBJECT_TYPE_LUAENTITY) + return nullptr; + return (LuaEntitySAO*)sao; } PlayerSAO* ObjectRef::getplayersao(ObjectRef *ref) { - ServerActiveObject *obj = getobject(ref); - if (obj == NULL) - return NULL; - if (obj->getType() != ACTIVEOBJECT_TYPE_PLAYER) - return NULL; - return (PlayerSAO*)obj; + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return nullptr; + if (sao->getType() != ACTIVEOBJECT_TYPE_PLAYER) + return nullptr; + return (PlayerSAO*)sao; } RemotePlayer *ObjectRef::getplayer(ObjectRef *ref) { PlayerSAO *playersao = getplayersao(ref); - if (playersao == NULL) - return NULL; + if (playersao == nullptr) + return nullptr; return playersao->getPlayer(); } @@ -88,9 +89,8 @@ RemotePlayer *ObjectRef::getplayer(ObjectRef *ref) // garbage collector int ObjectRef::gc_object(lua_State *L) { - ObjectRef *o = *(ObjectRef **)(lua_touserdata(L, 1)); - //infostream<<"ObjectRef::gc_object: o="<getType() == ACTIVEOBJECT_TYPE_PLAYER) + if (sao->getType() == ACTIVEOBJECT_TYPE_PLAYER) return 0; - co->clearChildAttachments(); - co->clearParentAttachment(); + sao->clearChildAttachments(); + sao->clearParentAttachment(); - verbosestream << "ObjectRef::l_remove(): id=" << co->getId() << std::endl; - co->m_pending_removal = true; + verbosestream << "ObjectRef::l_remove(): id=" << sao->getId() << std::endl; + sao->m_pending_removal = true; return 0; } // get_pos(self) -// returns: {x=num, y=num, z=num} int ObjectRef::l_get_pos(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (co == NULL) return 0; - push_v3f(L, co->getBasePosition() / BS); + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + + push_v3f(L, sao->getBasePosition() / BS); return 1; } @@ -131,28 +132,29 @@ int ObjectRef::l_set_pos(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (co == NULL) return 0; - // pos + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + v3f pos = checkFloatPos(L, 2); - // Do it - co->setPos(pos); + + sao->setPos(pos); return 0; } -// move_to(self, pos, continuous=false) +// move_to(self, pos, continuous) int ObjectRef::l_move_to(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (co == NULL) return 0; - // pos + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + v3f pos = checkFloatPos(L, 2); - // continuous bool continuous = readParam(L, 3); - // Do it - co->moveTo(pos, continuous); + + sao->moveTo(pos, continuous); return 0; } @@ -162,32 +164,28 @@ int ObjectRef::l_punch(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); ObjectRef *puncher_ref = checkobject(L, 2); - ServerActiveObject *co = getobject(ref); + ServerActiveObject *sao = getobject(ref); ServerActiveObject *puncher = getobject(puncher_ref); - if (!co || !puncher) + if (sao == nullptr || puncher == nullptr) return 0; - v3f dir; - if (lua_type(L, 5) != LUA_TTABLE) - dir = co->getBasePosition() - puncher->getBasePosition(); - else - dir = read_v3f(L, 5); - float time_from_last_punch = 1000000; - if (lua_isnumber(L, 3)) - time_from_last_punch = lua_tonumber(L, 3); + + float time_from_last_punch = lua_isnil(L, 3) ? + 1000000.0f : readParam(L,3); ToolCapabilities toolcap = read_tool_capabilities(L, 4); - dir.normalize(); + v3f dir = lua_isnil(L, 5) ? + sao->getBasePosition() - puncher->getBasePosition() : check_v3f(L, 5); - u16 src_original_hp = co->getHP(); + dir.normalize(); + u16 src_original_hp = sao->getHP(); u16 dst_origin_hp = puncher->getHP(); - // Do it - u16 wear = co->punch(dir, &toolcap, puncher, time_from_last_punch); + u16 wear = sao->punch(dir, &toolcap, puncher, time_from_last_punch); lua_pushnumber(L, wear); // If the punched is a player, and its HP changed - if (src_original_hp != co->getHP() && - co->getType() == ACTIVEOBJECT_TYPE_PLAYER) { - getServer(L)->SendPlayerHPOrDie((PlayerSAO *)co, + if (src_original_hp != sao->getHP() && + sao->getType() == ACTIVEOBJECT_TYPE_PLAYER) { + getServer(L)->SendPlayerHPOrDie((PlayerSAO *)sao, PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, puncher)); } @@ -195,45 +193,38 @@ int ObjectRef::l_punch(lua_State *L) if (dst_origin_hp != puncher->getHP() && puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) { getServer(L)->SendPlayerHPOrDie((PlayerSAO *)puncher, - PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, co)); + PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, sao)); } return 1; } -// right_click(self, clicker); clicker = an another ObjectRef +// right_click(self, clicker) int ObjectRef::l_right_click(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); ObjectRef *ref2 = checkobject(L, 2); - ServerActiveObject *co = getobject(ref); - ServerActiveObject *co2 = getobject(ref2); - if (co == NULL) return 0; - if (co2 == NULL) return 0; - // Do it - co->rightClick(co2); + ServerActiveObject *sao = getobject(ref); + ServerActiveObject *sao2 = getobject(ref2); + if (sao == nullptr || sao2 == nullptr) + return 0; + + sao->rightClick(sao2); return 0; } -// set_hp(self, hp) -// hp = number of hitpoints (2 * number of hearts) -// returns: nil +// set_hp(self, hp, reason) int ObjectRef::l_set_hp(lua_State *L) { NO_MAP_LOCK_REQUIRED; - - // Get Object ObjectRef *ref = checkobject(L, 1); - luaL_checknumber(L, 2); - ServerActiveObject *co = getobject(ref); - if (co == NULL) + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) return 0; - // Get HP - int hp = lua_tonumber(L, 2); - - // Get Reason + int hp = readParam(L, 2); PlayerHPChangeReason reason(PlayerHPChangeReason::SET_HP); + reason.from_mod = true; if (lua_istable(L, 3)) { lua_pushvalue(L, 3); @@ -248,35 +239,28 @@ int ObjectRef::l_set_hp(lua_State *L) reason.lua_reference = luaL_ref(L, LUA_REGISTRYINDEX); } - // Do it - co->setHP(hp, reason); - if (co->getType() == ACTIVEOBJECT_TYPE_PLAYER) - getServer(L)->SendPlayerHPOrDie((PlayerSAO *)co, reason); - + sao->setHP(hp, reason); + if (sao->getType() == ACTIVEOBJECT_TYPE_PLAYER) + getServer(L)->SendPlayerHPOrDie((PlayerSAO *)sao, reason); if (reason.hasLuaReference()) luaL_unref(L, LUA_REGISTRYINDEX, reason.lua_reference); - - // Return return 0; } // get_hp(self) -// returns: number of hitpoints (2 * number of hearts) -// 0 if not applicable to this type of object int ObjectRef::l_get_hp(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (co == NULL) { + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) { // Default hp is 1 lua_pushnumber(L, 1); return 1; } - int hp = co->getHP(); - /*infostream<<"ObjectRef::l_get_hp(): id="<getId() - <<" hp="<getHP(); + lua_pushnumber(L, hp); return 1; } @@ -286,11 +270,12 @@ int ObjectRef::l_get_inventory(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (co == NULL) return 0; - // Do it - InventoryLocation loc = co->getInventoryLocation(); - if (getServerInventoryMgr(L)->getInventory(loc) != NULL) + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + + InventoryLocation loc = sao->getInventoryLocation(); + if (getServerInventoryMgr(L)->getInventory(loc) != nullptr) InvRef::create(L, loc); else lua_pushnil(L); // An object may have no inventory (nil) @@ -302,11 +287,11 @@ int ObjectRef::l_get_wield_list(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (!co) + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) return 0; - lua_pushstring(L, co->getWieldList().c_str()); + lua_pushstring(L, sao->getWieldList().c_str()); return 1; } @@ -315,11 +300,11 @@ int ObjectRef::l_get_wield_index(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (!co) + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) return 0; - lua_pushinteger(L, co->getWieldIndex() + 1); + lua_pushinteger(L, sao->getWieldIndex() + 1); return 1; } @@ -328,31 +313,33 @@ int ObjectRef::l_get_wielded_item(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (!co) { + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) { // Empty ItemStack LuaItemStack::create(L, ItemStack()); return 1; } ItemStack selected_item; - co->getWieldedItem(&selected_item, nullptr); + sao->getWieldedItem(&selected_item, nullptr); LuaItemStack::create(L, selected_item); return 1; } -// set_wielded_item(self, itemstack or itemstring or table or nil) +// set_wielded_item(self, item) int ObjectRef::l_set_wielded_item(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (co == NULL) return 0; - // Do it + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + ItemStack item = read_item(L, 2, getServer(L)->idef()); - bool success = co->setWieldedItem(item); - if (success && co->getType() == ACTIVEOBJECT_TYPE_PLAYER) { - getServer(L)->SendInventory((PlayerSAO *)co, true); + + bool success = sao->setWieldedItem(item); + if (success && sao->getType() == ACTIVEOBJECT_TYPE_PLAYER) { + getServer(L)->SendInventory((PlayerSAO *)sao, true); } lua_pushboolean(L, success); return 1; @@ -363,12 +350,14 @@ int ObjectRef::l_set_armor_groups(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (co == NULL) return 0; - // Do it + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + ItemGroupList groups; + read_groups(L, 2, groups); - co->setArmorGroups(groups); + sao->setArmorGroups(groups); return 0; } @@ -377,77 +366,11 @@ int ObjectRef::l_get_armor_groups(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (co == NULL) + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) return 0; - // Do it - push_groups(L, co->getArmorGroups()); - return 1; -} -// set_physics_override(self, physics_override_speed, physics_override_jump, -// physics_override_gravity, sneak, sneak_glitch, new_move) -int ObjectRef::l_set_physics_override(lua_State *L) -{ - NO_MAP_LOCK_REQUIRED; - ObjectRef *ref = checkobject(L, 1); - PlayerSAO *co = (PlayerSAO *) getobject(ref); - if (co == NULL) return 0; - // Do it - if (lua_istable(L, 2)) { - co->m_physics_override_speed = getfloatfield_default( - L, 2, "speed", co->m_physics_override_speed); - co->m_physics_override_jump = getfloatfield_default( - L, 2, "jump", co->m_physics_override_jump); - co->m_physics_override_gravity = getfloatfield_default( - L, 2, "gravity", co->m_physics_override_gravity); - co->m_physics_override_sneak = getboolfield_default( - L, 2, "sneak", co->m_physics_override_sneak); - co->m_physics_override_sneak_glitch = getboolfield_default( - L, 2, "sneak_glitch", co->m_physics_override_sneak_glitch); - co->m_physics_override_new_move = getboolfield_default( - L, 2, "new_move", co->m_physics_override_new_move); - co->m_physics_override_sent = false; - } else { - // old, non-table format - if (!lua_isnil(L, 2)) { - co->m_physics_override_speed = lua_tonumber(L, 2); - co->m_physics_override_sent = false; - } - if (!lua_isnil(L, 3)) { - co->m_physics_override_jump = lua_tonumber(L, 3); - co->m_physics_override_sent = false; - } - if (!lua_isnil(L, 4)) { - co->m_physics_override_gravity = lua_tonumber(L, 4); - co->m_physics_override_sent = false; - } - } - return 0; -} - -// get_physics_override(self) -int ObjectRef::l_get_physics_override(lua_State *L) -{ - NO_MAP_LOCK_REQUIRED; - ObjectRef *ref = checkobject(L, 1); - PlayerSAO *co = (PlayerSAO *)getobject(ref); - if (co == NULL) - return 0; - // Do it - lua_newtable(L); - lua_pushnumber(L, co->m_physics_override_speed); - lua_setfield(L, -2, "speed"); - lua_pushnumber(L, co->m_physics_override_jump); - lua_setfield(L, -2, "jump"); - lua_pushnumber(L, co->m_physics_override_gravity); - lua_setfield(L, -2, "gravity"); - lua_pushboolean(L, co->m_physics_override_sneak); - lua_setfield(L, -2, "sneak"); - lua_pushboolean(L, co->m_physics_override_sneak_glitch); - lua_setfield(L, -2, "sneak_glitch"); - lua_pushboolean(L, co->m_physics_override_new_move); - lua_setfield(L, -2, "new_move"); + push_groups(L, sao->getArmorGroups()); return 1; } @@ -456,9 +379,10 @@ int ObjectRef::l_set_animation(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (co == NULL) return 0; - // Do it + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + v2f frames = v2f(1, 1); if (!lua_isnil(L, 2)) frames = readParam(L, 2); @@ -471,7 +395,8 @@ int ObjectRef::l_set_animation(lua_State *L) bool frame_loop = true; if (lua_isboolean(L, 5)) frame_loop = readParam(L, 5); - co->setAnimation(frames, frame_speed, frame_blend, frame_loop); + + sao->setAnimation(frames, frame_speed, frame_blend, frame_loop); return 0; } @@ -480,16 +405,16 @@ int ObjectRef::l_get_animation(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (co == NULL) + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) return 0; - // Do it + v2f frames = v2f(1,1); float frame_speed = 15; float frame_blend = 0; bool frame_loop = true; - co->getAnimation(&frames, &frame_speed, &frame_blend, &frame_loop); + sao->getAnimation(&frames, &frame_speed, &frame_blend, &frame_loop); push_v2f(L, frames); lua_pushnumber(L, frame_speed); lua_pushnumber(L, frame_blend); @@ -497,23 +422,21 @@ int ObjectRef::l_get_animation(lua_State *L) return 4; } -// set_local_animation(self, {stand/idle}, {walk}, {dig}, {walk+dig}, frame_speed) +// set_local_animation(self, idle, walk, dig, walk_while_dig, frame_speed) int ObjectRef::l_set_local_animation(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; - // Do it + v2s32 frames[4]; for (int i=0;i<4;i++) { if (!lua_isnil(L, 2+1)) frames[i] = read_v2s32(L, 2+i); } - float frame_speed = 30; - if (!lua_isnil(L, 6)) - frame_speed = lua_tonumber(L, 6); + float frame_speed = lua_isnil(L, 6) ? 30 : readParam(L, 6); getServer(L)->setLocalPlayerAnimations(player, frames, frame_speed); lua_pushboolean(L, true); @@ -526,7 +449,7 @@ int ObjectRef::l_get_local_animation(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; v2s32 frames[4]; @@ -541,27 +464,22 @@ int ObjectRef::l_get_local_animation(lua_State *L) return 5; } -// set_eye_offset(self, v3f first pv, v3f third pv) +// set_eye_offset(self, firstperson, thirdperson) int ObjectRef::l_set_eye_offset(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; - // Do it - v3f offset_first = v3f(0, 0, 0); - v3f offset_third = v3f(0, 0, 0); - if (!lua_isnil(L, 2)) - offset_first = read_v3f(L, 2); - if (!lua_isnil(L, 3)) - offset_third = read_v3f(L, 3); + v3f offset_first = read_v3f(L, 2); + v3f offset_third = read_v3f(L, 3); // Prevent abuse of offset values (keep player always visible) offset_third.X = rangelim(offset_third.X,-10,10); offset_third.Z = rangelim(offset_third.Z,-5,5); - /* TODO: if possible: improve the camera colision detetion to allow Y <= -1.5) */ + /* TODO: if possible: improve the camera collision detection to allow Y <= -1.5) */ offset_third.Y = rangelim(offset_third.Y,-10,15); //1.5*BS getServer(L)->setPlayerEyeOffset(player, offset_first, offset_third); @@ -575,9 +493,9 @@ int ObjectRef::l_get_eye_offset(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; - // Do it + push_v3f(L, player->eye_offset_first); push_v3f(L, player->eye_offset_third); return 2; @@ -588,14 +506,14 @@ int ObjectRef::l_send_mapblock(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - RemotePlayer *player = getplayer(ref); - if (!player) + if (player == nullptr) return 0; - v3s16 p = read_v3s16(L, 2); + + v3s16 pos = read_v3s16(L, 2); session_t peer_id = player->getPeerId(); - bool r = getServer(L)->SendBlock(peer_id, p); + bool r = getServer(L)->SendBlock(peer_id, pos); lua_pushboolean(L, r); return 1; @@ -606,14 +524,13 @@ int ObjectRef::l_set_animation_frame_speed(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (co == NULL) + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) return 0; - // Do it if (!lua_isnil(L, 2)) { - float frame_speed = lua_tonumber(L, 2); - co->setAnimationSpeed(frame_speed); + float frame_speed = readParam(L, 2); + sao->setAnimationSpeed(frame_speed); lua_pushboolean(L, true); } else { lua_pushboolean(L, false); @@ -621,24 +538,20 @@ int ObjectRef::l_set_animation_frame_speed(lua_State *L) return 1; } -// set_bone_position(self, std::string bone, v3f position, v3f rotation) +// set_bone_position(self, bone, position, rotation) int ObjectRef::l_set_bone_position(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (co == NULL) return 0; - // Do it - std::string bone; - if (!lua_isnil(L, 2)) - bone = readParam(L, 2); - v3f position = v3f(0, 0, 0); - if (!lua_isnil(L, 3)) - position = check_v3f(L, 3); - v3f rotation = v3f(0, 0, 0); - if (!lua_isnil(L, 4)) - rotation = check_v3f(L, 4); - co->setBonePosition(bone, position, rotation); + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + + std::string bone = readParam(L, 2); + v3f position = check_v3f(L, 3); + v3f rotation = check_v3f(L, 4); + + sao->setBonePosition(bone, position, rotation); return 0; } @@ -647,17 +560,15 @@ int ObjectRef::l_get_bone_position(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (co == NULL) + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) return 0; - // Do it - std::string bone; - if (!lua_isnil(L, 2)) - bone = readParam(L, 2); + + std::string bone = readParam(L, 2); v3f position = v3f(0, 0, 0); v3f rotation = v3f(0, 0, 0); - co->getBonePosition(bone, &position, &rotation); + sao->getBonePosition(bone, &position, &rotation); push_v3f(L, position); push_v3f(L, rotation); @@ -668,44 +579,34 @@ int ObjectRef::l_get_bone_position(lua_State *L) int ObjectRef::l_set_attach(lua_State *L) { GET_ENV_PTR; - ObjectRef *ref = checkobject(L, 1); ObjectRef *parent_ref = checkobject(L, 2); - ServerActiveObject *co = getobject(ref); + ServerActiveObject *sao = getobject(ref); ServerActiveObject *parent = getobject(parent_ref); - if (co == NULL) + if (sao == nullptr || parent == nullptr) return 0; - - if (parent == NULL) - return 0; - - if (co == parent) + if (sao == parent) throw LuaError("ObjectRef::set_attach: attaching object to itself is not allowed."); - // Do it int parent_id = 0; std::string bone; v3f position = v3f(0, 0, 0); v3f rotation = v3f(0, 0, 0); bool force_visible; - co->getAttachment(&parent_id, &bone, &position, &rotation, &force_visible); + + sao->getAttachment(&parent_id, &bone, &position, &rotation, &force_visible); if (parent_id) { ServerActiveObject *old_parent = env->getActiveObject(parent_id); - old_parent->removeAttachmentChild(co->getId()); + old_parent->removeAttachmentChild(sao->getId()); } - bone = ""; - if (!lua_isnil(L, 3)) - bone = readParam(L, 3); - position = v3f(0, 0, 0); - if (!lua_isnil(L, 4)) - position = read_v3f(L, 4); - rotation = v3f(0, 0, 0); - if (!lua_isnil(L, 5)) - rotation = read_v3f(L, 5); + bone = readParam(L, 3, ""); + position = read_v3f(L, 4); + rotation = read_v3f(L, 5); force_visible = readParam(L, 6, false); - co->setAttachment(parent->getId(), bone, position, rotation, force_visible); - parent->addAttachmentChild(co->getId()); + + sao->setAttachment(parent->getId(), bone, position, rotation, force_visible); + parent->addAttachmentChild(sao->getId()); return 0; } @@ -713,23 +614,22 @@ int ObjectRef::l_set_attach(lua_State *L) int ObjectRef::l_get_attach(lua_State *L) { GET_ENV_PTR; - ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (co == NULL) + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) return 0; - // Do it int parent_id = 0; std::string bone; v3f position = v3f(0, 0, 0); v3f rotation = v3f(0, 0, 0); bool force_visible; - co->getAttachment(&parent_id, &bone, &position, &rotation, &force_visible); - if (!parent_id) + + sao->getAttachment(&parent_id, &bone, &position, &rotation, &force_visible); + if (parent_id == 0) return 0; - ServerActiveObject *parent = env->getActiveObject(parent_id); + ServerActiveObject *parent = env->getActiveObject(parent_id); getScriptApiBase(L)->objectrefGetOrCreate(L, parent); lua_pushlstring(L, bone.c_str(), bone.size()); push_v3f(L, position); @@ -742,7 +642,6 @@ int ObjectRef::l_get_attach(lua_State *L) int ObjectRef::l_get_children(lua_State *L) { GET_ENV_PTR; - ObjectRef *ref = checkobject(L, 1); ServerActiveObject *sao = getobject(ref); if (sao == nullptr) @@ -750,6 +649,7 @@ int ObjectRef::l_get_children(lua_State *L) const std::unordered_set child_ids = sao->getAttachmentChildIds(); int i = 0; + lua_createtable(L, child_ids.size(), 0); for (const int id : child_ids) { ServerActiveObject *child = env->getActiveObject(id); @@ -763,13 +663,12 @@ int ObjectRef::l_get_children(lua_State *L) int ObjectRef::l_set_detach(lua_State *L) { GET_ENV_PTR; - ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (co == NULL) + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) return 0; - co->clearParentAttachment(); + sao->clearParentAttachment(); return 0; } @@ -778,16 +677,16 @@ int ObjectRef::l_set_properties(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (!co) + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) return 0; - ObjectProperties *prop = co->accessObjectProperties(); - if (!prop) + ObjectProperties *prop = sao->accessObjectProperties(); + if (prop == nullptr) return 0; - read_object_properties(L, 2, co, prop, getServer(L)->idef()); - co->notifyObjectPropertiesModified(); + read_object_properties(L, 2, sao, prop, getServer(L)->idef()); + sao->notifyObjectPropertiesModified(); return 0; } @@ -796,12 +695,14 @@ int ObjectRef::l_get_properties(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (co == NULL) + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) return 0; - ObjectProperties *prop = co->accessObjectProperties(); - if (!prop) + + ObjectProperties *prop = sao->accessObjectProperties(); + if (prop == nullptr) return 0; + push_object_properties(L, prop); return 1; } @@ -812,7 +713,7 @@ int ObjectRef::l_is_player(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - lua_pushboolean(L, (player != NULL)); + lua_pushboolean(L, (player != nullptr)); return 1; } @@ -821,12 +722,12 @@ int ObjectRef::l_set_nametag_attributes(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - - if (co == NULL) + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) return 0; - ObjectProperties *prop = co->accessObjectProperties(); - if (!prop) + + ObjectProperties *prop = sao->accessObjectProperties(); + if (prop == nullptr) return 0; lua_getfield(L, 2, "color"); @@ -840,7 +741,7 @@ int ObjectRef::l_set_nametag_attributes(lua_State *L) std::string nametag = getstringfield_default(L, 2, "text", ""); prop->nametag = nametag; - co->notifyObjectPropertiesModified(); + sao->notifyObjectPropertiesModified(); lua_pushboolean(L, true); return 1; } @@ -850,11 +751,11 @@ int ObjectRef::l_get_nametag_attributes(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - - if (co == NULL) + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) return 0; - ObjectProperties *prop = co->accessObjectProperties(); + + ObjectProperties *prop = sao->accessObjectProperties(); if (!prop) return 0; @@ -870,37 +771,39 @@ int ObjectRef::l_get_nametag_attributes(lua_State *L) /* LuaEntitySAO-only */ -// set_velocity(self, {x=num, y=num, z=num}) +// set_velocity(self, velocity) int ObjectRef::l_set_velocity(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); - if (co == NULL) return 0; - v3f pos = checkFloatPos(L, 2); - // Do it - co->setVelocity(pos); + LuaEntitySAO *sao = getluaobject(ref); + if (sao == nullptr) + return 0; + + v3f vel = checkFloatPos(L, 2); + + sao->setVelocity(vel); return 0; } -// add_velocity(self, {x=num, y=num, z=num}) +// add_velocity(self, velocity) int ObjectRef::l_add_velocity(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - v3f vel = checkFloatPos(L, 2); - - ServerActiveObject *obj = getobject(ref); - if (obj == nullptr) + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) return 0; - if (obj->getType() == ACTIVEOBJECT_TYPE_LUAENTITY) { - LuaEntitySAO *co = dynamic_cast(obj); - co->addVelocity(vel); - } else if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER) { - PlayerSAO *player = dynamic_cast(obj); - player->setMaxSpeedOverride(vel); - getServer(L)->SendPlayerSpeed(player->getPeerID(), vel); + v3f vel = checkFloatPos(L, 2); + + if (sao->getType() == ACTIVEOBJECT_TYPE_LUAENTITY) { + LuaEntitySAO *entitysao = dynamic_cast(sao); + entitysao->addVelocity(vel); + } else if (sao->getType() == ACTIVEOBJECT_TYPE_PLAYER) { + PlayerSAO *playersao = dynamic_cast(sao); + playersao->setMaxSpeedOverride(vel); + getServer(L)->SendPlayerSpeed(playersao->getPeerID(), vel); } return 0; @@ -911,18 +814,17 @@ int ObjectRef::l_get_velocity(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - - ServerActiveObject *obj = getobject(ref); - if (obj == nullptr) + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) return 0; - if (obj->getType() == ACTIVEOBJECT_TYPE_LUAENTITY) { - LuaEntitySAO *co = dynamic_cast(obj); - v3f v = co->getVelocity(); - pushFloatPos(L, v); + if (sao->getType() == ACTIVEOBJECT_TYPE_LUAENTITY) { + LuaEntitySAO *entitysao = dynamic_cast(sao); + v3f vel = entitysao->getVelocity(); + pushFloatPos(L, vel); return 1; - } else if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER) { - RemotePlayer *player = dynamic_cast(obj)->getPlayer(); + } else if (sao->getType() == ACTIVEOBJECT_TYPE_PLAYER) { + RemotePlayer *player = dynamic_cast(sao)->getPlayer(); push_v3f(L, player->getSpeed() / BS); return 1; } @@ -931,17 +833,18 @@ int ObjectRef::l_get_velocity(lua_State *L) return 1; } -// set_acceleration(self, {x=num, y=num, z=num}) +// set_acceleration(self, acceleration) int ObjectRef::l_set_acceleration(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); - if (co == NULL) return 0; - // pos - v3f pos = checkFloatPos(L, 2); - // Do it - co->setAcceleration(pos); + LuaEntitySAO *entitysao = getluaobject(ref); + if (entitysao == nullptr) + return 0; + + v3f acceleration = checkFloatPos(L, 2); + + entitysao->setAcceleration(acceleration); return 0; } @@ -950,59 +853,61 @@ int ObjectRef::l_get_acceleration(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); - if (co == NULL) return 0; - // Do it - v3f v = co->getAcceleration(); - pushFloatPos(L, v); + LuaEntitySAO *entitysao = getluaobject(ref); + if (entitysao == nullptr) + return 0; + + v3f acceleration = entitysao->getAcceleration(); + pushFloatPos(L, acceleration); return 1; } -// set_rotation(self, {x=num, y=num, z=num}) -// Each 'num' is in radians +// set_rotation(self, rotation) int ObjectRef::l_set_rotation(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); - if (!co) + LuaEntitySAO *entitysao = getluaobject(ref); + if (entitysao == nullptr) return 0; v3f rotation = check_v3f(L, 2) * core::RADTODEG; - co->setRotation(rotation); + + entitysao->setRotation(rotation); return 0; } // get_rotation(self) -// returns: {x=num, y=num, z=num} -// Each 'num' is in radians int ObjectRef::l_get_rotation(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); - if (!co) + LuaEntitySAO *entitysao = getluaobject(ref); + if (entitysao == nullptr) return 0; + v3f rotation = entitysao->getRotation() * core::DEGTORAD; + lua_newtable(L); - v3f rotation = co->getRotation() * core::DEGTORAD; push_v3f(L, rotation); return 1; } -// set_yaw(self, radians) +// set_yaw(self, yaw) int ObjectRef::l_set_yaw(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); + LuaEntitySAO *entitysao = getluaobject(ref); + if (entitysao == nullptr) + return 0; - if (co == NULL) return 0; if (isNaN(L, 2)) throw LuaError("ObjectRef::set_yaw: NaN value is not allowed."); float yaw = readParam(L, 2) * core::RADTODEG; - co->setRotation(v3f(0, yaw, 0)); + + entitysao->setRotation(v3f(0, yaw, 0)); return 0; } @@ -1011,11 +916,12 @@ int ObjectRef::l_get_yaw(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); - if (!co) + LuaEntitySAO *entitysao = getluaobject(ref); + if (entitysao == nullptr) return 0; - float yaw = co->getRotation().Y * core::DEGTORAD; + float yaw = entitysao->getRotation().Y * core::DEGTORAD; + lua_pushnumber(L, yaw); return 1; } @@ -1025,11 +931,13 @@ int ObjectRef::l_set_texture_mod(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); - if (co == NULL) return 0; - // Do it - std::string mod = luaL_checkstring(L, 2); - co->setTextureMod(mod); + LuaEntitySAO *entitysao = getluaobject(ref); + if (entitysao == nullptr) + return 0; + + std::string mod = readParam(L, 2); + + entitysao->setTextureMod(mod); return 0; } @@ -1038,36 +946,31 @@ int ObjectRef::l_get_texture_mod(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); - if (co == NULL) return 0; - // Do it - std::string mod = co->getTextureMod(); + LuaEntitySAO *entitysao = getluaobject(ref); + if (entitysao == nullptr) + return 0; + + std::string mod = entitysao->getTextureMod(); + lua_pushstring(L, mod.c_str()); return 1; } -// set_sprite(self, p={x=0,y=0}, num_frames=1, framelength=0.2, -// select_horiz_by_yawpitch=false) +// set_sprite(self, start_frame, num_frames, framelength, select_x_by_camera) int ObjectRef::l_set_sprite(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); - if (co == NULL) return 0; - // Do it - v2s16 p(0,0); - if (!lua_isnil(L, 2)) - p = readParam(L, 2); - int num_frames = 1; - if (!lua_isnil(L, 3)) - num_frames = lua_tonumber(L, 3); - float framelength = 0.2; - if (!lua_isnil(L, 4)) - framelength = lua_tonumber(L, 4); - bool select_horiz_by_yawpitch = false; - if (!lua_isnil(L, 5)) - select_horiz_by_yawpitch = readParam(L, 5); - co->setSprite(p, num_frames, framelength, select_horiz_by_yawpitch); + LuaEntitySAO *entitysao = getluaobject(ref); + if (entitysao == nullptr) + return 0; + + v2s16 start_frame = lua_isnil(L, 2) ? v2s16(0,0) : readParam(L, 2); + int num_frames = lua_isnil(L, 3) ? 1 : luaL_checkint(L, 3); + float framelength = lua_isnil(L, 4) ? 0.2 : lua_tonumber(L, 4); + bool select_x_by_camera = readParam(L, 5, false); + + entitysao->setSprite(start_frame, num_frames, framelength, select_x_by_camera); return 0; } @@ -1077,11 +980,13 @@ int ObjectRef::l_get_entity_name(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); + LuaEntitySAO *entitysao = getluaobject(ref); log_deprecated(L,"Deprecated call to \"get_entity_name"); - if (co == NULL) return 0; - // Do it - std::string name = co->getName(); + if (entitysao == nullptr) + return 0; + + std::string name = entitysao->getName(); + lua_pushstring(L, name.c_str()); return 1; } @@ -1091,39 +996,27 @@ int ObjectRef::l_get_luaentity(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); - if (co == NULL) return 0; - // Do it - luaentity_get(L, co->getId()); + LuaEntitySAO *entitysao = getluaobject(ref); + if (entitysao == nullptr) + return 0; + + luaentity_get(L, entitysao->getId()); return 1; } /* Player-only */ -// is_player_connected(self) -int ObjectRef::l_is_player_connected(lua_State *L) -{ - NO_MAP_LOCK_REQUIRED; - // This method was once added for a bugfix, but never documented - log_deprecated(L, "is_player_connected is undocumented and " - "will be removed in a future release"); - ObjectRef *ref = checkobject(L, 1); - RemotePlayer *player = getplayer(ref); - lua_pushboolean(L, (player != NULL && player->getPeerId() != PEER_ID_INEXISTENT)); - return 1; -} - // get_player_name(self) int ObjectRef::l_get_player_name(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) { + if (player == nullptr) { lua_pushlstring(L, "", 0); return 1; } - // Do it + lua_pushstring(L, player->getName()); return 1; } @@ -1133,13 +1026,15 @@ int ObjectRef::l_get_look_dir(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - PlayerSAO* co = getplayersao(ref); - if (co == NULL) return 0; - // Do it - float pitch = co->getRadLookPitchDep(); - float yaw = co->getRadYawDep(); + PlayerSAO* playersao = getplayersao(ref); + if (playersao == nullptr) + return 0; + + float pitch = playersao->getRadLookPitchDep(); + float yaw = playersao->getRadYawDep(); v3f v(std::cos(pitch) * std::cos(yaw), std::sin(pitch), std::cos(pitch) * std::sin(yaw)); + push_v3f(L, v); return 1; } @@ -1154,10 +1049,11 @@ int ObjectRef::l_get_look_pitch(lua_State *L) "Deprecated call to get_look_pitch, use get_look_vertical instead"); ObjectRef *ref = checkobject(L, 1); - PlayerSAO* co = getplayersao(ref); - if (co == NULL) return 0; - // Do it - lua_pushnumber(L, co->getRadLookPitchDep()); + PlayerSAO* playersao = getplayersao(ref); + if (playersao == nullptr) + return 0; + + lua_pushnumber(L, playersao->getRadLookPitchDep()); return 1; } @@ -1171,34 +1067,37 @@ int ObjectRef::l_get_look_yaw(lua_State *L) "Deprecated call to get_look_yaw, use get_look_horizontal instead"); ObjectRef *ref = checkobject(L, 1); - PlayerSAO* co = getplayersao(ref); - if (co == NULL) return 0; - // Do it - lua_pushnumber(L, co->getRadYawDep()); + PlayerSAO* playersao = getplayersao(ref); + if (playersao == nullptr) + return 0; + + lua_pushnumber(L, playersao->getRadYawDep()); return 1; } -// get_look_pitch2(self) +// get_look_vertical(self) int ObjectRef::l_get_look_vertical(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - PlayerSAO* co = getplayersao(ref); - if (co == NULL) return 0; - // Do it - lua_pushnumber(L, co->getRadLookPitch()); + PlayerSAO* playersao = getplayersao(ref); + if (playersao == nullptr) + return 0; + + lua_pushnumber(L, playersao->getRadLookPitch()); return 1; } -// get_look_yaw2(self) +// get_look_horizontal(self) int ObjectRef::l_get_look_horizontal(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - PlayerSAO* co = getplayersao(ref); - if (co == NULL) return 0; - // Do it - lua_pushnumber(L, co->getRadRotation().Y); + PlayerSAO* playersao = getplayersao(ref); + if (playersao == nullptr) + return 0; + + lua_pushnumber(L, playersao->getRadRotation().Y); return 1; } @@ -1207,11 +1106,13 @@ int ObjectRef::l_set_look_vertical(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - PlayerSAO* co = getplayersao(ref); - if (co == NULL) return 0; + PlayerSAO* playersao = getplayersao(ref); + if (playersao == nullptr) + return 0; + float pitch = readParam(L, 2) * core::RADTODEG; - // Do it - co->setLookPitchAndSend(pitch); + + playersao->setLookPitchAndSend(pitch); return 1; } @@ -1220,11 +1121,13 @@ int ObjectRef::l_set_look_horizontal(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - PlayerSAO* co = getplayersao(ref); - if (co == NULL) return 0; + PlayerSAO* playersao = getplayersao(ref); + if (playersao == nullptr) + return 0; + float yaw = readParam(L, 2) * core::RADTODEG; - // Do it - co->setPlayerYawAndSend(yaw); + + playersao->setPlayerYawAndSend(yaw); return 1; } @@ -1238,11 +1141,13 @@ int ObjectRef::l_set_look_pitch(lua_State *L) "Deprecated call to set_look_pitch, use set_look_vertical instead."); ObjectRef *ref = checkobject(L, 1); - PlayerSAO* co = getplayersao(ref); - if (co == NULL) return 0; + PlayerSAO* playersao = getplayersao(ref); + if (playersao == nullptr) + return 0; + float pitch = readParam(L, 2) * core::RADTODEG; - // Do it - co->setLookPitchAndSend(pitch); + + playersao->setLookPitchAndSend(pitch); return 1; } @@ -1256,30 +1161,32 @@ int ObjectRef::l_set_look_yaw(lua_State *L) "Deprecated call to set_look_yaw, use set_look_horizontal instead."); ObjectRef *ref = checkobject(L, 1); - PlayerSAO* co = getplayersao(ref); - if (co == NULL) return 0; + PlayerSAO* playersao = getplayersao(ref); + if (playersao == nullptr) + return 0; + float yaw = readParam(L, 2) * core::RADTODEG; - // Do it - co->setPlayerYawAndSend(yaw); + + playersao->setPlayerYawAndSend(yaw); return 1; } -// set_fov(self, degrees[, is_multiplier, transition_time]) +// set_fov(self, degrees, is_multiplier, transition_time) int ObjectRef::l_set_fov(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (!player) + if (player == nullptr) return 0; - player->setFov({ - static_cast(luaL_checknumber(L, 2)), - readParam(L, 3, false), - lua_isnumber(L, 4) ? static_cast(luaL_checknumber(L, 4)) : 0.0f - }); - getServer(L)->SendPlayerFov(player->getPeerId()); + float degrees = static_cast(luaL_checknumber(L, 2)); + bool is_multiplier = readParam(L, 3, false); + float transition_time = lua_isnumber(L, 4) ? + static_cast(luaL_checknumber(L, 4)) : 0.0f; + player->setFov({degrees, is_multiplier, transition_time}); + getServer(L)->SendPlayerFov(player->getPeerId()); return 0; } @@ -1289,14 +1196,14 @@ int ObjectRef::l_get_fov(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (!player) + if (player == nullptr) return 0; PlayerFovSpec fov_spec = player->getFov(); + lua_pushnumber(L, fov_spec.fov); lua_pushboolean(L, fov_spec.is_multiplier); lua_pushnumber(L, fov_spec.transition_time); - return 3; } @@ -1305,11 +1212,13 @@ int ObjectRef::l_set_breath(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - PlayerSAO* co = getplayersao(ref); - if (co == NULL) return 0; + PlayerSAO* playersao = getplayersao(ref); + if (playersao == nullptr) + return 0; + u16 breath = luaL_checknumber(L, 2); - co->setBreath(breath); + playersao->setBreath(breath); return 0; } @@ -1318,11 +1227,13 @@ int ObjectRef::l_get_breath(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - PlayerSAO* co = getplayersao(ref); - if (co == NULL) return 0; - // Do it - u16 breath = co->getBreath(); - lua_pushinteger (L, breath); + PlayerSAO* playersao = getplayersao(ref); + if (playersao == nullptr) + return 0; + + u16 breath = playersao->getBreath(); + + lua_pushinteger(L, breath); return 1; } @@ -1333,16 +1244,16 @@ int ObjectRef::l_set_attribute(lua_State *L) "Deprecated call to set_attribute, use MetaDataRef methods instead."); ObjectRef *ref = checkobject(L, 1); - PlayerSAO* co = getplayersao(ref); - if (co == NULL) + PlayerSAO* playersao = getplayersao(ref); + if (playersao == nullptr) return 0; std::string attr = luaL_checkstring(L, 2); if (lua_isnil(L, 3)) { - co->getMeta().removeString(attr); + playersao->getMeta().removeString(attr); } else { std::string value = luaL_checkstring(L, 3); - co->getMeta().setString(attr, value); + playersao->getMeta().setString(attr, value); } return 1; } @@ -1354,14 +1265,14 @@ int ObjectRef::l_get_attribute(lua_State *L) "Deprecated call to get_attribute, use MetaDataRef methods instead."); ObjectRef *ref = checkobject(L, 1); - PlayerSAO* co = getplayersao(ref); - if (co == NULL) + PlayerSAO* playersao = getplayersao(ref); + if (playersao == nullptr) return 0; std::string attr = luaL_checkstring(L, 2); std::string value; - if (co->getMeta().getStringToRef(attr, value)) { + if (playersao->getMeta().getStringToRef(attr, value)) { lua_pushstring(L, value.c_str()); return 1; } @@ -1374,11 +1285,11 @@ int ObjectRef::l_get_attribute(lua_State *L) int ObjectRef::l_get_meta(lua_State *L) { ObjectRef *ref = checkobject(L, 1); - PlayerSAO *co = getplayersao(ref); - if (co == NULL) + PlayerSAO *playersao = getplayersao(ref); + if (playersao == nullptr) return 0; - PlayerMetaRef::create(L, &co->getMeta()); + PlayerMetaRef::create(L, &playersao->getMeta()); return 1; } @@ -1389,7 +1300,9 @@ int ObjectRef::l_set_inventory_formspec(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) return 0; + if (player == nullptr) + return 0; + std::string formspec = luaL_checkstring(L, 2); player->inventory_formspec = formspec; @@ -1404,9 +1317,11 @@ int ObjectRef::l_get_inventory_formspec(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) return 0; + if (player == nullptr) + return 0; std::string formspec = player->inventory_formspec; + lua_pushlstring(L, formspec.c_str(), formspec.size()); return 1; } @@ -1417,7 +1332,7 @@ int ObjectRef::l_set_formspec_prepend(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; std::string formspec = luaL_checkstring(L, 2); @@ -1428,16 +1343,17 @@ int ObjectRef::l_set_formspec_prepend(lua_State *L) return 1; } -// get_formspec_prepend(self) -> formspec +// get_formspec_prepend(self) int ObjectRef::l_get_formspec_prepend(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; std::string formspec = player->formspec_prepend; + lua_pushlstring(L, formspec.c_str(), formspec.size()); return 1; } @@ -1448,7 +1364,7 @@ int ObjectRef::l_get_player_control(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) { + if (player == nullptr) { lua_pushlstring(L, "", 0); return 1; } @@ -1489,22 +1405,73 @@ int ObjectRef::l_get_player_control_bits(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) { + if (player == nullptr) { lua_pushlstring(L, "", 0); return 1; } - // Do it + lua_pushnumber(L, player->keyPressed); return 1; } -// hud_add(self, form) +// set_physics_override(self, override_table) +int ObjectRef::l_set_physics_override(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + PlayerSAO *playersao = (PlayerSAO *) getobject(ref); + if (playersao == nullptr) + return 0; + + luaL_checktype(L, 2, LUA_TTABLE); + playersao->m_physics_override_speed = getfloatfield_default( + L, 2, "speed", playersao->m_physics_override_speed); + playersao->m_physics_override_jump = getfloatfield_default( + L, 2, "jump", playersao->m_physics_override_jump); + playersao->m_physics_override_gravity = getfloatfield_default( + L, 2, "gravity", playersao->m_physics_override_gravity); + playersao->m_physics_override_sneak = getboolfield_default( + L, 2, "sneak", playersao->m_physics_override_sneak); + playersao->m_physics_override_sneak_glitch = getboolfield_default( + L, 2, "sneak_glitch", playersao->m_physics_override_sneak_glitch); + playersao->m_physics_override_new_move = getboolfield_default( + L, 2, "new_move", playersao->m_physics_override_new_move); + playersao->m_physics_override_sent = false; + return 0; +} + +// get_physics_override(self) +int ObjectRef::l_get_physics_override(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + PlayerSAO *playersao = (PlayerSAO *)getobject(ref); + if (playersao == nullptr) + return 0; + + lua_newtable(L); + lua_pushnumber(L, playersao->m_physics_override_speed); + lua_setfield(L, -2, "speed"); + lua_pushnumber(L, playersao->m_physics_override_jump); + lua_setfield(L, -2, "jump"); + lua_pushnumber(L, playersao->m_physics_override_gravity); + lua_setfield(L, -2, "gravity"); + lua_pushboolean(L, playersao->m_physics_override_sneak); + lua_setfield(L, -2, "sneak"); + lua_pushboolean(L, playersao->m_physics_override_sneak_glitch); + lua_setfield(L, -2, "sneak_glitch"); + lua_pushboolean(L, playersao->m_physics_override_new_move); + lua_setfield(L, -2, "new_move"); + return 1; +} + +// hud_add(self, hud) int ObjectRef::l_hud_add(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; HudElement *elem = new HudElement; @@ -1526,12 +1493,10 @@ int ObjectRef::l_hud_remove(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; - u32 id = -1; - if (!lua_isnil(L, 2)) - id = lua_tonumber(L, 2); + u32 id = luaL_checkint(L, 2); if (!getServer(L)->hudRemove(player, id)) return 0; @@ -1546,17 +1511,17 @@ int ObjectRef::l_hud_change(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; - u32 id = lua_isnumber(L, 2) ? lua_tonumber(L, 2) : -1; + u32 id = luaL_checkint(L, 2); - HudElement *e = player->getHud(id); - if (!e) + HudElement *elem = player->getHud(id); + if (elem == nullptr) return 0; - void *value = NULL; - HudElementStat stat = read_hud_change(L, e, &value); + void *value = nullptr; + HudElementStat stat = read_hud_change(L, elem, &value); getServer(L)->hudChange(player, id, stat, value); @@ -1570,15 +1535,16 @@ int ObjectRef::l_hud_get(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; - u32 id = lua_tonumber(L, -1); + u32 id = luaL_checkint(L, 2); - HudElement *e = player->getHud(id); - if (!e) + HudElement *elem = player->getHud(id); + if (elem == nullptr) return 0; - push_hud_element(L, e); + + push_hud_element(L, elem); return 1; } @@ -1588,7 +1554,7 @@ int ObjectRef::l_hud_set_flags(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; u32 flags = 0; @@ -1609,12 +1575,13 @@ int ObjectRef::l_hud_set_flags(lua_State *L) return 1; } +// hud_get_flags(self) int ObjectRef::l_hud_get_flags(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; lua_newtable(L); @@ -1632,7 +1599,6 @@ int ObjectRef::l_hud_get_flags(lua_State *L) lua_setfield(L, -2, "minimap"); lua_pushboolean(L, player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE); lua_setfield(L, -2, "minimap_radar"); - return 1; } @@ -1642,10 +1608,10 @@ int ObjectRef::l_hud_set_hotbar_itemcount(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; - s32 hotbar_itemcount = lua_tonumber(L, 2); + s32 hotbar_itemcount = luaL_checkint(L, 2); if (!getServer(L)->hudSetHotbarItemcount(player, hotbar_itemcount)) return 0; @@ -1660,7 +1626,7 @@ int ObjectRef::l_hud_get_hotbar_itemcount(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; lua_pushnumber(L, player->getHotbarItemcount()); @@ -1673,7 +1639,7 @@ int ObjectRef::l_hud_set_hotbar_image(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; std::string name = readParam(L, 2); @@ -1688,10 +1654,11 @@ int ObjectRef::l_hud_get_hotbar_image(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; const std::string &name = player->getHotbarImage(); + lua_pushlstring(L, name.c_str(), name.size()); return 1; } @@ -1702,7 +1669,7 @@ int ObjectRef::l_hud_set_hotbar_selected_image(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; std::string name = readParam(L, 2); @@ -1717,44 +1684,45 @@ int ObjectRef::l_hud_get_hotbar_selected_image(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; const std::string &name = player->getHotbarSelectedImage(); + lua_pushlstring(L, name.c_str(), name.size()); return 1; } -// set_sky(self, {base_color=, type=, textures=, clouds=, sky_colors={}}) +// set_sky(self, sky_parameters) int ObjectRef::l_set_sky(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (!player) + if (player == nullptr) return 0; + SkyboxParams sky_params = player->getSkyParams(); bool is_colorspec = is_color_table(L, 2); - SkyboxParams skybox_params = player->getSkyParams(); if (lua_istable(L, 2) && !is_colorspec) { lua_getfield(L, 2, "base_color"); if (!lua_isnil(L, -1)) - read_color(L, -1, &skybox_params.bgcolor); + read_color(L, -1, &sky_params.bgcolor); lua_pop(L, 1); lua_getfield(L, 2, "type"); if (!lua_isnil(L, -1)) - skybox_params.type = luaL_checkstring(L, -1); + sky_params.type = luaL_checkstring(L, -1); lua_pop(L, 1); lua_getfield(L, 2, "textures"); - skybox_params.textures.clear(); - if (lua_istable(L, -1) && skybox_params.type == "skybox") { + sky_params.textures.clear(); + if (lua_istable(L, -1) && sky_params.type == "skybox") { lua_pushnil(L); while (lua_next(L, -2) != 0) { // Key is at index -2 and value at index -1 - skybox_params.textures.emplace_back(readParam(L, -1)); + sky_params.textures.emplace_back(readParam(L, -1)); // Removes the value, but keeps the key for iteration lua_pop(L, 1); } @@ -1767,56 +1735,56 @@ int ObjectRef::l_set_sky(lua_State *L) using "regular" or "plain" skybox modes as textures aren't needed. */ - if (skybox_params.textures.size() != 6 && skybox_params.textures.size() > 0) + if (sky_params.textures.size() != 6 && sky_params.textures.size() > 0) throw LuaError("Skybox expects 6 textures!"); - skybox_params.clouds = getboolfield_default(L, 2, - "clouds", skybox_params.clouds); + sky_params.clouds = getboolfield_default(L, 2, + "clouds", sky_params.clouds); lua_getfield(L, 2, "sky_color"); if (lua_istable(L, -1)) { lua_getfield(L, -1, "day_sky"); - read_color(L, -1, &skybox_params.sky_color.day_sky); + read_color(L, -1, &sky_params.sky_color.day_sky); lua_pop(L, 1); lua_getfield(L, -1, "day_horizon"); - read_color(L, -1, &skybox_params.sky_color.day_horizon); + read_color(L, -1, &sky_params.sky_color.day_horizon); lua_pop(L, 1); lua_getfield(L, -1, "dawn_sky"); - read_color(L, -1, &skybox_params.sky_color.dawn_sky); + read_color(L, -1, &sky_params.sky_color.dawn_sky); lua_pop(L, 1); lua_getfield(L, -1, "dawn_horizon"); - read_color(L, -1, &skybox_params.sky_color.dawn_horizon); + read_color(L, -1, &sky_params.sky_color.dawn_horizon); lua_pop(L, 1); lua_getfield(L, -1, "night_sky"); - read_color(L, -1, &skybox_params.sky_color.night_sky); + read_color(L, -1, &sky_params.sky_color.night_sky); lua_pop(L, 1); lua_getfield(L, -1, "night_horizon"); - read_color(L, -1, &skybox_params.sky_color.night_horizon); + read_color(L, -1, &sky_params.sky_color.night_horizon); lua_pop(L, 1); lua_getfield(L, -1, "indoors"); - read_color(L, -1, &skybox_params.sky_color.indoors); + read_color(L, -1, &sky_params.sky_color.indoors); lua_pop(L, 1); // Prevent flickering clouds at dawn/dusk: - skybox_params.fog_sun_tint = video::SColor(255, 255, 255, 255); + sky_params.fog_sun_tint = video::SColor(255, 255, 255, 255); lua_getfield(L, -1, "fog_sun_tint"); - read_color(L, -1, &skybox_params.fog_sun_tint); + read_color(L, -1, &sky_params.fog_sun_tint); lua_pop(L, 1); - skybox_params.fog_moon_tint = video::SColor(255, 255, 255, 255); + sky_params.fog_moon_tint = video::SColor(255, 255, 255, 255); lua_getfield(L, -1, "fog_moon_tint"); - read_color(L, -1, &skybox_params.fog_moon_tint); + read_color(L, -1, &sky_params.fog_moon_tint); lua_pop(L, 1); lua_getfield(L, -1, "fog_tint_type"); if (!lua_isnil(L, -1)) - skybox_params.fog_tint_type = luaL_checkstring(L, -1); + sky_params.fog_tint_type = luaL_checkstring(L, -1); lua_pop(L, 1); // Because we need to leave the "sky_color" table. @@ -1832,14 +1800,14 @@ int ObjectRef::l_set_sky(lua_State *L) StarParams star_params = player->getStarParams(); // Prevent erroneous background colors - skybox_params.bgcolor = video::SColor(255, 255, 255, 255); - read_color(L, 2, &skybox_params.bgcolor); + sky_params.bgcolor = video::SColor(255, 255, 255, 255); + read_color(L, 2, &sky_params.bgcolor); - skybox_params.type = luaL_checkstring(L, 3); + sky_params.type = luaL_checkstring(L, 3); // Preserve old behaviour of the sun, moon and stars // when using the old set_sky call. - if (skybox_params.type == "regular") { + if (sky_params.type == "regular") { sun_params.visible = true; sun_params.sunrise_visible = true; moon_params.visible = true; @@ -1851,31 +1819,31 @@ int ObjectRef::l_set_sky(lua_State *L) star_params.visible = false; } - skybox_params.textures.clear(); + sky_params.textures.clear(); if (lua_istable(L, 4)) { lua_pushnil(L); while (lua_next(L, 4) != 0) { // Key at index -2, and value at index -1 if (lua_isstring(L, -1)) - skybox_params.textures.emplace_back(readParam(L, -1)); + sky_params.textures.emplace_back(readParam(L, -1)); else - skybox_params.textures.emplace_back(""); + sky_params.textures.emplace_back(""); // Remove the value, keep the key for the next iteration lua_pop(L, 1); } } - if (skybox_params.type == "skybox" && skybox_params.textures.size() != 6) + if (sky_params.type == "skybox" && sky_params.textures.size() != 6) throw LuaError("Skybox expects 6 textures."); - skybox_params.clouds = true; + sky_params.clouds = true; if (lua_isboolean(L, 5)) - skybox_params.clouds = readParam(L, 5); + sky_params.clouds = readParam(L, 5); getServer(L)->setSun(player, sun_params); getServer(L)->setMoon(player, moon_params); getServer(L)->setStars(player, star_params); } - getServer(L)->setSky(player, skybox_params); + getServer(L)->setSky(player, sky_params); lua_pushboolean(L, true); return 1; } @@ -1886,18 +1854,17 @@ int ObjectRef::l_get_sky(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - - if (!player) + if (player == nullptr) return 0; - SkyboxParams skybox_params; - skybox_params = player->getSkyParams(); + + SkyboxParams skybox_params = player->getSkyParams(); push_ARGB8(L, skybox_params.bgcolor); lua_pushlstring(L, skybox_params.type.c_str(), skybox_params.type.size()); lua_newtable(L); s16 i = 1; - for (const std::string& texture : skybox_params.textures) { + for (const std::string &texture : skybox_params.textures) { lua_pushlstring(L, texture.c_str(), texture.size()); lua_rawseti(L, -2, i++); } @@ -1911,11 +1878,10 @@ int ObjectRef::l_get_sky_color(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - - if (!player) + if (player == nullptr) return 0; - const SkyboxParams& skybox_params = player->getSkyParams(); + const SkyboxParams &skybox_params = player->getSkyParams(); lua_newtable(L); if (skybox_params.type == "regular") { @@ -1943,18 +1909,16 @@ int ObjectRef::l_get_sky_color(lua_State *L) return 1; } -// set_sun(self, {visible, texture=, tonemap=, sunrise=, rotation=, scale=}) +// set_sun(self, sun_parameters) int ObjectRef::l_set_sun(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (!player) - return 0; - - if (!lua_istable(L, 2)) + if (player == nullptr) return 0; + luaL_checktype(L, 2, LUA_TTABLE); SunParams sun_params = player->getSunParams(); sun_params.visible = getboolfield_default(L, 2, @@ -1981,8 +1945,9 @@ int ObjectRef::l_get_sun(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (!player) + if (player == nullptr) return 0; + const SunParams &sun_params = player->getSunParams(); lua_newtable(L); @@ -1998,21 +1963,19 @@ int ObjectRef::l_get_sun(lua_State *L) lua_setfield(L, -2, "sunrise_visible"); lua_pushnumber(L, sun_params.scale); lua_setfield(L, -2, "scale"); - return 1; } -// set_moon(self, {visible, texture=, tonemap=, sunrise=, rotation=, scale=}) +// set_moon(self, moon_parameters) int ObjectRef::l_set_moon(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (!player) - return 0; - if (!lua_istable(L, 2)) + if (player == nullptr) return 0; + luaL_checktype(L, 2, LUA_TTABLE); MoonParams moon_params = player->getMoonParams(); moon_params.visible = getboolfield_default(L, 2, @@ -2035,8 +1998,9 @@ int ObjectRef::l_get_moon(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (!player) + if (player == nullptr) return 0; + const MoonParams &moon_params = player->getMoonParams(); lua_newtable(L); @@ -2048,21 +2012,19 @@ int ObjectRef::l_get_moon(lua_State *L) lua_setfield(L, -2, "tonemap"); lua_pushnumber(L, moon_params.scale); lua_setfield(L, -2, "scale"); - return 1; } -// set_stars(self, {visible, count=, starcolor=, rotation=, scale=}) +// set_stars(self, star_parameters) int ObjectRef::l_set_stars(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (!player) - return 0; - if (!lua_istable(L, 2)) + if (player == nullptr) return 0; + luaL_checktype(L, 2, LUA_TTABLE); StarParams star_params = player->getStarParams(); star_params.visible = getboolfield_default(L, 2, @@ -2089,8 +2051,9 @@ int ObjectRef::l_get_stars(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (!player) + if (player == nullptr) return 0; + const StarParams &star_params = player->getStarParams(); lua_newtable(L); @@ -2102,21 +2065,19 @@ int ObjectRef::l_get_stars(lua_State *L) lua_setfield(L, -2, "star_color"); lua_pushnumber(L, star_params.scale); lua_setfield(L, -2, "scale"); - return 1; } -// set_clouds(self, {density=, color=, ambient=, height=, thickness=, speed=}) +// set_clouds(self, cloud_parameters) int ObjectRef::l_set_clouds(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (!player) - return 0; - if (!lua_istable(L, 2)) + if (player == nullptr) return 0; + luaL_checktype(L, 2, LUA_TTABLE); CloudParams cloud_params = player->getCloudParams(); cloud_params.density = getfloatfield_default(L, 2, "density", cloud_params.density); @@ -2152,8 +2113,9 @@ int ObjectRef::l_get_clouds(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (!player) + if (player == nullptr) return 0; + const CloudParams &cloud_params = player->getCloudParams(); lua_newtable(L); @@ -2173,25 +2135,27 @@ int ObjectRef::l_get_clouds(lua_State *L) lua_pushnumber(L, cloud_params.speed.Y); lua_setfield(L, -2, "y"); lua_setfield(L, -2, "speed"); - return 1; } -// override_day_night_ratio(self, brightness=0...1) +// override_day_night_ratio(self, ratio) int ObjectRef::l_override_day_night_ratio(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; bool do_override = false; float ratio = 0.0f; + if (!lua_isnil(L, 2)) { do_override = true; ratio = readParam(L, 2); + luaL_argcheck(L, ratio >= 0.0f && ratio <= 1.0f, 1, + "value must be between 0 and 1"); } getServer(L)->overrideDayNightRatio(player, do_override, ratio); @@ -2205,7 +2169,7 @@ int ObjectRef::l_get_day_night_ratio(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; bool do_override; @@ -2220,26 +2184,18 @@ int ObjectRef::l_get_day_night_ratio(lua_State *L) return 1; } -// set_minimap_modes(self, modes, modeindex) +// set_minimap_modes(self, modes, selected_mode) int ObjectRef::l_set_minimap_modes(lua_State *L) { NO_MAP_LOCK_REQUIRED; - - // Arg 1 should be a player ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; - // Arg 2 should be a table (of tables) - if (!lua_istable(L, 2)) { - return 0; - } - - // Arg 3 should be a number - s16 wantedmode = lua_tonumber(L, 3); - + luaL_checktype(L, 2, LUA_TTABLE); std::vector modes; + s16 selected_mode = luaL_checkint(L, 3); lua_pushnil(L); while (lua_next(L, 2) != 0) { @@ -2277,31 +2233,28 @@ int ObjectRef::l_set_minimap_modes(lua_State *L) } lua_pop(L, 1); // Remove key - getServer(L)->SendMinimapModes(player->getPeerId(), modes, wantedmode); + getServer(L)->SendMinimapModes(player->getPeerId(), modes, selected_mode); return 0; } ObjectRef::ObjectRef(ServerActiveObject *object): m_object(object) -{ - //infostream<<"ObjectRef created for id="<getId()< formspec + // get_inventory_formspec(self) static int l_get_inventory_formspec(lua_State *L); // set_formspec_prepend(self, formspec) static int l_set_formspec_prepend(lua_State *L); - // get_formspec_prepend(self) -> formspec + // get_formspec_prepend(self) static int l_get_formspec_prepend(lua_State *L); // get_player_control(self) @@ -321,7 +313,7 @@ private: // hud_get_hotbar_selected_image(self) static int l_hud_get_hotbar_selected_image(lua_State *L); - // set_sky({base_color=, type=, textures=, clouds=, sky_colors={}}) + // set_sky(self, sky_parameters) static int l_set_sky(lua_State *L); // get_sky(self) @@ -330,25 +322,25 @@ private: // get_sky_color(self) static int l_get_sky_color(lua_State* L); - // set_sun(self, {visible, texture=, tonemap=, sunrise=, rotation=, scale=}) + // set_sun(self, sun_parameters) static int l_set_sun(lua_State *L); // get_sun(self) static int l_get_sun(lua_State *L); - // set_moon(self, {visible, texture=, tonemap=, rotation, scale=}) + // set_moon(self, moon_parameters) static int l_set_moon(lua_State *L); // get_moon(self) static int l_get_moon(lua_State *L); - // set_stars(self, {visible, count=, starcolor=, rotation, scale=}) + // set_stars(self, star_parameters) static int l_set_stars(lua_State *L); // get_stars(self) static int l_get_stars(lua_State *L); - // set_clouds(self, {density=, color=, ambient=, height=, thickness=, speed=}) + // set_clouds(self, cloud_parameters) static int l_set_clouds(lua_State *L); // get_clouds(self) @@ -360,13 +352,13 @@ private: // get_day_night_ratio(self) static int l_get_day_night_ratio(lua_State *L); - // set_local_animation(self, {stand/idle}, {walk}, {dig}, {walk+dig}, frame_speed) + // set_local_animation(self, idle, walk, dig, walk_while_dig, frame_speed) static int l_set_local_animation(lua_State *L); // get_local_animation(self) static int l_get_local_animation(lua_State *L); - // set_eye_offset(self, v3f first pv, v3f third pv) + // set_eye_offset(self, firstperson, thirdperson) static int l_set_eye_offset(lua_State *L); // get_eye_offset(self) -- cgit v1.2.3 From 3356da01513860d899cde503408436f7e1918f63 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Wed, 4 Nov 2020 21:46:18 +0100 Subject: Add model[] formspec element (#10320) Formspec element to display models, written by @kilbith, rebased and tweaked. Co-authored-by: Jean-Patrick Guerrero Co-authored-by: sfan5 --- doc/lua_api.txt | 16 ++ games/devtest/mods/testformspec/LICENSE.txt | 14 ++ games/devtest/mods/testformspec/formspec.lua | 4 + .../testformspec/models/testformspec_character.b3d | Bin 0 -> 73433 bytes .../testformspec/models/testformspec_chest.obj | 79 +++++++ .../testformspec/textures/default_chest_front.png | Bin 0 -> 423 bytes .../testformspec/textures/default_chest_inside.png | Bin 0 -> 102 bytes .../testformspec/textures/default_chest_side.png | Bin 0 -> 375 bytes .../testformspec/textures/default_chest_top.png | Bin 0 -> 423 bytes .../textures/testformspec_character.png | Bin 0 -> 2754 bytes src/gui/CMakeLists.txt | 1 + src/gui/guiFormSpecMenu.cpp | 86 +++++++ src/gui/guiFormSpecMenu.h | 2 +- src/gui/guiScene.cpp | 257 +++++++++++++++++++++ src/gui/guiScene.h | 85 +++++++ util/ci/clang-format-whitelist.txt | 2 + 16 files changed, 545 insertions(+), 1 deletion(-) create mode 100644 games/devtest/mods/testformspec/LICENSE.txt create mode 100644 games/devtest/mods/testformspec/models/testformspec_character.b3d create mode 100644 games/devtest/mods/testformspec/models/testformspec_chest.obj create mode 100644 games/devtest/mods/testformspec/textures/default_chest_front.png create mode 100644 games/devtest/mods/testformspec/textures/default_chest_inside.png create mode 100644 games/devtest/mods/testformspec/textures/default_chest_side.png create mode 100644 games/devtest/mods/testformspec/textures/default_chest_top.png create mode 100644 games/devtest/mods/testformspec/textures/testformspec_character.png create mode 100644 src/gui/guiScene.cpp create mode 100644 src/gui/guiScene.h (limited to 'doc') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index daf0da3d2..38fc3066a 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2272,6 +2272,18 @@ Elements * `frame duration`: Milliseconds between each frame. `0` means the frames don't advance. * `frame start` (Optional): The index of the frame to start on. Default `1`. +### `model[,;,;;;;;;]` + +* Show a mesh model. +* `name`: Element name that can be used for styling +* `mesh`: The mesh model to use. +* `textures`: The mesh textures to use according to the mesh materials. + Texture names must be separated by commas. +* `rotation {X,Y}` (Optional): Initial rotation of the camera. + The axes are euler angles in degrees. +* `continuous` (Optional): Whether the rotation is continuous. Default `false`. +* `mouse control` (Optional): Whether the model can be controlled with the mouse. Default `true`. + ### `item_image[,;,;]` * Show an inventory image of registered item/node @@ -2842,6 +2854,10 @@ Some types may inherit styles from parent types. * font_size - Sets font size. See button `font_size` property for more information. * noclip - boolean, set to true to allow the element to exceed formspec bounds. * textcolor - color. Default white. +* model + * bgcolor - color, sets background color. + * noclip - boolean, set to true to allow the element to exceed formspec bounds. + * Default to false in formspec_version version 3 or higher * image * noclip - boolean, set to true to allow the element to exceed formspec bounds. * Default to false in formspec_version version 3 or higher diff --git a/games/devtest/mods/testformspec/LICENSE.txt b/games/devtest/mods/testformspec/LICENSE.txt new file mode 100644 index 000000000..07696cc30 --- /dev/null +++ b/games/devtest/mods/testformspec/LICENSE.txt @@ -0,0 +1,14 @@ +License of media files +---------------------- +Content imported from minetest_game. + + +BlockMen (CC BY-SA 3.0) + default_chest_front.png + default_chest_lock.png + default_chest_side.png + default_chest_top.png + +stujones11 (CC BY-SA 3.0) +An0n3m0us (CC BY-SA 3.0) + testformspec_character.b3d diff --git a/games/devtest/mods/testformspec/formspec.lua b/games/devtest/mods/testformspec/formspec.lua index 87a05fc96..5495896ce 100644 --- a/games/devtest/mods/testformspec/formspec.lua +++ b/games/devtest/mods/testformspec/formspec.lua @@ -327,6 +327,10 @@ Number] animated_image[3,4.25;1,1;;testformspec_animation.png;4;0;3] animated_image[5.5,0.5;5,2;;testformspec_animation.png;4;100] animated_image[5.5,2.75;5,2;;testformspec_animation.jpg;4;100] + + style[m1;bgcolor=black] + model[0.5,6;4,4;m1;testformspec_character.b3d;testformspec_character.png] + model[5,6;4,4;m2;testformspec_chest.obj;default_chest_top.png,default_chest_top.png,default_chest_side.png,default_chest_side.png,default_chest_front.png,default_chest_inside.png;30,1;true;true] ]], -- Scroll containers diff --git a/games/devtest/mods/testformspec/models/testformspec_character.b3d b/games/devtest/mods/testformspec/models/testformspec_character.b3d new file mode 100644 index 000000000..8edbaf637 Binary files /dev/null and b/games/devtest/mods/testformspec/models/testformspec_character.b3d differ diff --git a/games/devtest/mods/testformspec/models/testformspec_chest.obj b/games/devtest/mods/testformspec/models/testformspec_chest.obj new file mode 100644 index 000000000..72ba175a0 --- /dev/null +++ b/games/devtest/mods/testformspec/models/testformspec_chest.obj @@ -0,0 +1,79 @@ +# Blender v2.78 (sub 0) OBJ File: 'chest-open.blend' +# www.blender.org +o Top_Cube.002_None_Top_Cube.002_None_bottom +v -0.500000 0.408471 0.720970 +v -0.500000 1.115578 0.013863 +v -0.500000 0.894607 -0.207108 +v -0.500000 0.187501 0.499999 +v 0.500000 1.115578 0.013863 +v 0.500000 0.408471 0.720970 +v 0.500000 0.187501 0.499999 +v 0.500000 0.894607 -0.207108 +v -0.500000 0.187500 -0.500000 +v -0.500000 -0.500000 -0.500000 +v -0.500000 -0.500000 0.500000 +v 0.500000 0.187500 -0.500000 +v 0.500000 -0.500000 0.500000 +v 0.500000 -0.500000 -0.500000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 0.0000 1.0000 +vt 1.0000 1.0000 +vt 1.0000 0.6875 +vt 0.0000 0.6875 +vt 1.0000 1.0000 +vt 0.0000 0.6875 +vt 1.0000 0.6875 +vt 1.0000 0.6875 +vt 1.0000 0.0000 +vt 0.0000 0.0000 +vt 1.0000 0.6875 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 1.0000 0.6875 +vt 1.0000 0.0000 +vt 0.0000 1.0000 +vt 0.0000 0.6875 +vt 0.0000 0.6875 +vt 0.0000 0.0000 +vt 1.0000 0.5000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.5000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vn 0.0000 0.7071 0.7071 +vn -0.0000 -1.0000 -0.0000 +vn -1.0000 0.0000 0.0000 +vn 1.0000 0.0000 -0.0000 +vn 0.0000 -0.7071 0.7071 +vn 0.0000 0.0000 1.0000 +vn -0.0000 0.7071 -0.7071 +vn -0.0000 0.0000 -1.0000 +vn -0.0000 -0.7071 -0.7071 +vn -0.0000 1.0000 -0.0000 +g Top_Cube.002_None_Top_Cube.002_None_bottom_Top_Cube.002_None_Top_Cube.002_None_bottom_Top +s off +f 6/1/1 5/2/1 2/3/1 1/4/1 +g Top_Cube.002_None_Top_Cube.002_None_bottom_Top_Cube.002_None_Top_Cube.002_None_bottom_Bottom +f 11/5/2 10/6/2 14/7/2 13/8/2 +g Top_Cube.002_None_Top_Cube.002_None_bottom_Top_Cube.002_None_Top_Cube.002_None_bottom_Right-Left +f 1/9/3 2/10/3 3/11/3 4/12/3 +f 5/13/4 6/1/4 7/14/4 8/15/4 +f 4/12/3 9/16/3 10/17/3 11/18/3 +f 12/19/4 7/14/4 13/8/4 14/20/4 +g Top_Cube.002_None_Top_Cube.002_None_bottom_Top_Cube.002_None_Top_Cube.002_None_bottom_Back +f 6/21/5 1/9/5 4/12/5 7/22/5 +f 7/22/6 4/12/6 11/18/6 13/23/6 +g Top_Cube.002_None_Top_Cube.002_None_bottom_Top_Cube.002_None_Top_Cube.002_None_bottom_Front +f 2/10/7 5/24/7 8/25/7 3/11/7 +f 9/16/8 12/26/8 14/27/8 10/17/8 +g Top_Cube.002_None_Top_Cube.002_None_bottom_Top_Cube.002_None_Top_Cube.002_None_bottom_Inside +f 4/28/9 3/29/9 8/30/9 7/31/9 +f 7/31/10 12/32/10 9/33/10 4/28/10 diff --git a/games/devtest/mods/testformspec/textures/default_chest_front.png b/games/devtest/mods/testformspec/textures/default_chest_front.png new file mode 100644 index 000000000..85227d8fd Binary files /dev/null and b/games/devtest/mods/testformspec/textures/default_chest_front.png differ diff --git a/games/devtest/mods/testformspec/textures/default_chest_inside.png b/games/devtest/mods/testformspec/textures/default_chest_inside.png new file mode 100644 index 000000000..5f7b6b132 Binary files /dev/null and b/games/devtest/mods/testformspec/textures/default_chest_inside.png differ diff --git a/games/devtest/mods/testformspec/textures/default_chest_side.png b/games/devtest/mods/testformspec/textures/default_chest_side.png new file mode 100644 index 000000000..44a65a43d Binary files /dev/null and b/games/devtest/mods/testformspec/textures/default_chest_side.png differ diff --git a/games/devtest/mods/testformspec/textures/default_chest_top.png b/games/devtest/mods/testformspec/textures/default_chest_top.png new file mode 100644 index 000000000..f4a92ee07 Binary files /dev/null and b/games/devtest/mods/testformspec/textures/default_chest_top.png differ diff --git a/games/devtest/mods/testformspec/textures/testformspec_character.png b/games/devtest/mods/testformspec/textures/testformspec_character.png new file mode 100644 index 000000000..05021781e Binary files /dev/null and b/games/devtest/mods/testformspec/textures/testformspec_character.png differ diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 147f445f4..5305e7ad3 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -15,6 +15,7 @@ set(gui_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/guiKeyChangeMenu.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiPasswordChange.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiPathSelectMenu.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/guiScene.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiScrollBar.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiScrollContainer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiSkin.cpp diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 7e3ad3b15..039b28e79 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -65,6 +65,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "guiScrollContainer.h" #include "intlGUIEditBox.h" #include "guiHyperText.h" +#include "guiScene.h" #define MY_CHECKPOS(a,b) \ if (v_pos.size() != 2) { \ @@ -2695,6 +2696,86 @@ void GUIFormSpecMenu::parseSetFocus(const std::string &element) << "'" << std::endl; } +void GUIFormSpecMenu::parseModel(parserData *data, const std::string &element) +{ + std::vector parts = split(element, ';'); + + if (parts.size() < 5 || (parts.size() > 8 && + m_formspec_version <= FORMSPEC_API_VERSION)) { + errorstream << "Invalid model element (" << parts.size() << "): '" << element + << "'" << std::endl; + return; + } + + // Avoid length checks by resizing + if (parts.size() < 8) + parts.resize(8); + + std::vector v_pos = split(parts[0], ','); + std::vector v_geom = split(parts[1], ','); + std::string name = unescape_string(parts[2]); + std::string meshstr = unescape_string(parts[3]); + std::vector textures = split(parts[4], ','); + std::vector vec_rot = split(parts[5], ','); + bool inf_rotation = is_yes(parts[6]); + bool mousectrl = is_yes(parts[7]) || parts[7].empty(); // default true + + MY_CHECKPOS("model", 0); + MY_CHECKGEOM("model", 1); + + v2s32 pos; + v2s32 geom; + + if (data->real_coordinates) { + pos = getRealCoordinateBasePos(v_pos); + geom = getRealCoordinateGeometry(v_geom); + } else { + pos = getElementBasePos(&v_pos); + geom.X = stof(v_geom[0]) * (float)imgsize.X; + geom.Y = stof(v_geom[1]) * (float)imgsize.Y; + } + + if (!data->explicit_size) + warningstream << "invalid use of model without a size[] element" << std::endl; + + scene::IAnimatedMesh *mesh = m_client->getMesh(meshstr); + + if (!mesh) { + errorstream << "Invalid model element: Unable to load mesh:" + << std::endl << "\t" << meshstr << std::endl; + return; + } + + FieldSpec spec( + name, + L"", + L"", + 258 + m_fields.size() + ); + + core::rect rect(pos, pos + geom); + + GUIScene *e = new GUIScene(Environment, RenderingEngine::get_scene_manager(), + data->current_parent, rect, spec.fid); + + auto meshnode = e->setMesh(mesh); + + for (u32 i = 0; i < textures.size() && i < meshnode->getMaterialCount(); ++i) + e->setTexture(i, m_tsrc->getTexture(textures[i])); + + if (vec_rot.size() >= 2) + e->setRotation(v2f(stof(vec_rot[0]), stof(vec_rot[1]))); + + e->enableContinuousRotation(inf_rotation); + e->enableMouseControl(mousectrl); + + auto style = getStyleForElement("model", spec.fname); + e->setStyles(style); + e->drop(); + + m_fields.push_back(spec); +} + void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element) { //some prechecks @@ -2891,6 +2972,11 @@ void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element) return; } + if (type == "model") { + parseModel(data, description); + return; + } + // Ignore others infostream << "Unknown DrawSpec: type=" << type << ", data=\"" << description << "\"" << std::endl; diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h index 613acaa04..c5d662a69 100644 --- a/src/gui/guiFormSpecMenu.h +++ b/src/gui/guiFormSpecMenu.h @@ -38,7 +38,6 @@ with this program; if not, write to the Free Software Foundation, Inc., class InventoryManager; class ISimpleTextureSource; class Client; -class TexturePool; class GUIScrollContainer; typedef enum { @@ -444,6 +443,7 @@ private: void parseAnchor(parserData *data, const std::string &element); bool parseStyle(parserData *data, const std::string &element, bool style_type); void parseSetFocus(const std::string &element); + void parseModel(parserData *data, const std::string &element); void tryClose(); diff --git a/src/gui/guiScene.cpp b/src/gui/guiScene.cpp new file mode 100644 index 000000000..08f119e07 --- /dev/null +++ b/src/gui/guiScene.cpp @@ -0,0 +1,257 @@ +/* +Minetest +Copyright (C) 2020 Jean-Patrick Guerrero + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "guiScene.h" + +#include +#include +#include +#include "porting.h" + +GUIScene::GUIScene(gui::IGUIEnvironment *env, scene::ISceneManager *smgr, + gui::IGUIElement *parent, core::recti rect, s32 id) + : IGUIElement(gui::EGUIET_ELEMENT, env, parent, id, rect) +{ + m_driver = env->getVideoDriver(); + m_smgr = smgr->createNewSceneManager(false); + + m_cam = m_smgr->addCameraSceneNode(0, v3f(0.f, 0.f, -100.f), v3f(0.f)); + m_cam->setFOV(30.f * core::DEGTORAD); + + scene::ILightSceneNode *light = m_smgr->addLightSceneNode(m_cam); + light->setRadius(1000.f); + + m_smgr->getParameters()->setAttribute(scene::ALLOW_ZWRITE_ON_TRANSPARENT, true); +} + +GUIScene::~GUIScene() +{ + setMesh(nullptr); + + m_smgr->drop(); +} + +scene::IAnimatedMeshSceneNode *GUIScene::setMesh(scene::IAnimatedMesh *mesh) +{ + if (m_mesh) { + m_mesh->remove(); + m_mesh = nullptr; + } + + if (!mesh) + return nullptr; + + m_mesh = m_smgr->addAnimatedMeshSceneNode(mesh); + m_mesh->setPosition(-m_mesh->getBoundingBox().getCenter()); + m_mesh->animateJoints(); + return m_mesh; +} + +void GUIScene::setTexture(u32 idx, video::ITexture *texture) +{ + video::SMaterial &material = m_mesh->getMaterial(idx); + material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; + material.MaterialTypeParam = 0.5f; + material.TextureLayer[0].Texture = texture; + material.setFlag(video::EMF_LIGHTING, false); + material.setFlag(video::EMF_FOG_ENABLE, true); + material.setFlag(video::EMF_BILINEAR_FILTER, false); + material.setFlag(video::EMF_BACK_FACE_CULLING, false); +} + +void GUIScene::draw() +{ + // Control rotation speed based on time + u64 new_time = porting::getTimeMs(); + u64 dtime_ms = 0; + if (m_last_time != 0) + dtime_ms = porting::getDeltaMs(m_last_time, new_time); + m_last_time = new_time; + + core::rect oldViewPort = m_driver->getViewPort(); + m_driver->setViewPort(getAbsoluteClippingRect()); + core::recti borderRect = Environment->getRootGUIElement()->getAbsoluteClippingRect(); + + if (m_bgcolor != 0) { + Environment->getSkin()->draw3DSunkenPane( + this, m_bgcolor, false, true, borderRect, 0); + } + + core::dimension2d size = getAbsoluteClippingRect().getSize(); + m_smgr->getActiveCamera()->setAspectRatio((f32)size.Width / (f32)size.Height); + + if (!m_target) { + updateCamera(m_smgr->addEmptySceneNode()); + rotateCamera(v3f(0.f)); + m_cam->bindTargetAndRotation(true); + } + + cameraLoop(); + + // Continuous rotation + if (m_inf_rot) + rotateCamera(v3f(0.f, -0.03f * (float)dtime_ms, 0.f)); + + m_smgr->drawAll(); + + if (m_initial_rotation && m_mesh) { + rotateCamera(v3f(m_custom_rot.X, m_custom_rot.Y, 0.f)); + calcOptimalDistance(); + + m_initial_rotation = false; + } + + m_driver->setViewPort(oldViewPort); +} + +bool GUIScene::OnEvent(const SEvent &event) +{ + if (m_mouse_ctrl && event.EventType == EET_MOUSE_INPUT_EVENT) { + if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) { + m_last_pos = v2f((f32)event.MouseInput.X, (f32)event.MouseInput.Y); + return true; + } else if (event.MouseInput.Event == EMIE_MOUSE_MOVED) { + if (event.MouseInput.isLeftPressed()) { + m_curr_pos = v2f((f32)event.MouseInput.X, (f32)event.MouseInput.Y); + + rotateCamera(v3f( + m_last_pos.Y - m_curr_pos.Y, + m_curr_pos.X - m_last_pos.X, 0.f)); + + m_last_pos = m_curr_pos; + return true; + } + } + } + + return gui::IGUIElement::OnEvent(event); +} + +void GUIScene::setStyles(const std::array &styles) +{ + StyleSpec::State state = StyleSpec::STATE_DEFAULT; + StyleSpec style = StyleSpec::getStyleFromStatePropagation(styles, state); + + setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + setBackgroundColor(style.getColor(StyleSpec::BGCOLOR, m_bgcolor)); +} + +/* Camera control functions */ + +inline void GUIScene::calcOptimalDistance() +{ + core::aabbox3df box = m_mesh->getBoundingBox(); + f32 width = box.MaxEdge.X - box.MinEdge.X; + f32 height = box.MaxEdge.Y - box.MinEdge.Y; + f32 depth = box.MaxEdge.Z - box.MinEdge.Z; + f32 max_width = width > depth ? width : depth; + + const scene::SViewFrustum *f = m_cam->getViewFrustum(); + f32 cam_far = m_cam->getFarValue(); + f32 far_width = core::line3df(f->getFarLeftUp(), f->getFarRightUp()).getLength(); + f32 far_height = core::line3df(f->getFarLeftUp(), f->getFarLeftDown()).getLength(); + + core::recti rect = getAbsolutePosition(); + f32 zoomX = rect.getWidth() / max_width; + f32 zoomY = rect.getHeight() / height; + f32 dist; + + if (zoomX < zoomY) + dist = (max_width / (far_width / cam_far)) + (0.5f * max_width); + else + dist = (height / (far_height / cam_far)) + (0.5f * max_width); + + m_cam_distance = dist; + m_update_cam = true; +} + +void GUIScene::updateCamera(scene::ISceneNode *target) +{ + m_target = target; + updateTargetPos(); + + m_last_target_pos = m_target_pos; + updateCameraPos(); + + m_update_cam = true; +} + +void GUIScene::updateTargetPos() +{ + m_last_target_pos = m_target_pos; + m_target->updateAbsolutePosition(); + m_target_pos = m_target->getAbsolutePosition(); +} + +void GUIScene::setCameraRotation(v3f rot) +{ + correctBounds(rot); + + core::matrix4 mat; + mat.setRotationDegrees(rot); + + m_cam_pos = v3f(0.f, 0.f, m_cam_distance); + mat.rotateVect(m_cam_pos); + + m_cam_pos += m_target_pos; + m_cam->setPosition(m_cam_pos); + m_update_cam = false; +} + +bool GUIScene::correctBounds(v3f &rot) +{ + const float ROTATION_MAX_1 = 60.0f; + const float ROTATION_MAX_2 = 300.0f; + + // Limit and correct the rotation when needed + if (rot.X < 90.f) { + if (rot.X > ROTATION_MAX_1) { + rot.X = ROTATION_MAX_1; + return true; + } + } else if (rot.X < ROTATION_MAX_2) { + rot.X = ROTATION_MAX_2; + return true; + } + + // Not modified + return false; +} + +void GUIScene::cameraLoop() +{ + updateCameraPos(); + updateTargetPos(); + + if (m_target_pos != m_last_target_pos) + m_update_cam = true; + + if (m_update_cam) { + m_cam_pos = m_target_pos + (m_cam_pos - m_target_pos).normalize() * m_cam_distance; + + v3f rot = getCameraRotation(); + if (correctBounds(rot)) + setCameraRotation(rot); + + m_cam->setPosition(m_cam_pos); + m_cam->setTarget(m_target_pos); + + m_update_cam = false; + } +} diff --git a/src/gui/guiScene.h b/src/gui/guiScene.h new file mode 100644 index 000000000..707e6f66a --- /dev/null +++ b/src/gui/guiScene.h @@ -0,0 +1,85 @@ +/* +Minetest +Copyright (C) 2020 Jean-Patrick Guerrero + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "irrlichttypes_extrabloated.h" +#include "ICameraSceneNode.h" +#include "StyleSpec.h" + +using namespace irr; + +class GUIScene : public gui::IGUIElement +{ +public: + GUIScene(gui::IGUIEnvironment *env, scene::ISceneManager *smgr, + gui::IGUIElement *parent, core::recti rect, s32 id = -1); + + ~GUIScene(); + + scene::IAnimatedMeshSceneNode *setMesh(scene::IAnimatedMesh *mesh = nullptr); + void setTexture(u32 idx, video::ITexture *texture); + void setBackgroundColor(const video::SColor &color) noexcept { m_bgcolor = color; }; + void enableMouseControl(bool enable) noexcept { m_mouse_ctrl = enable; }; + void setRotation(v2f rot) noexcept { m_custom_rot = rot; }; + void enableContinuousRotation(bool enable) noexcept { m_inf_rot = enable; }; + void setStyles(const std::array &styles); + + virtual void draw(); + virtual bool OnEvent(const SEvent &event); + +private: + void calcOptimalDistance(); + void updateTargetPos(); + void updateCamera(scene::ISceneNode *target); + void setCameraRotation(v3f rot); + /// @return true indicates that the rotation was corrected + bool correctBounds(v3f &rot); + void cameraLoop(); + + void updateCameraPos() { m_cam_pos = m_cam->getPosition(); }; + v3f getCameraRotation() const { return (m_cam_pos - m_target_pos).getHorizontalAngle(); }; + void rotateCamera(const v3f &delta) { setCameraRotation(getCameraRotation() + delta); }; + + scene::ISceneManager *m_smgr; + video::IVideoDriver *m_driver; + scene::ICameraSceneNode *m_cam; + scene::ISceneNode *m_target = nullptr; + scene::IAnimatedMeshSceneNode *m_mesh = nullptr; + + f32 m_cam_distance = 50.f; + + u64 m_last_time = 0; + + v3f m_cam_pos; + v3f m_target_pos; + v3f m_last_target_pos; + // Cursor positions + v2f m_curr_pos; + v2f m_last_pos; + // Initial rotation + v2f m_custom_rot; + + bool m_mouse_ctrl = true; + bool m_update_cam = false; + bool m_inf_rot = false; + bool m_initial_rotation = true; + + video::SColor m_bgcolor = 0; +}; diff --git a/util/ci/clang-format-whitelist.txt b/util/ci/clang-format-whitelist.txt index 3334257ae..75d99f4cd 100644 --- a/util/ci/clang-format-whitelist.txt +++ b/util/ci/clang-format-whitelist.txt @@ -183,6 +183,8 @@ src/gui/guiMainMenu.h src/gui/guiPasswordChange.cpp src/gui/guiPathSelectMenu.cpp src/gui/guiPathSelectMenu.h +src/gui/guiScene.cpp +src/gui/guiScene.h src/gui/guiScrollBar.cpp src/gui/guiSkin.cpp src/gui/guiSkin.h -- cgit v1.2.3 From 7589cbe086a5558eea018e1e3d25e388b26d51e8 Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Mon, 9 Nov 2020 10:55:15 +0000 Subject: Fix object interaction distance not being checked (#10547) --- doc/lua_api.txt | 4 ++-- src/network/serverpackethandler.cpp | 13 +++++++------ 2 files changed, 9 insertions(+), 8 deletions(-) (limited to 'doc') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 38fc3066a..985af2f6e 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -6189,9 +6189,9 @@ object you are working with still exists. * `bone`: string * `position`: `{x=num, y=num, z=num}` (relative) * `rotation`: `{x=num, y=num, z=num}` = Rotation on each axis, in degrees - * `forced_visible`: Boolean to control whether the attached entity + * `forced_visible`: Boolean to control whether the attached entity should appear in first person. -* `get_attach()`: returns parent, bone, position, rotation, forced_visible, +* `get_attach()`: returns parent, bone, position, rotation, forced_visible, or nil if it isn't attached. * `get_children()`: returns a list of ObjectRefs that are attached to the object. diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index 0bd09e637..95e7c548e 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -1050,12 +1050,13 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) } float d = playersao->getEyePosition().getDistanceFrom(target_pos); - if (!checkInteractDistance(player, d, pointed.dump()) - && pointed.type == POINTEDTHING_NODE) { - // Re-send block to revert change on client-side - RemoteClient *client = getClient(peer_id); - v3s16 blockpos = getNodeBlockPos(pointed.node_undersurface); - client->SetBlockNotSent(blockpos); + if (!checkInteractDistance(player, d, pointed.dump())) { + if (pointed.type == POINTEDTHING_NODE) { + // Re-send block to revert change on client-side + RemoteClient *client = getClient(peer_id); + v3s16 blockpos = getNodeBlockPos(pointed.node_undersurface); + client->SetBlockNotSent(blockpos); + } return; } } -- cgit v1.2.3 From be3fe161fc831c9e6da1357dc908ed4a7681c46c Mon Sep 17 00:00:00 2001 From: DS Date: Tue, 10 Nov 2020 21:03:10 +0100 Subject: Do not set a default for description in itemdef table (#10559) * Do not set a default for description in itemdef table * improve documentation --- builtin/game/register.lua | 4 +--- doc/lua_api.txt | 21 ++++++++++++++++----- 2 files changed, 17 insertions(+), 8 deletions(-) (limited to 'doc') diff --git a/builtin/game/register.lua b/builtin/game/register.lua index f00b76494..1f94a9dca 100644 --- a/builtin/game/register.lua +++ b/builtin/game/register.lua @@ -118,11 +118,9 @@ function core.register_item(name, itemdef) end itemdef.name = name - -- default description to item name - itemdef.description = itemdef.description or name -- default short_description to first line of description itemdef.short_description = itemdef.short_description or - itemdef.description:gsub("\n.*","") + (itemdef.description or ""):gsub("\n.*","") -- Apply defaults and add to registered_* table if itemdef.type == "node" then diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 985af2f6e..ef283f0c1 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2023,10 +2023,10 @@ Item metadata only contains a key-value store. Some of the values in the key-value store are handled specially: -* `description`: Set the item stack's description. Defaults to - `idef.description`. -* `short_description`: Set the item stack's short description. Defaults - to `idef.short_description`. +* `description`: Set the item stack's description. + See also: `get_description` in [`ItemStack`] +* `short_description`: Set the item stack's short description. + See also: `get_short_description` in [`ItemStack`] * `color`: A `ColorString`, which sets the stack's color. * `palette_index`: If the item has a palette, this is used to get the current color from the palette. @@ -5983,8 +5983,18 @@ an itemstring, a table or `nil`. stack). * `set_metadata(metadata)`: (DEPRECATED) Returns true. * `get_description()`: returns the description shown in inventory list tooltips. + * The engine uses the same as this function for item descriptions. + * Fields for finding the description, in order: + * `description` in item metadata (See [Item Metadata].) + * `description` in item definition + * item name * `get_short_description()`: returns the short description. * Unlike the description, this does not include new lines. + * The engine uses the same as this function for short item descriptions. + * Fields for finding the short description, in order: + * `short_description` in item metadata (See [Item Metadata].) + * `short_description` in item definition + * first line of the description (See `get_description()`.) * `clear()`: removes all items from the stack, making it empty. * `replace(item)`: replace the contents of this stack. * `item` can also be an itemstring or table. @@ -7085,11 +7095,12 @@ Used by `minetest.register_node`, `minetest.register_craftitem`, and { description = "Steel Axe", -- Can contain new lines. "\n" has to be used as new line character. - -- Defaults to the item's name. + -- See also: `get_description` in [`ItemStack`] short_description = "Steel Axe", -- Must not contain new lines. -- Defaults to the first line of description. + -- See also: `get_short_description` in [`ItemStack`] groups = {}, -- key = name, value = rating; rating = 1..3. -- cgit v1.2.3 From 78273027bf4bbee7488c76c6d65f85381ec7c0ba Mon Sep 17 00:00:00 2001 From: Pierre-Yves Rollo Date: Wed, 16 Sep 2020 17:10:17 +0200 Subject: Add sound to press event of some formspecs elements (#10402) --- doc/lua_api.txt | 5 +++++ src/client/game.cpp | 12 ++++++------ src/gui/StyleSpec.h | 3 +++ src/gui/guiEngine.cpp | 1 + src/gui/guiFormSpecMenu.cpp | 40 +++++++++++++++++++++++++++++++++++++--- src/gui/guiFormSpecMenu.h | 6 +++++- 6 files changed, 57 insertions(+), 10 deletions(-) (limited to 'doc') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index ef283f0c1..27f871618 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2843,11 +2843,14 @@ Some types may inherit styles from parent types. * noclip - boolean, set to true to allow the element to exceed formspec bounds. * padding - rect, adds space between the edges of the button and the content. This value is relative to bgimg_middle. + * sound - a sound to be played when clicked. * textcolor - color, default white. * checkbox * noclip - boolean, set to true to allow the element to exceed formspec bounds. + * sound - a sound to be played when clicked. * dropdown * noclip - boolean, set to true to allow the element to exceed formspec bounds. + * sound - a sound to be played when clicked. * field, pwdfield, textarea * border - set to false to hide the textbox background and border. Default true. * font - Sets font type. See button `font` property for more information. @@ -2874,10 +2877,12 @@ Some types may inherit styles from parent types. * fgimg_pressed - image when pressed. Defaults to fgimg when not provided. * This is deprecated, use states instead. * NOTE: The parameters of any given image_button will take precedence over fgimg/fgimg_pressed + * sound - a sound to be played when clicked. * scrollbar * noclip - boolean, set to true to allow the element to exceed formspec bounds. * tabheader * noclip - boolean, set to true to allow the element to exceed formspec bounds. + * sound - a sound to be played when clicked. * textcolor - color. Default white. * table, textlist * font - Sets font type. See button `font` property for more information. diff --git a/src/client/game.cpp b/src/client/game.cpp index 5c38e027d..b7bb0a330 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -2071,7 +2071,7 @@ void Game::openInventory() TextDest *txt_dst = new TextDestPlayerInventory(client); auto *&formspec = m_game_ui->updateFormspec(""); GUIFormSpecMenu::create(formspec, client, &input->joystick, fs_src, - txt_dst, client->getFormspecPrepend()); + txt_dst, client->getFormspecPrepend(), sound); formspec->setFormSpec(fs_src->getForm(), inventoryloc); } @@ -2603,7 +2603,7 @@ void Game::handleClientEvent_ShowFormSpec(ClientEvent *event, CameraOrientation auto *&formspec = m_game_ui->updateFormspec(*(event->show_formspec.formname)); GUIFormSpecMenu::create(formspec, client, &input->joystick, - fs_src, txt_dst, client->getFormspecPrepend()); + fs_src, txt_dst, client->getFormspecPrepend(), sound); } delete event->show_formspec.formspec; @@ -2616,7 +2616,7 @@ void Game::handleClientEvent_ShowLocalFormSpec(ClientEvent *event, CameraOrienta LocalFormspecHandler *txt_dst = new LocalFormspecHandler(*event->show_formspec.formname, client); GUIFormSpecMenu::create(m_game_ui->getFormspecGUI(), client, &input->joystick, - fs_src, txt_dst, client->getFormspecPrepend()); + fs_src, txt_dst, client->getFormspecPrepend(), sound); delete event->show_formspec.formspec; delete event->show_formspec.formname; @@ -3336,7 +3336,7 @@ bool Game::nodePlacement(const ItemDefinition &selected_def, auto *&formspec = m_game_ui->updateFormspec(""); GUIFormSpecMenu::create(formspec, client, &input->joystick, fs_src, - txt_dst, client->getFormspecPrepend()); + txt_dst, client->getFormspecPrepend(), sound); formspec->setFormSpec(meta->getString("formspec"), inventoryloc); return false; @@ -4108,7 +4108,7 @@ void Game::showDeathFormspec() auto *&formspec = m_game_ui->getFormspecGUI(); GUIFormSpecMenu::create(formspec, client, &input->joystick, - fs_src, txt_dst, client->getFormspecPrepend()); + fs_src, txt_dst, client->getFormspecPrepend(), sound); formspec->setFocus("btn_respawn"); } @@ -4242,7 +4242,7 @@ void Game::showPauseMenu() auto *&formspec = m_game_ui->getFormspecGUI(); GUIFormSpecMenu::create(formspec, client, &input->joystick, - fs_src, txt_dst, client->getFormspecPrepend()); + fs_src, txt_dst, client->getFormspecPrepend(), sound); formspec->setFocus("btn_continue"); formspec->doPause = true; } diff --git a/src/gui/StyleSpec.h b/src/gui/StyleSpec.h index 36ad51a89..f2844ce28 100644 --- a/src/gui/StyleSpec.h +++ b/src/gui/StyleSpec.h @@ -54,6 +54,7 @@ public: COLORS, BORDERCOLORS, BORDERWIDTHS, + SOUND, NUM_PROPERTIES, NONE }; @@ -116,6 +117,8 @@ public: return BORDERCOLORS; } else if (name == "borderwidths") { return BORDERWIDTHS; + } else if (name == "sound") { + return SOUND; } else { return NONE; } diff --git a/src/gui/guiEngine.cpp b/src/gui/guiEngine.cpp index 4a13f0b11..c5ad5c323 100644 --- a/src/gui/guiEngine.cpp +++ b/src/gui/guiEngine.cpp @@ -170,6 +170,7 @@ GUIEngine::GUIEngine(JoystickController *joystick, m_menumanager, NULL /* &client */, m_texture_source, + m_sound_manager, m_formspecgui, m_buttonhandler, "", diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 039b28e79..632b15992 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -48,6 +48,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "client/client.h" #include "client/fontengine.h" +#include "client/sound.h" #include "util/hex.h" #include "util/numeric.h" #include "util/string.h" // for parseColorString() @@ -95,11 +96,13 @@ inline u32 clamp_u8(s32 value) GUIFormSpecMenu::GUIFormSpecMenu(JoystickController *joystick, gui::IGUIElement *parent, s32 id, IMenuManager *menumgr, - Client *client, ISimpleTextureSource *tsrc, IFormSource *fsrc, TextDest *tdst, + Client *client, ISimpleTextureSource *tsrc, ISoundManager *sound_manager, + IFormSource *fsrc, TextDest *tdst, const std::string &formspecPrepend, bool remap_dbl_click): GUIModalMenu(RenderingEngine::get_gui_env(), parent, id, menumgr, remap_dbl_click), m_invmgr(client), m_tsrc(tsrc), + m_sound_manager(sound_manager), m_client(client), m_formspec_prepend(formspecPrepend), m_form_src(fsrc), @@ -143,11 +146,12 @@ GUIFormSpecMenu::~GUIFormSpecMenu() void GUIFormSpecMenu::create(GUIFormSpecMenu *&cur_formspec, Client *client, JoystickController *joystick, IFormSource *fs_src, TextDest *txt_dest, - const std::string &formspecPrepend) + const std::string &formspecPrepend, ISoundManager *sound_manager) { if (cur_formspec == nullptr) { cur_formspec = new GUIFormSpecMenu(joystick, guiroot, -1, &g_menumgr, - client, client->getTextureSource(), fs_src, txt_dest, formspecPrepend); + client, client->getTextureSource(), sound_manager, fs_src, + txt_dest, formspecPrepend); cur_formspec->doPause = false; /* @@ -614,6 +618,9 @@ void GUIFormSpecMenu::parseCheckbox(parserData* data, const std::string &element data->current_parent, spec.fid, spec.flabel.c_str()); auto style = getDefaultStyleForElement("checkbox", name); + + spec.sound = style.get(StyleSpec::Property::SOUND, ""); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); if (spec.fname == m_focused_element) { @@ -1020,6 +1027,9 @@ void GUIFormSpecMenu::parseButton(parserData* data, const std::string &element, data->current_parent, spec.fid, spec.flabel.c_str()); auto style = getStyleForElement(type, name, (type != "button") ? "button" : ""); + + spec.sound = style[StyleSpec::STATE_DEFAULT].get(StyleSpec::Property::SOUND, ""); + e->setStyles(style); if (spec.fname == m_focused_element) { @@ -1381,6 +1391,9 @@ void GUIFormSpecMenu::parseDropDown(parserData* data, const std::string &element e->setSelected(stoi(str_initial_selection)-1); auto style = getDefaultStyleForElement("dropdown", name); + + spec.sound = style.get(StyleSpec::Property::SOUND, ""); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); m_fields.push_back(spec); @@ -1747,6 +1760,10 @@ void GUIFormSpecMenu::parseHyperText(parserData *data, const std::string &elemen ); spec.ftype = f_HyperText; + + auto style = getDefaultStyleForElement("hypertext", spec.fname); + spec.sound = style.get(StyleSpec::Property::SOUND, ""); + GUIHyperText *e = new GUIHyperText(spec.flabel.c_str(), Environment, data->current_parent, spec.fid, rect, m_client, m_tsrc); e->drop(); @@ -1999,6 +2016,8 @@ void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &elem auto style = getStyleForElement("image_button", spec.fname); + spec.sound = style[StyleSpec::STATE_DEFAULT].get(StyleSpec::Property::SOUND, ""); + // Override style properties with values specified directly in the element if (!image_name.empty()) style[StyleSpec::STATE_DEFAULT].set(StyleSpec::FGIMG, image_name); @@ -2107,6 +2126,9 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &elemen e->setTabHeight(geom.Y); auto style = getDefaultStyleForElement("tabheader", name); + + spec.sound = style.get(StyleSpec::Property::SOUND, ""); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, true)); for (const std::string &button : buttons) { @@ -2195,6 +2217,9 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data, const std::string & item_name, m_client); auto style = getStyleForElement("item_image_button", spec_btn.fname, "image_button"); + + spec_btn.sound = style[StyleSpec::STATE_DEFAULT].get(StyleSpec::Property::SOUND, ""); + e_btn->setStyles(style); if (spec_btn.fname == m_focused_element) { @@ -4486,6 +4511,8 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) for (GUIFormSpecMenu::FieldSpec &s : m_fields) { if ((s.ftype == f_TabHeader) && (s.fid == event.GUIEvent.Caller->getID())) { + if (!s.sound.empty() && m_sound_manager) + m_sound_manager->playSound(s.sound, false, 1.0f); s.send = true; acceptInput(); s.send = false; @@ -4529,6 +4556,9 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) continue; if (s.ftype == f_Button || s.ftype == f_CheckBox) { + if (!s.sound.empty() && m_sound_manager) + m_sound_manager->playSound(s.sound, false, 1.0f); + s.send = true; if (s.is_exit) { if (m_allowclose) { @@ -4551,6 +4581,8 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) s2.send = false; } } + if (!s.sound.empty() && m_sound_manager) + m_sound_manager->playSound(s.sound, false, 1.0f); s.send = true; acceptInput(quit_mode_no); @@ -4567,6 +4599,8 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) acceptInput(quit_mode_no); s.fdefault = L""; } else if (s.ftype == f_Unknown || s.ftype == f_HyperText) { + if (!s.sound.empty() && m_sound_manager) + m_sound_manager->playSound(s.sound, false, 1.0f); s.send = true; acceptInput(); s.send = false; diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h index c5d662a69..53076e3bd 100644 --- a/src/gui/guiFormSpecMenu.h +++ b/src/gui/guiFormSpecMenu.h @@ -39,6 +39,7 @@ class InventoryManager; class ISimpleTextureSource; class Client; class GUIScrollContainer; +class ISoundManager; typedef enum { f_Button, @@ -127,6 +128,7 @@ class GUIFormSpecMenu : public GUIModalMenu int priority; core::rect rect; gui::ECURSOR_ICON fcursor_icon; + std::string sound; }; struct TooltipSpec @@ -151,6 +153,7 @@ public: IMenuManager *menumgr, Client *client, ISimpleTextureSource *tsrc, + ISoundManager *sound_manager, IFormSource* fs_src, TextDest* txt_dst, const std::string &formspecPrepend, @@ -160,7 +163,7 @@ public: static void create(GUIFormSpecMenu *&cur_formspec, Client *client, JoystickController *joystick, IFormSource *fs_src, TextDest *txt_dest, - const std::string &formspecPrepend); + const std::string &formspecPrepend, ISoundManager *sound_manager); void setFormSpec(const std::string &formspec_string, const InventoryLocation ¤t_inventory_location) @@ -293,6 +296,7 @@ protected: InventoryManager *m_invmgr; ISimpleTextureSource *m_tsrc; + ISoundManager *m_sound_manager; Client *m_client; std::string m_formspec_string; -- cgit v1.2.3