diff --git a/builtin/game/features.lua b/builtin/game/features.lua index 727d90174..510d0461f 100644 --- a/builtin/game/features.lua +++ b/builtin/game/features.lua @@ -33,6 +33,8 @@ core.features = { random_state_restore = true, after_order_expiry_registration = true, wallmounted_rotate = true, + item_specific_pointabilities = true, + blocking_pointability_type = true, } function core.has_feature(arg) diff --git a/doc/lua_api.md b/doc/lua_api.md index ab2602043..7de57aff7 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -5298,6 +5298,10 @@ Utilities -- wallmounted nodes mounted at floor or ceiling may additionally -- be rotated by 90° with special param2 values (5.9.0) wallmounted_rotate = true, + -- Availability of the `pointabilities` property in the item definition (5.9.0) + item_specific_pointabilities = true, + -- Nodes `pointable` property can be `"blocking"` (5.9.0) + blocking_pointability_type = true, } ``` @@ -8382,7 +8386,9 @@ Player properties need to be saved manually. pointable = true, - -- Whether the object can be pointed at + -- Can be `true` if it is pointable, `false` if it can be pointed through, + -- or `"blocking"` if it is pointable but not selectable. + -- Can be overridden by the `pointabilities` of the held item. visual = "cube" / "sprite" / "upright_sprite" / "mesh" / "wielditem" / "item", -- "cube" is a node-sized cube. @@ -8746,6 +8752,27 @@ Used by `minetest.register_node`, `minetest.register_craftitem`, and -- If true, item can point to all liquid nodes (`liquidtype ~= "none"`), -- even those for which `pointable = false` + pointabilities = { + nodes = { + ["default:stone"] = "blocking", + ["group:leaves"] = false, + }, + objects = { + ["modname:entityname"] = true, + ["group:ghosty"] = true, -- (an armor group) + } + }, + -- Contains lists to override the `pointable` property of pointed nodes and objects. + -- The index can be a node/entity name or a group with the prefix `"group:"`. + -- (For objects `armor_groups` are used and for players the entity name is irrelevant.) + -- If multiple fields fit, the following priority order is applied: + -- value of matching node/entity name + -- `true` for any group + -- `false` for any group + -- `"blocking"` for any group + -- `liquids_pointable` if it is a liquid node + -- `pointable` property of the node or object + light_source = 0, -- When used for nodes: Defines amount of light emitted by node. -- Otherwise: Defines texture glow when viewed as a dropped item @@ -8971,7 +8998,11 @@ Used by `minetest.register_node`. walkable = true, -- If true, objects collide with node - pointable = true, -- If true, can be pointed at + pointable = true, + -- Can be `true` if it is pointable, `false` if it can be pointed through, + -- or `"blocking"` if it is pointable but not selectable. + -- Can be overridden by the `pointabilities` of the held item. + -- A client may be able to point non-pointable nodes, since it isn't checked server-side. diggable = true, -- If false, can never be dug diff --git a/games/devtest/mods/testentities/init.lua b/games/devtest/mods/testentities/init.lua index 6e0f8acb8..9ab54f5ab 100644 --- a/games/devtest/mods/testentities/init.lua +++ b/games/devtest/mods/testentities/init.lua @@ -1,3 +1,4 @@ dofile(minetest.get_modpath("testentities").."/visuals.lua") dofile(minetest.get_modpath("testentities").."/selectionbox.lua") dofile(minetest.get_modpath("testentities").."/armor.lua") +dofile(minetest.get_modpath("testentities").."/pointable.lua") diff --git a/games/devtest/mods/testentities/pointable.lua b/games/devtest/mods/testentities/pointable.lua new file mode 100644 index 000000000..be7ce4ba8 --- /dev/null +++ b/games/devtest/mods/testentities/pointable.lua @@ -0,0 +1,23 @@ +-- Pointability test Entities + +-- Register wrapper for compactness +local function register_pointable_testentity(name, pointable) + local texture = "testnodes_"..name..".png" + minetest.register_entity("testentities:"..name, { + initial_properties = { + visual = "cube", + visual_size = {x = 0.6, y = 0.6, z = 0.6}, + textures = { + texture, texture, texture, texture, texture, texture + }, + pointable = pointable, + }, + on_activate = function(self) + self.object:set_armor_groups({[name.."_test"] = 1}) + end + }) +end + +register_pointable_testentity("pointable", true) +register_pointable_testentity("not_pointable", false) +register_pointable_testentity("blocking_pointable", "blocking") diff --git a/games/devtest/mods/testnodes/properties.lua b/games/devtest/mods/testnodes/properties.lua index b93d0c73b..a688d3814 100644 --- a/games/devtest/mods/testnodes/properties.lua +++ b/games/devtest/mods/testnodes/properties.lua @@ -663,3 +663,23 @@ minetest.register_node("testnodes:post_effect_color_shaded_true", { is_ground_content = false, groups = {dig_immediate=3}, }) + +-- Pointability + +-- Register wrapper for compactness +local function register_pointable_test_node(name, description, pointable) + local texture = "testnodes_"..name..".png" + minetest.register_node("testnodes:"..name, { + description = S(description), + tiles = {texture}, + drawtype = "glasslike_framed", + paramtype = "light", + walkable = false, + pointable = pointable, + groups = {dig_immediate=3, [name.."_test"]=1}, + }) +end + +register_pointable_test_node("pointable", "Pointable Node", true) +register_pointable_test_node("not_pointable", "Not Pointable Node", false) +register_pointable_test_node("blocking_pointable", "Blocking Pointable Node", "blocking") diff --git a/games/devtest/mods/testnodes/textures/testnodes_blocking_pointable.png b/games/devtest/mods/testnodes/textures/testnodes_blocking_pointable.png new file mode 100644 index 000000000..fa021041a Binary files /dev/null and b/games/devtest/mods/testnodes/textures/testnodes_blocking_pointable.png differ diff --git a/games/devtest/mods/testnodes/textures/testnodes_not_pointable.png b/games/devtest/mods/testnodes/textures/testnodes_not_pointable.png new file mode 100644 index 000000000..8509a68b7 Binary files /dev/null and b/games/devtest/mods/testnodes/textures/testnodes_not_pointable.png differ diff --git a/games/devtest/mods/testnodes/textures/testnodes_pointable.png b/games/devtest/mods/testnodes/textures/testnodes_pointable.png new file mode 100644 index 000000000..70c8f36f5 Binary files /dev/null and b/games/devtest/mods/testnodes/textures/testnodes_pointable.png differ diff --git a/games/devtest/mods/testtools/init.lua b/games/devtest/mods/testtools/init.lua index 7aa55bdb3..09280f61f 100644 --- a/games/devtest/mods/testtools/init.lua +++ b/games/devtest/mods/testtools/init.lua @@ -7,6 +7,20 @@ dofile(minetest.get_modpath("testtools") .. "/light.lua") dofile(minetest.get_modpath("testtools") .. "/privatizer.lua") dofile(minetest.get_modpath("testtools") .. "/particles.lua") +local pointabilities_nodes = { + nodes = { + ["group:blocking_pointable_test"] = true, + ["group:not_pointable_test"] = true, + }, +} + +local pointabilities_objects = { + objects = { + ["group:blocking_pointable_test"] = true, + ["group:not_pointable_test"] = true, + }, +} + minetest.register_tool("testtools:param2tool", { description = S("Param2 Tool") .."\n".. S("Modify param2 value of nodes") .."\n".. @@ -16,6 +30,7 @@ minetest.register_tool("testtools:param2tool", { S("Sneak+Place: -8"), inventory_image = "testtools_param2tool.png", groups = { testtool = 1, disable_repair = 1 }, + pointabilities = pointabilities_nodes, on_use = function(itemstack, user, pointed_thing) local pos = minetest.get_pointed_thing_position(pointed_thing) if pointed_thing.type ~= "node" or (not pos) then @@ -58,6 +73,7 @@ minetest.register_tool("testtools:node_setter", { S("Place in air: Manually select a node"), inventory_image = "testtools_node_setter.png", groups = { testtool = 1, disable_repair = 1 }, + pointabilities = pointabilities_nodes, on_use = function(itemstack, user, pointed_thing) local pos = minetest.get_pointed_thing_position(pointed_thing) if pointed_thing.type == "nothing" then @@ -118,6 +134,10 @@ minetest.register_tool("testtools:remover", { S("Punch: Remove pointed node or object"), inventory_image = "testtools_remover.png", groups = { testtool = 1, disable_repair = 1 }, + pointabilities = { + nodes = pointabilities_nodes.nodes, + objects = pointabilities_objects.objects, + }, on_use = function(itemstack, user, pointed_thing) local pos = minetest.get_pointed_thing_position(pointed_thing) if pointed_thing.type == "node" and pos ~= nil then @@ -139,6 +159,7 @@ minetest.register_tool("testtools:falling_node_tool", { S("Place: Move pointed node 2 units upwards, then make it fall"), inventory_image = "testtools_falling_node_tool.png", groups = { testtool = 1, disable_repair = 1 }, + pointabilities = pointabilities_nodes, on_place = function(itemstack, user, pointed_thing) -- Teleport node 1-2 units upwards (if possible) and make it fall local pos = minetest.get_pointed_thing_position(pointed_thing) @@ -192,6 +213,7 @@ minetest.register_tool("testtools:rotator", { S("Aux1+Punch: Roll"), inventory_image = "testtools_entity_rotator.png", groups = { testtool = 1, disable_repair = 1 }, + pointabilities = pointabilities_objects, on_use = function(itemstack, user, pointed_thing) if pointed_thing.type ~= "object" then return @@ -250,6 +272,7 @@ minetest.register_tool("testtools:object_mover", { S("Sneak+Place: Decrease distance"), inventory_image = "testtools_object_mover.png", groups = { testtool = 1, disable_repair = 1 }, + pointabilities = pointabilities_objects, on_place = mover_config, on_secondary_use = mover_config, on_use = function(itemstack, user, pointed_thing) @@ -296,6 +319,7 @@ minetest.register_tool("testtools:entity_scaler", { S("Sneak+Punch: Decrease scale"), inventory_image = "testtools_entity_scaler.png", groups = { testtool = 1, disable_repair = 1 }, + pointabilities = pointabilities_objects, on_use = function(itemstack, user, pointed_thing) if pointed_thing.type ~= "object" then return @@ -355,6 +379,7 @@ minetest.register_tool("testtools:branding_iron", { S("Devices that accept the returned name also accept \"player:\" for players."), inventory_image = "testtools_branding_iron.png", groups = { testtool = 1, disable_repair = 1 }, + pointabilities = pointabilities_objects, on_use = function(_itemstack, user, pointed_thing) local obj local msg @@ -499,6 +524,7 @@ minetest.register_tool("testtools:object_editor", { S("Punch air: Edit yourself"), inventory_image = "testtools_object_editor.png", groups = { testtool = 1, disable_repair = 1 }, + pointabilities = pointabilities_objects, on_use = function(itemstack, user, pointed_thing) if user and user:is_player() then local name = user:get_player_name() @@ -586,6 +612,7 @@ minetest.register_tool("testtools:object_attacher", { S("Aux1+Sneak+Place: Decrease attachment rotation"), inventory_image = "testtools_object_attacher.png", groups = { testtool = 1, disable_repair = 1 }, + pointabilities = pointabilities_objects, on_place = attacher_config, on_secondary_use = attacher_config, on_use = function(itemstack, user, pointed_thing) @@ -679,6 +706,7 @@ minetest.register_tool("testtools:children_getter", { S("Punch air to show your own 'children'"), inventory_image = "testtools_children_getter.png", groups = { testtool = 1, disable_repair = 1 }, + pointabilities = pointabilities_objects, on_use = function(itemstack, user, pointed_thing) if user and user:is_player() then local name = user:get_player_name() @@ -998,3 +1026,41 @@ minetest.register_on_leaveplayer(function(player) meta_latest_keylist[name] = nil node_meta_posses[name] = nil end) + +-- Pointing Staffs + +minetest.register_tool("testtools:blocked_pointing_staff", { + description = S("Blocked Pointing Staff").."\n".. + S("Can point the Blocking Pointable Node/Object and ".. + "the Pointable Node/Object is point blocking."), + inventory_image = "testtools_blocked_pointing_staff.png", + pointabilities = { + nodes = { + ["testnodes:blocking_pointable"] = true, + ["group:pointable_test"] = "blocking" + }, + objects = { + ["testentities:blocking_pointable"] = true, + ["group:pointable_test"] = "blocking" + } + } +}) + +minetest.register_tool("testtools:ultimate_pointing_staff", { + description = S("Ultimate Pointing Staff").."\n".. + S("Can point all pointable test nodes, objects and liquids."), + inventory_image = "testtools_ultimate_pointing_staff.png", + liquids_pointable = true, + pointabilities = { + nodes = { + ["group:blocking_pointable_test"] = true, + ["group:pointable_test"] = true, + ["testnodes:not_pointable"] = true + }, + objects = { + ["group:blocking_pointable_test"] = true, + ["group:pointable_test"] = true, + ["testentities:not_pointable"] = true + } + } +}) diff --git a/games/devtest/mods/testtools/textures/testtools_blocked_pointing_staff.png b/games/devtest/mods/testtools/textures/testtools_blocked_pointing_staff.png new file mode 100644 index 000000000..aa1cdb97a Binary files /dev/null and b/games/devtest/mods/testtools/textures/testtools_blocked_pointing_staff.png differ diff --git a/games/devtest/mods/testtools/textures/testtools_ultimate_pointing_staff.png b/games/devtest/mods/testtools/textures/testtools_ultimate_pointing_staff.png new file mode 100644 index 000000000..e4d80fb4d Binary files /dev/null and b/games/devtest/mods/testtools/textures/testtools_ultimate_pointing_staff.png differ diff --git a/src/client/clientenvironment.cpp b/src/client/clientenvironment.cpp index 54baf30a1..b9bb82bdf 100644 --- a/src/client/clientenvironment.cpp +++ b/src/client/clientenvironment.cpp @@ -489,7 +489,8 @@ ClientEnvEvent ClientEnvironment::getClientEnvEvent() void ClientEnvironment::getSelectedActiveObjects( const core::line3d &shootline_on_map, - std::vector &objects) + std::vector &objects, + const std::optional &pointabilities) { auto allObjects = m_ao_manager.getActiveSelectableObjects(shootline_on_map); const v3f line_vector = shootline_on_map.getVector(); @@ -516,9 +517,23 @@ void ClientEnvironment::getSelectedActiveObjects( current_raw_normal = current_normal; } if (collision) { - current_intersection += obj->getPosition(); - objects.emplace_back(obj->getId(), current_intersection, current_normal, current_raw_normal, - (current_intersection - shootline_on_map.start).getLengthSQ()); + PointabilityType pointable; + if (pointabilities) { + if (gcao->isPlayer()) { + pointable = pointabilities->matchPlayer(gcao->getGroups()).value_or( + gcao->getProperties().pointable); + } else { + pointable = pointabilities->matchObject(gcao->getName(), + gcao->getGroups()).value_or(gcao->getProperties().pointable); + } + } else { + pointable = gcao->getProperties().pointable; + } + if (pointable != PointabilityType::POINTABLE_NOT) { + current_intersection += obj->getPosition(); + objects.emplace_back(obj->getId(), current_intersection, current_normal, current_raw_normal, + (current_intersection - shootline_on_map.start).getLengthSQ(), pointable); + } } } } diff --git a/src/client/clientenvironment.h b/src/client/clientenvironment.h index ba0fdb50a..407473be7 100644 --- a/src/client/clientenvironment.h +++ b/src/client/clientenvironment.h @@ -131,7 +131,8 @@ public: virtual void getSelectedActiveObjects( const core::line3d &shootline_on_map, - std::vector &objects + std::vector &objects, + const std::optional &pointabilities ); const std::set &getPlayerNames() { return m_player_names; } diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index 728906bb8..d80869d9c 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -411,8 +411,7 @@ GenericCAO::~GenericCAO() bool GenericCAO::getSelectionBox(aabb3f *toset) const { - if (!m_prop.is_visible || !m_is_visible || m_is_local_player - || !m_prop.pointable) { + if (!m_prop.is_visible || !m_is_visible || m_is_local_player) { return false; } *toset = m_selection_box; diff --git a/src/client/content_cao.h b/src/client/content_cao.h index 08d42220e..7fdcb73da 100644 --- a/src/client/content_cao.h +++ b/src/client/content_cao.h @@ -174,6 +174,8 @@ public: inline const ObjectProperties &getProperties() const { return m_prop; } + inline const std::string &getName() const { return m_name; } + scene::ISceneNode *getSceneNode() const override; scene::IAnimatedMeshSceneNode *getAnimatedMeshSceneNode() const override; @@ -208,6 +210,11 @@ public: return m_is_local_player; } + inline bool isPlayer() const + { + return m_is_player; + } + inline bool isVisible() const { return m_is_visible; diff --git a/src/client/game.cpp b/src/client/game.cpp index b740f43a1..54bff7557 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -841,6 +841,7 @@ protected: * the camera position. This also gives the maximal distance * of the search. * @param[in] liquids_pointable if false, liquids are ignored + * @param[in] pointabilities item specific pointable overriding * @param[in] look_for_object if false, objects are ignored * @param[in] camera_offset offset of the camera * @param[out] selected_object the selected object or @@ -848,6 +849,7 @@ protected: */ PointedThing updatePointedThing( const core::line3d &shootline, bool liquids_pointable, + const std::optional &pointabilities, bool look_for_object, const v3s16 &camera_offset); void handlePointingAtNothing(const ItemStack &playerItem); void handlePointingAtNode(const PointedThing &pointed, @@ -3343,6 +3345,7 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud) PointedThing pointed = updatePointedThing(shootline, selected_def.liquids_pointable, + selected_def.pointabilities, !runData.btn_down_for_dig, camera_offset); @@ -3454,6 +3457,7 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud) PointedThing Game::updatePointedThing( const core::line3d &shootline, bool liquids_pointable, + const std::optional &pointabilities, bool look_for_object, const v3s16 &camera_offset) { @@ -3470,7 +3474,7 @@ PointedThing Game::updatePointedThing( runData.selected_object = NULL; hud->pointing_at_object = false; - RaycastState s(shootline, look_for_object, liquids_pointable); + RaycastState s(shootline, look_for_object, liquids_pointable, pointabilities); PointedThing result; env.continueRaycast(&s, &result); if (result.type == POINTEDTHING_OBJECT) { diff --git a/src/environment.cpp b/src/environment.cpp index 0f74ae6bb..44ab8a6bf 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -102,24 +102,33 @@ bool Environment::line_of_sight(v3f pos1, v3f pos2, v3s16 *p) } /* - Check if a node is pointable + Check how a node can be pointed at */ -inline static bool isPointableNode(const MapNode &n, - const NodeDefManager *nodedef , bool liquids_pointable) +inline static PointabilityType isPointableNode(const MapNode &n, + const NodeDefManager *nodedef, bool liquids_pointable, + const std::optional &pointabilities) { const ContentFeatures &features = nodedef->get(n); - return features.pointable || - (liquids_pointable && features.isLiquid()); + if (pointabilities) { + std::optional match = + pointabilities->matchNode(features.name, features.groups); + if (match) + return match.value(); + } + + if (features.isLiquid() && liquids_pointable) + return PointabilityType::POINTABLE; + return features.pointable; } -void Environment::continueRaycast(RaycastState *state, PointedThing *result) +void Environment::continueRaycast(RaycastState *state, PointedThing *result_p) { const NodeDefManager *nodedef = getMap().getNodeDefManager(); if (state->m_initialization_needed) { // Add objects if (state->m_objects_pointable) { std::vector found; - getSelectedActiveObjects(state->m_shootline, found); + getSelectedActiveObjects(state->m_shootline, found, state->m_pointabilities); for (const PointedThing &pointed : found) { state->m_found.push(pointed); } @@ -184,10 +193,15 @@ void Environment::continueRaycast(RaycastState *state, PointedThing *result) bool is_valid_position; n = map.getNode(np, &is_valid_position); - if (!(is_valid_position && isPointableNode(n, nodedef, - state->m_liquids_pointable))) { + if (!is_valid_position) + continue; + + PointabilityType pointable = isPointableNode(n, nodedef, + state->m_liquids_pointable, + state->m_pointabilities); + // If it can be pointed through skip + if (pointable == PointabilityType::POINTABLE_NOT) continue; - } PointedThing result; @@ -234,6 +248,7 @@ void Environment::continueRaycast(RaycastState *state, PointedThing *result) if (!is_colliding) { continue; } + result.pointability = pointable; result.type = POINTEDTHING_NODE; result.node_undersurface = np; result.distanceSq = min_distance_sq; @@ -275,12 +290,16 @@ void Environment::continueRaycast(RaycastState *state, PointedThing *result) state->m_previous_node = state->m_iterator.m_current_node_pos; state->m_iterator.next(); } - // Return empty PointedThing if nothing left on the ray + + // Return empty PointedThing if nothing left on the ray or it is blocking pointable if (state->m_found.empty()) { - result->type = POINTEDTHING_NOTHING; + result_p->type = POINTEDTHING_NOTHING; } else { - *result = state->m_found.top(); + *result_p = state->m_found.top(); state->m_found.pop(); + if (result_p->pointability == PointabilityType::POINTABLE_BLOCKING) { + result_p->type = POINTEDTHING_NOTHING; + } } } diff --git a/src/environment.h b/src/environment.h index 19c30b4d5..62e52a8a4 100644 --- a/src/environment.h +++ b/src/environment.h @@ -34,6 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include +#include #include "irr_v3d.h" #include "util/basic_macros.h" #include "line3d.h" @@ -42,6 +43,7 @@ class IGameDef; class Map; struct PointedThing; class RaycastState; +struct Pointabilities; class Environment { @@ -97,7 +99,8 @@ public: * @param[out] objects found objects */ virtual void getSelectedActiveObjects(const core::line3d &shootline_on_map, - std::vector &objects) = 0; + std::vector &objects, + const std::optional &pointabilities) = 0; /*! * Returns the next node or object the shootline meets. diff --git a/src/itemdef.cpp b/src/itemdef.cpp index 143612afd..ee3e3829d 100644 --- a/src/itemdef.cpp +++ b/src/itemdef.cpp @@ -122,6 +122,7 @@ ItemDefinition& ItemDefinition::operator=(const ItemDefinition &def) stack_max = def.stack_max; usable = def.usable; liquids_pointable = def.liquids_pointable; + pointabilities = def.pointabilities; if (def.tool_capabilities) tool_capabilities = new ToolCapabilities(*def.tool_capabilities); groups = def.groups; @@ -167,6 +168,7 @@ void ItemDefinition::reset() stack_max = 99; usable = false; liquids_pointable = false; + pointabilities = std::nullopt; delete tool_capabilities; tool_capabilities = NULL; groups.clear(); @@ -241,6 +243,14 @@ void ItemDefinition::serialize(std::ostream &os, u16 protocol_version) const writeU8(os, wallmounted_rotate_vertical); touch_interaction.serialize(os); + + std::string pointabilities_s; + if (pointabilities) { + std::ostringstream tmp_os(std::ios::binary); + pointabilities->serialize(tmp_os); + pointabilities_s = tmp_os.str(); + } + os << serializeString16(pointabilities_s); } void ItemDefinition::deSerialize(std::istream &is, u16 protocol_version) @@ -316,6 +326,13 @@ void ItemDefinition::deSerialize(std::istream &is, u16 protocol_version) wallmounted_rotate_vertical = readU8(is); // 0 if missing touch_interaction.deSerialize(is); + + std::string pointabilities_s = deSerializeString16(is); + if (!pointabilities_s.empty()) { + std::istringstream tmp_is(pointabilities_s, std::ios::binary); + pointabilities = std::make_optional(); + pointabilities->deSerialize(tmp_is); + } } catch(SerializationError &e) {}; } diff --git a/src/itemdef.h b/src/itemdef.h index 361848911..192e90095 100644 --- a/src/itemdef.h +++ b/src/itemdef.h @@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "itemgroup.h" #include "sound.h" #include "texture_override.h" // TextureOverride +#include "util/pointabilities.h" class IGameDef; class Client; struct ToolCapabilities; @@ -97,8 +98,11 @@ struct ItemDefinition u16 stack_max; bool usable; bool liquids_pointable; - // May be NULL. If non-NULL, deleted by destructor + std::optional pointabilities; + + // They may be NULL. If non-NULL, deleted by destructor ToolCapabilities *tool_capabilities; + ItemGroupList groups; SoundSpec sound_place; SoundSpec sound_place_failed; diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 3979fc575..830ee1ff4 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -386,7 +386,7 @@ void ContentFeatures::reset() light_propagates = false; sunlight_propagates = false; walkable = true; - pointable = true; + pointable = PointabilityType::POINTABLE; diggable = true; climbable = false; buildable_to = false; @@ -504,7 +504,7 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const // interaction writeU8(os, walkable); - writeU8(os, pointable); + Pointabilities::serializePointabilityType(os, pointable); writeU8(os, diggable); writeU8(os, climbable); writeU8(os, buildable_to); @@ -617,7 +617,7 @@ void ContentFeatures::deSerialize(std::istream &is, u16 protocol_version) // interaction walkable = readU8(is); - pointable = readU8(is); + pointable = Pointabilities::deSerializePointabilityType(is); diggable = readU8(is); climbable = readU8(is); buildable_to = readU8(is); @@ -1083,7 +1083,7 @@ void NodeDefManager::clear() f.light_propagates = true; f.sunlight_propagates = true; f.walkable = false; - f.pointable = false; + f.pointable = PointabilityType::POINTABLE_NOT; f.diggable = false; f.buildable_to = true; f.floodable = true; @@ -1104,7 +1104,7 @@ void NodeDefManager::clear() f.light_propagates = false; f.sunlight_propagates = false; f.walkable = false; - f.pointable = false; + f.pointable = PointabilityType::POINTABLE_NOT; f.diggable = false; f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs f.is_ground_content = true; diff --git a/src/nodedef.h b/src/nodedef.h index 8d111f67e..f493d48c4 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -35,6 +35,7 @@ class Client; #include "constants.h" // BS #include "texture_override.h" // TextureOverride #include "tileanimation.h" +#include "util/pointabilities.h" class IItemDefManager; class ITextureSource; @@ -395,8 +396,8 @@ struct ContentFeatures // This is used for collision detection. // Also for general solidness queries. bool walkable; - // Player can point to these - bool pointable; + // Player can point to these, point through or it is blocking + PointabilityType pointable; // Player can dig these bool diggable; // Player can climb these diff --git a/src/object_properties.cpp b/src/object_properties.cpp index 00f538d29..6f66fbaee 100644 --- a/src/object_properties.cpp +++ b/src/object_properties.cpp @@ -73,7 +73,7 @@ std::string ObjectProperties::dump() os << ", selectionbox=" << selectionbox.MinEdge << "," << selectionbox.MaxEdge; os << ", rotate_selectionbox=" << rotate_selectionbox; - os << ", pointable=" << pointable; + os << ", pointable=" << Pointabilities::toStringPointabilityType(pointable); os << ", static_save=" << static_save; os << ", eye_height=" << eye_height; os << ", zoom_fov=" << zoom_fov; @@ -127,7 +127,7 @@ void ObjectProperties::serialize(std::ostream &os) const writeV3F32(os, collisionbox.MaxEdge); writeV3F32(os, selectionbox.MinEdge); writeV3F32(os, selectionbox.MaxEdge); - writeU8(os, pointable); + Pointabilities::serializePointabilityType(os, pointable); os << serializeString16(visual); writeV3F32(os, visual_size); writeU16(os, textures.size()); @@ -188,7 +188,7 @@ void ObjectProperties::deSerialize(std::istream &is) collisionbox.MaxEdge = readV3F32(is); selectionbox.MinEdge = readV3F32(is); selectionbox.MaxEdge = readV3F32(is); - pointable = readU8(is); + pointable = Pointabilities::deSerializePointabilityType(is); visual = deSerializeString16(is); visual_size = readV3F32(is); textures.clear(); diff --git a/src/object_properties.h b/src/object_properties.h index 44d2f29b2..557f128be 100644 --- a/src/object_properties.h +++ b/src/object_properties.h @@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include +#include "util/pointabilities.h" struct ObjectProperties { @@ -36,7 +37,7 @@ struct ObjectProperties aabb3f collisionbox = aabb3f(-0.5f, -0.5f, -0.5f, 0.5f, 0.5f, 0.5f); aabb3f selectionbox = aabb3f(-0.5f, -0.5f, -0.5f, 0.5f, 0.5f, 0.5f); bool rotate_selectionbox = false; - bool pointable = true; + PointabilityType pointable = PointabilityType::POINTABLE; std::string visual = "sprite"; std::string mesh = ""; v3f visual_size = v3f(1, 1, 1); diff --git a/src/raycast.cpp b/src/raycast.cpp index c41b9c394..510a8f7f9 100644 --- a/src/raycast.cpp +++ b/src/raycast.cpp @@ -58,12 +58,14 @@ bool RaycastSort::operator() (const PointedThing &pt1, RaycastState::RaycastState(const core::line3d &shootline, - bool objects_pointable, bool liquids_pointable) : + bool objects_pointable, bool liquids_pointable, + const std::optional &pointabilities) : m_shootline(shootline), m_iterator(shootline.start / BS, shootline.getVector() / BS), m_previous_node(m_iterator.m_current_node_pos), m_objects_pointable(objects_pointable), - m_liquids_pointable(liquids_pointable) + m_liquids_pointable(liquids_pointable), + m_pointabilities(pointabilities) { } diff --git a/src/raycast.h b/src/raycast.h index 8da075738..d33f13c98 100644 --- a/src/raycast.h +++ b/src/raycast.h @@ -38,7 +38,7 @@ public: * @param liquids pointable if false, liquid nodes won't be found */ RaycastState(const core::line3d &shootline, bool objects_pointable, - bool liquids_pointable); + bool liquids_pointable, const std::optional &pointabilities); //! Shootline of the raycast. core::line3d m_shootline; @@ -55,6 +55,7 @@ public: bool m_objects_pointable; bool m_liquids_pointable; + const std::optional &m_pointabilities; //! The code needs to search these nodes around the center node. core::aabbox3d m_search_range { 0, 0, 0, 0, 0, 0 }; diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index b50e02a80..5259d3f38 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -83,6 +83,12 @@ void read_item_definition(lua_State* L, int index, getboolfield(L, index, "liquids_pointable", def.liquids_pointable); + lua_getfield(L, index, "pointabilities"); + if(lua_istable(L, -1)){ + def.pointabilities = std::make_optional( + read_pointabilities(L, -1)); + } + lua_getfield(L, index, "tool_capabilities"); if(lua_istable(L, -1)){ def.tool_capabilities = new ToolCapabilities( @@ -199,6 +205,10 @@ void push_item_definition_full(lua_State *L, const ItemDefinition &i) lua_setfield(L, -2, "usable"); lua_pushboolean(L, i.liquids_pointable); lua_setfield(L, -2, "liquids_pointable"); + if (i.pointabilities) { + push_pointabilities(L, *i.pointabilities); + lua_setfield(L, -2, "pointabilities"); + } if (i.tool_capabilities) { push_tool_capabilities(L, *i.tool_capabilities); lua_setfield(L, -2, "tool_capabilities"); @@ -311,7 +321,12 @@ void read_object_properties(lua_State *L, int index, } lua_pop(L, 1); - getboolfield(L, -1, "pointable", prop->pointable); + lua_getfield(L, -1, "pointable"); + if(!lua_isnil(L, -1)){ + prop->pointable = read_pointability_type(L, -1); + } + lua_pop(L, 1); + getstringfield(L, -1, "visual", prop->visual); getstringfield(L, -1, "mesh", prop->mesh); @@ -452,7 +467,7 @@ void push_object_properties(lua_State *L, ObjectProperties *prop) lua_pushboolean(L, prop->rotate_selectionbox); lua_setfield(L, -2, "rotate"); lua_setfield(L, -2, "selectionbox"); - lua_pushboolean(L, prop->pointable); + push_pointability_type(L, prop->pointable); lua_setfield(L, -2, "pointable"); lua_pushlstring(L, prop->visual.c_str(), prop->visual.size()); lua_setfield(L, -2, "visual"); @@ -781,8 +796,14 @@ void read_content_features(lua_State *L, ContentFeatures &f, int index) // This is used for collision detection. // Also for general solidness queries. getboolfield(L, index, "walkable", f.walkable); - // Player can point to these - getboolfield(L, index, "pointable", f.pointable); + + // Player can point to these, point through or it is blocking + lua_getfield(L, index, "pointable"); + if(!lua_isnil(L, -1)){ + f.pointable = read_pointability_type(L, -1); + } + lua_pop(L, 1); + // Player can dig these getboolfield(L, index, "diggable", f.diggable); // Player can climb these @@ -1005,7 +1026,7 @@ void push_content_features(lua_State *L, const ContentFeatures &c) lua_setfield(L, -2, "is_ground_content"); lua_pushboolean(L, c.walkable); lua_setfield(L, -2, "walkable"); - lua_pushboolean(L, c.pointable); + push_pointability_type(L, c.pointable); lua_setfield(L, -2, "pointable"); lua_pushboolean(L, c.diggable); lua_setfield(L, -2, "diggable"); @@ -1592,6 +1613,125 @@ ToolCapabilities read_tool_capabilities( return toolcap; } +/******************************************************************************/ +PointabilityType read_pointability_type(lua_State *L, int index) +{ + if (lua_isboolean(L, index)) { + if (lua_toboolean(L, index)) + return PointabilityType::POINTABLE; + else + return PointabilityType::POINTABLE_NOT; + } else { + const char* s = luaL_checkstring(L, index); + if (s && !strcmp(s, "blocking")) { + return PointabilityType::POINTABLE_BLOCKING; + } + } + throw LuaError("Invalid pointable type."); +} + +/******************************************************************************/ +Pointabilities read_pointabilities(lua_State *L, int index) +{ + Pointabilities pointabilities; + + lua_getfield(L, index, "nodes"); + if(lua_istable(L, -1)){ + int ti = lua_gettop(L); + lua_pushnil(L); + while(lua_next(L, ti) != 0) { + // key at index -2 and value at index -1 + std::string name = luaL_checkstring(L, -2); + + // handle groups + if(std::string_view(name).substr(0,6)=="group:") { + pointabilities.node_groups[name.substr(6)] = read_pointability_type(L, -1); + } else { + pointabilities.nodes[name] = read_pointability_type(L, -1); + } + + // removes value, keeps key for next iteration + lua_pop(L, 1); + } + } + lua_pop(L, 1); + + lua_getfield(L, index, "objects"); + if(lua_istable(L, -1)){ + int ti = lua_gettop(L); + lua_pushnil(L); + while(lua_next(L, ti) != 0) { + // key at index -2 and value at index -1 + std::string name = luaL_checkstring(L, -2); + + // handle groups + if(std::string_view(name).substr(0,6)=="group:") { + pointabilities.object_groups[name.substr(6)] = read_pointability_type(L, -1); + } else { + pointabilities.objects[name] = read_pointability_type(L, -1); + } + + // removes value, keeps key for next iteration + lua_pop(L, 1); + } + } + lua_pop(L, 1); + + return pointabilities; +} + +/******************************************************************************/ +void push_pointability_type(lua_State *L, PointabilityType pointable) +{ + switch(pointable) + { + case PointabilityType::POINTABLE: + lua_pushboolean(L, true); + break; + case PointabilityType::POINTABLE_NOT: + lua_pushboolean(L, false); + break; + case PointabilityType::POINTABLE_BLOCKING: + lua_pushliteral(L, "blocking"); + break; + } +} + +/******************************************************************************/ +void push_pointabilities(lua_State *L, const Pointabilities &pointabilities) +{ + // pointabilities table + lua_newtable(L); + + if (!pointabilities.nodes.empty() || !pointabilities.node_groups.empty()) { + // Create and fill table + lua_newtable(L); + for (const auto &entry : pointabilities.nodes) { + push_pointability_type(L, entry.second); + lua_setfield(L, -2, entry.first.c_str()); + } + for (const auto &entry : pointabilities.node_groups) { + push_pointability_type(L, entry.second); + lua_setfield(L, -2, ("group:" + entry.first).c_str()); + } + lua_setfield(L, -2, "nodes"); + } + + if (!pointabilities.objects.empty() || !pointabilities.object_groups.empty()) { + // Create and fill table + lua_newtable(L); + for (const auto &entry : pointabilities.objects) { + push_pointability_type(L, entry.second); + lua_setfield(L, -2, entry.first.c_str()); + } + for (const auto &entry : pointabilities.object_groups) { + push_pointability_type(L, entry.second); + lua_setfield(L, -2, ("group:" + entry.first).c_str()); + } + lua_setfield(L, -2, "objects"); + } +} + /******************************************************************************/ void push_dig_params(lua_State *L,const DigParams ¶ms) { diff --git a/src/script/common/c_content.h b/src/script/common/c_content.h index 4335479c4..6f54b7b23 100644 --- a/src/script/common/c_content.h +++ b/src/script/common/c_content.h @@ -39,6 +39,7 @@ extern "C" { #include "util/string.h" #include "itemgroup.h" #include "itemdef.h" +#include "util/pointabilities.h" #include "c_types.h" // We do an explicit path include because by default c_content.h include src/client/hud.h // prior to the src/hud.h, which is not good on server only build @@ -107,6 +108,11 @@ ItemStack read_item (lua_State *L, int index, IItemDefM struct TileAnimationParams read_animation_definition(lua_State *L, int index); +PointabilityType read_pointability_type (lua_State *L, int index); +Pointabilities read_pointabilities (lua_State *L, int index); +void push_pointability_type (lua_State *L, PointabilityType pointable); +void push_pointabilities (lua_State *L, const Pointabilities &pointabilities); + ToolCapabilities read_tool_capabilities (lua_State *L, int table); void push_tool_capabilities (lua_State *L, const ToolCapabilities &prop); diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index 33d790316..b8daa86a0 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -188,7 +188,7 @@ int LuaRaycast::create_object(lua_State *L) } LuaRaycast *o = new LuaRaycast(core::line3d(pos1, pos2), - objects, liquids); + objects, liquids, std::nullopt); *(void **) (lua_newuserdata(L, sizeof(void *))) = o; luaL_getmetatable(L, className); diff --git a/src/script/lua_api/l_env.h b/src/script/lua_api/l_env.h index 1378736d8..f1fa680e9 100644 --- a/src/script/lua_api/l_env.h +++ b/src/script/lua_api/l_env.h @@ -336,8 +336,9 @@ public: LuaRaycast( const core::line3d &shootline, bool objects_pointable, - bool liquids_pointable) : - state(shootline, objects_pointable, liquids_pointable) + bool liquids_pointable, + const std::optional &pointabilities) : + state(shootline, objects_pointable, liquids_pointable, pointabilities) {} //! Creates a LuaRaycast and leaves it on top of the stack. diff --git a/src/server/luaentity_sao.cpp b/src/server/luaentity_sao.cpp index e4c392eb2..3995f656d 100644 --- a/src/server/luaentity_sao.cpp +++ b/src/server/luaentity_sao.cpp @@ -245,7 +245,7 @@ std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version) // PROTOCOL_VERSION >= 37 writeU8(os, 1); // version - os << serializeString16(""); // name + os << serializeString16(m_init_name); // name writeU8(os, 0); // is_player writeU16(os, getId()); //id writeV3F32(os, m_base_position); @@ -553,7 +553,7 @@ bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const bool LuaEntitySAO::getSelectionBox(aabb3f *toset) const { - if (!m_prop.is_visible || !m_prop.pointable) { + if (!m_prop.is_visible) { return false; } diff --git a/src/server/player_sao.cpp b/src/server/player_sao.cpp index ede3ccb8e..688dcde4e 100644 --- a/src/server/player_sao.cpp +++ b/src/server/player_sao.cpp @@ -39,7 +39,7 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t p m_prop.physical = false; m_prop.collisionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f); m_prop.selectionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f); - m_prop.pointable = true; + m_prop.pointable = PointabilityType::POINTABLE; // Start of default appearance, this should be overwritten by Lua m_prop.visual = "upright_sprite"; m_prop.visual_size = v3f(1, 2, 1); @@ -724,7 +724,7 @@ bool PlayerSAO::getCollisionBox(aabb3f *toset) const bool PlayerSAO::getSelectionBox(aabb3f *toset) const { - if (!m_prop.is_visible || !m_prop.pointable) { + if (!m_prop.is_visible) { return false; } diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index e92b00f57..e0cf99378 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -1830,7 +1830,8 @@ bool ServerEnvironment::getActiveObjectMessage(ActiveObjectMessage *dest) void ServerEnvironment::getSelectedActiveObjects( const core::line3d &shootline_on_map, - std::vector &objects) + std::vector &objects, + const std::optional &pointabilities) { std::vector objs; getObjectsInsideRadius(objs, shootline_on_map.start, @@ -1863,10 +1864,26 @@ void ServerEnvironment::getSelectedActiveObjects( current_raw_normal = current_normal; } if (collision) { - current_intersection += pos; - objects.emplace_back( - (s16) obj->getId(), current_intersection, current_normal, current_raw_normal, - (current_intersection - shootline_on_map.start).getLengthSQ()); + PointabilityType pointable; + if (pointabilities) { + if (LuaEntitySAO* lsao = dynamic_cast(obj)) { + pointable = pointabilities->matchObject(lsao->getName(), + usao->getArmorGroups()).value_or(props->pointable); + } else if (PlayerSAO* psao = dynamic_cast(obj)) { + pointable = pointabilities->matchPlayer(psao->getArmorGroups()).value_or( + props->pointable); + } else { + pointable = props->pointable; + } + } else { + pointable = props->pointable; + } + if (pointable != PointabilityType::POINTABLE_NOT) { + current_intersection += pos; + objects.emplace_back( + (s16) obj->getId(), current_intersection, current_normal, current_raw_normal, + (current_intersection - shootline_on_map.start).getLengthSQ(), pointable); + } } } } diff --git a/src/serverenvironment.h b/src/serverenvironment.h index bb689ea2c..ad6a3acc5 100644 --- a/src/serverenvironment.h +++ b/src/serverenvironment.h @@ -315,7 +315,8 @@ public: virtual void getSelectedActiveObjects( const core::line3d &shootline_on_map, - std::vector &objects + std::vector &objects, + const std::optional &pointabilities ); /* diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index 6bc97915f..d8f106589 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -8,6 +8,7 @@ set(UTIL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/metricsbackend.cpp ${CMAKE_CURRENT_SOURCE_DIR}/numeric.cpp ${CMAKE_CURRENT_SOURCE_DIR}/pointedthing.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/pointabilities.cpp ${CMAKE_CURRENT_SOURCE_DIR}/quicktune.cpp ${CMAKE_CURRENT_SOURCE_DIR}/serialize.cpp ${CMAKE_CURRENT_SOURCE_DIR}/sha1.cpp diff --git a/src/util/pointabilities.cpp b/src/util/pointabilities.cpp new file mode 100644 index 000000000..575ae269b --- /dev/null +++ b/src/util/pointabilities.cpp @@ -0,0 +1,147 @@ +/* +Minetest +Copyright (C) 2023 cx384 + +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 "pointabilities.h" + +#include "serialize.h" +#include "exceptions.h" +#include + +PointabilityType Pointabilities::deSerializePointabilityType(std::istream &is) +{ + PointabilityType pointable_type = static_cast(readU8(is)); + switch(pointable_type) { + case PointabilityType::POINTABLE: + case PointabilityType::POINTABLE_NOT: + case PointabilityType::POINTABLE_BLOCKING: + break; + default: + // Default to POINTABLE in case of unknown PointabilityType type. + pointable_type = PointabilityType::POINTABLE; + break; + } + return pointable_type; +} + +void Pointabilities::serializePointabilityType(std::ostream &os, PointabilityType pointable_type) +{ + writeU8(os, static_cast(pointable_type)); +} + +std::string Pointabilities::toStringPointabilityType(PointabilityType pointable_type) +{ + switch(pointable_type) { + case PointabilityType::POINTABLE: + return "true"; + case PointabilityType::POINTABLE_NOT: + return "false"; + case PointabilityType::POINTABLE_BLOCKING: + return "\"blocking\""; + } + return "unknown"; +} + +std::optional Pointabilities::matchNode(const std::string &name, + const ItemGroupList &groups) const +{ + auto i = nodes.find(name); + return i == nodes.end() ? matchGroups(groups, node_groups) : i->second; +} + +std::optional Pointabilities::matchObject(const std::string &name, + const ItemGroupList &groups) const +{ + auto i = objects.find(name); + return i == objects.end() ? matchGroups(groups, object_groups) : i->second; +} + +std::optional Pointabilities::matchPlayer(const ItemGroupList &groups) const +{ + return matchGroups(groups, object_groups); +} + +std::optional Pointabilities::matchGroups(const ItemGroupList &groups, + const std::unordered_map &pointable_groups) +{ + // prefers POINTABLE over POINTABLE_NOT over POINTABLE_BLOCKING + bool blocking = false; + bool not_pointable = false; + for (auto const &ability : pointable_groups) { + if (itemgroup_get(groups, ability.first) > 0) { + switch(ability.second) { + case PointabilityType::POINTABLE: + return PointabilityType::POINTABLE; + case PointabilityType::POINTABLE_NOT: + not_pointable = true; + break; + default: + blocking = true; + break; + } + } + } + if (not_pointable) + return PointabilityType::POINTABLE_NOT; + if (blocking) + return PointabilityType::POINTABLE_BLOCKING; + return std::nullopt; +} + +void Pointabilities::serializeTypeMap(std::ostream &os, + const std::unordered_map &map) +{ + writeU32(os, map.size()); + for (const auto &entry : map) { + os << serializeString16(entry.first); + writeU8(os, (u8)entry.second); + } +} + +void Pointabilities::deSerializeTypeMap(std::istream &is, + std::unordered_map &map) +{ + map.clear(); + u32 size = readU32(is); + for (u32 i = 0; i < size; i++) { + std::string name = deSerializeString16(is); + PointabilityType type = Pointabilities::deSerializePointabilityType(is); + map[name] = type; + } +} + +void Pointabilities::serialize(std::ostream &os) const +{ + writeU8(os, 0); // version + serializeTypeMap(os, nodes); + serializeTypeMap(os, node_groups); + serializeTypeMap(os, objects); + serializeTypeMap(os, object_groups); +} + +void Pointabilities::deSerialize(std::istream &is) +{ + int version = readU8(is); + if (version != 0) + throw SerializationError("unsupported Pointabilities version"); + + deSerializeTypeMap(is, nodes); + deSerializeTypeMap(is, node_groups); + deSerializeTypeMap(is, objects); + deSerializeTypeMap(is, object_groups); +} diff --git a/src/util/pointabilities.h b/src/util/pointabilities.h new file mode 100644 index 000000000..4acc1ddb7 --- /dev/null +++ b/src/util/pointabilities.h @@ -0,0 +1,70 @@ +/* +Minetest +Copyright (C) 2023 cx384 + +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 +#include +#include "itemgroup.h" +#include +#include "irrlichttypes.h" + +enum class PointabilityType : u8 +{ + POINTABLE, + POINTABLE_NOT, // Can be pointed through. + POINTABLE_BLOCKING, +}; + +// An object to store overridden pointable properties +struct Pointabilities +{ + // Nodes + std::unordered_map nodes; + std::unordered_map node_groups; + + // Objects + std::unordered_map objects; + std::unordered_map object_groups; // armor_groups + + // Match functions return fitting pointability, + // otherwise the default pointability should be used. + + std::optional matchNode(const std::string &name, + const ItemGroupList &groups) const; + std::optional matchObject(const std::string &name, + const ItemGroupList &groups) const; + // For players only armor groups will work + std::optional matchPlayer(const ItemGroupList &groups) const; + + void serialize(std::ostream &os) const; + void deSerialize(std::istream &is); + + // For a save enum conversion. + static PointabilityType deSerializePointabilityType(std::istream &is); + static void serializePointabilityType(std::ostream &os, PointabilityType pointable_type); + static std::string toStringPointabilityType(PointabilityType pointable_type); + +private: + static std::optional matchGroups(const ItemGroupList &groups, + const std::unordered_map &pointable_groups); + static void serializeTypeMap(std::ostream &os, + const std::unordered_map &map); + static void deSerializeTypeMap(std::istream &is, + std::unordered_map &map); +}; diff --git a/src/util/pointedthing.cpp b/src/util/pointedthing.cpp index b4e48c8d5..f87021dc6 100644 --- a/src/util/pointedthing.cpp +++ b/src/util/pointedthing.cpp @@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., PointedThing::PointedThing(const v3s16 &under, const v3s16 &above, const v3s16 &real_under, const v3f &point, const v3f &normal, - u16 box_id, f32 distSq): + u16 box_id, f32 distSq, PointabilityType pointab): type(POINTEDTHING_NODE), node_undersurface(under), node_abovesurface(above), @@ -33,17 +33,19 @@ PointedThing::PointedThing(const v3s16 &under, const v3s16 &above, intersection_point(point), intersection_normal(normal), box_id(box_id), - distanceSq(distSq) + distanceSq(distSq), + pointability(pointab) {} -PointedThing::PointedThing(u16 id, const v3f &point, - const v3f &normal, const v3f &raw_normal, f32 distSq) : +PointedThing::PointedThing(u16 id, const v3f &point, const v3f &normal, + const v3f &raw_normal, f32 distSq, PointabilityType pointab) : type(POINTEDTHING_OBJECT), object_id(id), intersection_point(point), intersection_normal(normal), raw_intersection_normal(raw_normal), - distanceSq(distSq) + distanceSq(distSq), + pointability(pointab) {} std::string PointedThing::dump() const @@ -118,12 +120,13 @@ bool PointedThing::operator==(const PointedThing &pt2) const { if ((node_undersurface != pt2.node_undersurface) || (node_abovesurface != pt2.node_abovesurface) - || (node_real_undersurface != pt2.node_real_undersurface)) + || (node_real_undersurface != pt2.node_real_undersurface) + || (pointability != pt2.pointability)) return false; } else if (type == POINTEDTHING_OBJECT) { - if (object_id != pt2.object_id) + if (object_id != pt2.object_id || pointability != pt2.pointability) return false; } return true; diff --git a/src/util/pointedthing.h b/src/util/pointedthing.h index 276b064b8..c44bcc2a8 100644 --- a/src/util/pointedthing.h +++ b/src/util/pointedthing.h @@ -23,8 +23,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irr_v3d.h" #include #include +#include "pointabilities.h" -enum PointedThingType : u8 +enum PointedThingType :u8 { POINTEDTHING_NOTHING, POINTEDTHING_NODE, @@ -90,15 +91,20 @@ struct PointedThing * ray's start point and the intersection point in irrlicht coordinates. */ f32 distanceSq = 0; + /*! + * How the object or node has been pointed at. + */ + PointabilityType pointability = PointabilityType::POINTABLE_NOT; //! Constructor for POINTEDTHING_NOTHING PointedThing() = default; //! Constructor for POINTEDTHING_NODE PointedThing(const v3s16 &under, const v3s16 &above, const v3s16 &real_under, const v3f &point, const v3f &normal, - u16 box_id, f32 distSq); + u16 box_id, f32 distSq, PointabilityType pointability); //! Constructor for POINTEDTHING_OBJECT - PointedThing(u16 id, const v3f &point, const v3f &normal, const v3f &raw_normal, f32 distSq); + PointedThing(u16 id, const v3f &point, const v3f &normal, const v3f &raw_normal, f32 distSq, + PointabilityType pointability); std::string dump() const; void serialize(std::ostream &os) const; void deSerialize(std::istream &is);