From 4acba9b64832890075d83b0a8fe9af0d357aa103 Mon Sep 17 00:00:00 2001 From: Lizzy Fleckenstein Date: Sat, 10 Dec 2022 15:31:22 +0100 Subject: [PATCH 1/6] Dual wielding --- doc/lua_api.md | 20 +- src/client/camera.cpp | 390 ++++++++++++++++++---------- src/client/camera.h | 69 +++-- src/client/client.cpp | 2 + src/client/game.cpp | 41 +-- src/itemdef.cpp | 10 + src/itemdef.h | 4 + src/network/serverpackethandler.cpp | 58 +++-- src/player.cpp | 40 +++ src/player.h | 8 + src/script/common/c_content.cpp | 10 + src/server/player_sao.cpp | 15 ++ src/server/player_sao.h | 3 + 13 files changed, 464 insertions(+), 206 deletions(-) diff --git a/doc/lua_api.md b/doc/lua_api.md index 796a0f39e..1c16fbe1f 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -3660,6 +3660,16 @@ Player Inventory lists * `hand`: list containing an override for the empty hand * Is not created automatically, use `InvRef:set_size` * Is only used to enhance the empty hand's tool capabilities +* `offhand`: list containing the offhand wielded item. + * Is not created automatically, use `InvRef:set_size` + * Will be used for placements and secondary uses if the + main hand does not have any node_place_prediction, on_place + or on_secondary_use callbacks. + * Is passed to on_place and on_secondary_use callbacks; make sure + mods are aware of the itemstack not neccessarily being + located in the main hand. + * The offhand item cannot have its own range or liquids_pointable and + will always reuse the characteristics from the hand item. ItemStack transaction order --------------------------- @@ -9173,18 +9183,18 @@ Used by `minetest.register_node`, `minetest.register_craftitem`, and }, on_place = function(itemstack, placer, pointed_thing), - -- When the 'place' key was pressed with the item in hand + -- When the 'place' key was pressed with the item one of the hands -- and a node was pointed at. + -- 'itemstack' may be the offhand item in cases where the main hand has + -- no on_place handler and no node_placement_prediction. -- Shall place item and return the leftover itemstack -- or nil to not modify the inventory. -- The placer may be any ObjectRef or nil. -- default: minetest.item_place on_secondary_use = function(itemstack, user, pointed_thing), - -- Same as on_place but called when not pointing at a node. - -- Function must return either nil if inventory shall not be modified, - -- or an itemstack to replace the original itemstack. - -- The user may be any ObjectRef or nil. + -- Same as on_place but called when not pointing at a node, + -- whereas `user` is the same as `placer` above. -- default: nil on_drop = function(itemstack, dropper, pos), diff --git a/src/client/camera.cpp b/src/client/camera.cpp index fda9948e9..674fe3757 100644 --- a/src/client/camera.cpp +++ b/src/client/camera.cpp @@ -45,6 +45,236 @@ with this program; if not, write to the Free Software Foundation, Inc., #define WIELDMESH_AMPLITUDE_X 7.0f #define WIELDMESH_AMPLITUDE_Y 10.0f +// Returns the fractional part of x +inline f32 my_modf(f32 x) +{ + f32 dummy; + return modff(x, &dummy); +} + +WieldNode::WieldNode(HandIndex index, Client *client, scene::ISceneManager *mgr) : + m_index(index), + m_direction(index == MAINHAND ? +1 : -1), + m_client(client), + m_meshnode(new WieldMeshSceneNode(mgr, -1, false)), + m_player_light_color(0xFFFFFFFF) +{ + m_meshnode->setItem(ItemStack(), m_client); + m_meshnode->drop(); // mgr grabbed it +} + +void WieldNode::step(f32 dtime) +{ + bool was_under_zero = m_change_timer < 0; + m_change_timer = MYMIN(m_change_timer + dtime, 0.125); + + if (m_change_timer >= 0 && was_under_zero) { + m_meshnode->setItem(m_item_next, m_client); + m_meshnode->setNodeLightColor(m_player_light_color); + } + + if (m_digging_button == -1) + return; + + f32 offset = dtime * 3.5f; + float m_digging_anim_was = m_digging_anim; + m_digging_anim += offset; + if (m_digging_anim >= 1) + { + m_digging_anim = 0; + m_digging_button = -1; + } + float lim = 0.15; + if(m_digging_anim_was < lim && m_digging_anim >= lim) + { + if (m_digging_button == 0) { + m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::CAMERA_PUNCH_LEFT)); + } else if(m_digging_button == 1) { + m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::CAMERA_PUNCH_RIGHT)); + } + } +} + +static inline v2f dir(const v2f &pos_dist) +{ + f32 x = pos_dist.X - WIELDMESH_OFFSET_X; + f32 y = pos_dist.Y - WIELDMESH_OFFSET_Y; + + f32 x_abs = std::fabs(x); + f32 y_abs = std::fabs(y); + + if (x_abs >= y_abs) { + y *= (1.0f / x_abs); + x /= x_abs; + } + + if (y_abs >= x_abs) { + x *= (1.0f / y_abs); + y /= y_abs; + } + + return v2f(std::fabs(x), std::fabs(y)); +} + +void WieldNode::addArmInertia(f32 player_yaw, v3f camera_direction) +{ + m_cam_vel.X = std::fabs(rangelim(m_last_cam_pos.X - player_yaw, + -100.0f, 100.0f) / 0.016f) * 0.01f; + m_cam_vel.Y = std::fabs((m_last_cam_pos.Y - camera_direction.Y) / 0.016f); + f32 gap_X = std::fabs(WIELDMESH_OFFSET_X - m_offset.X); + f32 gap_Y = std::fabs(WIELDMESH_OFFSET_Y - m_offset.Y); + + if (m_cam_vel.X > 1.0f || m_cam_vel.Y > 1.0f) { + /* + The arm moves relative to the camera speed, + with an acceleration factor. + */ + + if (m_cam_vel.X > 1.0f) { + if (m_cam_vel.X > m_cam_vel_old.X) + m_cam_vel_old.X = m_cam_vel.X; + + f32 acc_X = 0.12f * (m_cam_vel.X - (gap_X * 0.1f)); + m_offset.X += (m_last_cam_pos.X < player_yaw ? acc_X : -acc_X) * m_direction; + + if (m_last_cam_pos.X != player_yaw) + m_last_cam_pos.X = player_yaw; + + m_offset.X = rangelim(m_offset.X, + WIELDMESH_OFFSET_X - (WIELDMESH_AMPLITUDE_X * 0.5f), + WIELDMESH_OFFSET_X + (WIELDMESH_AMPLITUDE_X * 0.5f)); + } + + if (m_cam_vel.Y > 1.0f) { + if (m_cam_vel.Y > m_cam_vel_old.Y) + m_cam_vel_old.Y = m_cam_vel.Y; + + f32 acc_Y = 0.12f * (m_cam_vel.Y - (gap_Y * 0.1f)); + m_offset.Y += + m_last_cam_pos.Y > camera_direction.Y ? acc_Y : -acc_Y; + + if (m_last_cam_pos.Y != camera_direction.Y) + m_last_cam_pos.Y = camera_direction.Y; + + m_offset.Y = rangelim(m_offset.Y, + WIELDMESH_OFFSET_Y - (WIELDMESH_AMPLITUDE_Y * 0.5f), + WIELDMESH_OFFSET_Y + (WIELDMESH_AMPLITUDE_Y * 0.5f)); + } + + m_arm_dir = dir(m_offset); + } else { + /* + Now the arm gets back to its default position when the camera stops, + following a vector, with a smooth deceleration factor. + */ + + f32 dec_X = 0.35f * (std::min(15.0f, m_cam_vel_old.X) * (1.0f + + (1.0f - m_arm_dir.X))) * (gap_X / 20.0f); + + f32 dec_Y = 0.25f * (std::min(15.0f, m_cam_vel_old.Y) * (1.0f + + (1.0f - m_arm_dir.Y))) * (gap_Y / 15.0f); + + if (gap_X < 0.1f) + m_cam_vel_old.X = 0.0f; + + m_offset.X -= + m_offset.X > WIELDMESH_OFFSET_X ? dec_X : -dec_X; + + if (gap_Y < 0.1f) + m_cam_vel_old.Y = 0.0f; + + m_offset.Y -= + m_offset.Y > WIELDMESH_OFFSET_Y ? dec_Y : -dec_Y; + } +} + +void WieldNode::update(video::SColor player_light_color, f32 view_bobbing_anim, f32 tool_reload_ratio) +{ + m_player_light_color = player_light_color; + + // Position the wielded item + //v3f pos = v3f(45, -35, 65); + v3f pos = v3f(m_offset.X, m_offset.Y, 65); + //v3f rot = v3f(-100, 120, -100); + v3f rot = v3f(-100, 120, -100); + + if (m_index == OFFHAND) + tool_reload_ratio = 1.0f; + + pos.Y += fabs(m_change_timer)*320 - 40; + if(m_digging_anim < 0.05 || m_digging_anim > 0.5) + { + f32 frac = 1.0; + if(m_digging_anim > 0.5) + frac = 2.0 * (m_digging_anim - 0.5); + // This value starts from 1 and settles to 0 + f32 ratiothing = std::pow((1.0f - tool_reload_ratio), 0.5f); + //f32 ratiothing2 = pow(ratiothing, 0.5f); + f32 ratiothing2 = (easeCurve(ratiothing*0.5))*2.0; + pos.Y -= frac * 25.0 * pow(ratiothing2, 1.7f); + //rot.Z += frac * 5.0 * ratiothing2; + pos.X -= frac * 35.0 * pow(ratiothing2, 1.1f); + rot.Y += frac * 70.0 * pow(ratiothing2, 1.4f); + //rot.X -= frac * 15.0 * pow(ratiothing2, 1.4f); + //rot.Z += frac * 15.0 * pow(ratiothing2, 1.0f); + } + if (m_digging_button != -1) + { + f32 digfrac = m_digging_anim; + pos.X -= 50 * sin(pow(digfrac, 0.8f) * M_PI); + pos.Y += 24 * sin(digfrac * 1.8 * M_PI); + pos.Z += 25 * 0.5; + + // Euler angles are PURE EVIL, so why not use quaternions? + core::quaternion quat_begin(rot * core::DEGTORAD); + //core::quaternion quat_end(v3f(s * 80, 30, s * 100) * core::DEGTORAD); + core::quaternion quat_end(v3f(80, 30, 100) * core::DEGTORAD); + core::quaternion quat_slerp; + quat_slerp.slerp(quat_begin, quat_end, sin(digfrac * M_PI)); + quat_slerp.W *= m_direction; + quat_slerp.X *= m_direction; + quat_slerp.toEuler(rot); + rot *= core::RADTODEG; + pos.X *= m_direction; + } else { + f32 bobfrac = my_modf(view_bobbing_anim); + pos.X *= m_direction; + pos.X -= sin(bobfrac*M_PI*2.0+M_PI*m_index) * 3.0 * m_direction; + pos.Y += sin(my_modf(bobfrac*2.0)*M_PI+M_PI*m_index) * 3.0; + } + + m_meshnode->setPosition(pos); + m_meshnode->setRotation(rot); + + m_meshnode->setNodeLightColor(m_player_light_color); + + if (m_index == OFFHAND) { + m_meshnode->setVisible( + m_change_timer > 0 ? !m_item_next.name.empty() : m_item_old); + } + +} + +void WieldNode::setDigging(s32 button) +{ + if (m_digging_button == -1) + m_digging_button = button; +} + +void WieldNode::wield(const ItemStack &item) +{ + if (item.name == m_item_next.name && + item.metadata == m_item_next.metadata) + return; + + m_item_old = m_item_next.name != ""; + m_item_next = item; + if (m_change_timer > 0) + m_change_timer = -m_change_timer; + else if (m_change_timer == 0) + m_change_timer = -0.001; +} + Camera::Camera(MapDrawControl &draw_control, Client *client, RenderingEngine *rendering_engine): m_draw_control(draw_control), m_client(client), @@ -62,9 +292,9 @@ Camera::Camera(MapDrawControl &draw_control, Client *client, RenderingEngine *re // all other 3D scene nodes and before the GUI. m_wieldmgr = smgr->createNewSceneManager(); m_wieldmgr->addCameraSceneNode(); - m_wieldnode = new WieldMeshSceneNode(m_wieldmgr, -1, false); - m_wieldnode->setItem(ItemStack(), m_client); - m_wieldnode->drop(); // m_wieldmgr grabbed it + + m_wieldnodes[MAINHAND] = new WieldNode(MAINHAND, m_client, m_wieldmgr); + m_wieldnodes[ OFFHAND] = new WieldNode( OFFHAND, m_client, m_wieldmgr); /* TODO: Add a callback function so these can be updated when a setting * changes. At this point in time it doesn't matter (e.g. /set @@ -87,6 +317,8 @@ Camera::Camera(MapDrawControl &draw_control, Client *client, RenderingEngine *re Camera::~Camera() { + for (auto node : m_wieldnodes) + delete node; m_wieldmgr->drop(); } @@ -144,6 +376,9 @@ inline f32 my_modf(f32 x) void Camera::step(f32 dtime) { + for (auto node : m_wieldnodes) + node->step(dtime); + if(m_view_bobbing_fall > 0) { m_view_bobbing_fall -= 3 * dtime; @@ -218,97 +453,10 @@ void Camera::step(f32 dtime) } } -static inline v2f dir(const v2f &pos_dist) -{ - f32 x = pos_dist.X - WIELDMESH_OFFSET_X; - f32 y = pos_dist.Y - WIELDMESH_OFFSET_Y; - - f32 x_abs = std::fabs(x); - f32 y_abs = std::fabs(y); - - if (x_abs >= y_abs) { - y *= (1.0f / x_abs); - x /= x_abs; - } - - if (y_abs >= x_abs) { - x *= (1.0f / y_abs); - y /= y_abs; - } - - return v2f(std::fabs(x), std::fabs(y)); -} - void Camera::addArmInertia(f32 player_yaw) { - m_cam_vel.X = std::fabs(rangelim(m_last_cam_pos.X - player_yaw, - -100.0f, 100.0f) / 0.016f) * 0.01f; - m_cam_vel.Y = std::fabs((m_last_cam_pos.Y - m_camera_direction.Y) / 0.016f); - f32 gap_X = std::fabs(WIELDMESH_OFFSET_X - m_wieldmesh_offset.X); - f32 gap_Y = std::fabs(WIELDMESH_OFFSET_Y - m_wieldmesh_offset.Y); - - if (m_cam_vel.X > 1.0f || m_cam_vel.Y > 1.0f) { - /* - The arm moves relative to the camera speed, - with an acceleration factor. - */ - - if (m_cam_vel.X > 1.0f) { - if (m_cam_vel.X > m_cam_vel_old.X) - m_cam_vel_old.X = m_cam_vel.X; - - f32 acc_X = 0.12f * (m_cam_vel.X - (gap_X * 0.1f)); - m_wieldmesh_offset.X += m_last_cam_pos.X < player_yaw ? acc_X : -acc_X; - - if (m_last_cam_pos.X != player_yaw) - m_last_cam_pos.X = player_yaw; - - m_wieldmesh_offset.X = rangelim(m_wieldmesh_offset.X, - WIELDMESH_OFFSET_X - (WIELDMESH_AMPLITUDE_X * 0.5f), - WIELDMESH_OFFSET_X + (WIELDMESH_AMPLITUDE_X * 0.5f)); - } - - if (m_cam_vel.Y > 1.0f) { - if (m_cam_vel.Y > m_cam_vel_old.Y) - m_cam_vel_old.Y = m_cam_vel.Y; - - f32 acc_Y = 0.12f * (m_cam_vel.Y - (gap_Y * 0.1f)); - m_wieldmesh_offset.Y += - m_last_cam_pos.Y > m_camera_direction.Y ? acc_Y : -acc_Y; - - if (m_last_cam_pos.Y != m_camera_direction.Y) - m_last_cam_pos.Y = m_camera_direction.Y; - - m_wieldmesh_offset.Y = rangelim(m_wieldmesh_offset.Y, - WIELDMESH_OFFSET_Y - (WIELDMESH_AMPLITUDE_Y * 0.5f), - WIELDMESH_OFFSET_Y + (WIELDMESH_AMPLITUDE_Y * 0.5f)); - } - - m_arm_dir = dir(m_wieldmesh_offset); - } else { - /* - Now the arm gets back to its default position when the camera stops, - following a vector, with a smooth deceleration factor. - */ - - f32 dec_X = 0.35f * (std::min(15.0f, m_cam_vel_old.X) * (1.0f + - (1.0f - m_arm_dir.X))) * (gap_X / 20.0f); - - f32 dec_Y = 0.25f * (std::min(15.0f, m_cam_vel_old.Y) * (1.0f + - (1.0f - m_arm_dir.Y))) * (gap_Y / 15.0f); - - if (gap_X < 0.1f) - m_cam_vel_old.X = 0.0f; - - m_wieldmesh_offset.X -= - m_wieldmesh_offset.X > WIELDMESH_OFFSET_X ? dec_X : -dec_X; - - if (gap_Y < 0.1f) - m_cam_vel_old.Y = 0.0f; - - m_wieldmesh_offset.Y -= - m_wieldmesh_offset.Y > WIELDMESH_OFFSET_Y ? dec_Y : -dec_Y; - } + for (auto node : m_wieldnodes) + node->addArmInertia(player_yaw, m_camera_direction); } void Camera::update(LocalPlayer* player, f32 frametime, f32 tool_reload_ratio) @@ -530,46 +678,10 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 tool_reload_ratio) if (m_arm_inertia) addArmInertia(yaw); - // Position the wielded item - v3f wield_position = v3f(m_wieldmesh_offset.X, m_wieldmesh_offset.Y, 65); - v3f wield_rotation = v3f(-100, 120, -100); - wield_position.Y += std::abs(m_wield_change_timer)*320 - 40; - if(m_digging_anim < 0.05 || m_digging_anim > 0.5) - { - f32 frac = 1.0; - if(m_digging_anim > 0.5) - frac = 2.0 * (m_digging_anim - 0.5); - // This value starts from 1 and settles to 0 - f32 ratiothing = std::pow((1.0f - tool_reload_ratio), 0.5f); - f32 ratiothing2 = (easeCurve(ratiothing*0.5))*2.0; - wield_position.Y -= frac * 25.0f * std::pow(ratiothing2, 1.7f); - wield_position.X -= frac * 35.0f * std::pow(ratiothing2, 1.1f); - wield_rotation.Y += frac * 70.0f * std::pow(ratiothing2, 1.4f); - } - if (m_digging_button != -1) - { - f32 digfrac = m_digging_anim; - wield_position.X -= 50 * std::sin(std::pow(digfrac, 0.8f) * M_PI); - wield_position.Y += 24 * std::sin(digfrac * 1.8 * M_PI); - wield_position.Z += 25 * 0.5; - - // Euler angles are PURE EVIL, so why not use quaternions? - core::quaternion quat_begin(wield_rotation * core::DEGTORAD); - core::quaternion quat_end(v3f(80, 30, 100) * core::DEGTORAD); - core::quaternion quat_slerp; - quat_slerp.slerp(quat_begin, quat_end, std::sin(digfrac * M_PI)); - quat_slerp.toEuler(wield_rotation); - wield_rotation *= core::RADTODEG; - } else { - f32 bobfrac = my_modf(m_view_bobbing_anim); - wield_position.X -= std::sin(bobfrac*M_PI*2.0) * 3.0; - wield_position.Y += std::sin(my_modf(bobfrac*2.0)*M_PI) * 3.0; - } - m_wieldnode->setPosition(wield_position); - m_wieldnode->setRotation(wield_rotation); - m_player_light_color = player->light_color; - m_wieldnode->setNodeLightColor(m_player_light_color); + + for (auto node : m_wieldnodes) + node->update(m_player_light_color, m_view_bobbing_anim, tool_reload_ratio); // Set render distance updateViewingRange(); @@ -611,22 +723,14 @@ void Camera::updateViewingRange() m_cameranode->setFarValue((viewing_range < 2000) ? 2000 * BS : viewing_range * BS); } -void Camera::setDigging(s32 button) +void Camera::setDigging(s32 button, HandIndex hand) { - if (m_digging_button == -1) - m_digging_button = button; + m_wieldnodes[hand]->setDigging(button); } -void Camera::wield(const ItemStack &item) +void Camera::wield(const ItemStack &item, HandIndex hand) { - if (item.name != m_wield_item_next.name || - item.metadata != m_wield_item_next.metadata) { - m_wield_item_next = item; - if (m_wield_change_timer > 0) - m_wield_change_timer = -m_wield_change_timer; - else if (m_wield_change_timer == 0) - m_wield_change_timer = -0.001; - } + m_wieldnodes[hand]->wield(item); } void Camera::drawWieldedTool(irr::core::matrix4* translation) diff --git a/src/client/camera.h b/src/client/camera.h index d6aa0bc54..d33b99039 100644 --- a/src/client/camera.h +++ b/src/client/camera.h @@ -72,6 +72,49 @@ struct Nametag } }; +enum HandIndex { MAINHAND = 0, OFFHAND = 1 }; + +class WieldNode +{ +public: + WieldNode(HandIndex index, Client *client, scene::ISceneManager *mgr); + void step(f32 dtime); + void addArmInertia(f32 player_yaw, v3f camera_direction); + void update(video::SColor player_light_color, f32 view_bobbing_anim, f32 tool_reload_ratio); + void setDigging(s32 button); + void wield(const ItemStack &item); + +private: + HandIndex m_index; + int m_direction; + + Client *m_client; + WieldMeshSceneNode *m_meshnode = nullptr; + + // Digging animation frame (0 <= m_digging_anim < 1) + f32 m_digging_anim = 0.0f; + + // If -1, no digging animation + // If 0, left-click digging animation + // If 1, right-click digging animation + s32 m_digging_button = -1; + + // Animation when changing wielded item + f32 m_change_timer = 0.125f; + ItemStack m_item_next; + bool m_item_old = false; + + // Last known light color of the player + video::SColor m_player_light_color; + + // Arm inertia + v2f m_offset = v2f(55.0f, -35.0f); + v2f m_arm_dir; + v2f m_cam_vel; + v2f m_cam_vel_old; + v2f m_last_cam_pos; +}; + enum CameraMode {CAMERA_MODE_FIRST, CAMERA_MODE_THIRD, CAMERA_MODE_THIRD_FRONT}; /* @@ -166,11 +209,11 @@ class Camera void updateViewingRange(); // Start digging animation - // Pass 0 for left click, 1 for right click - void setDigging(s32 button); + // button: Pass 0 for left click, 1 for right click + void setDigging(s32 button, HandIndex hand); // Replace the wielded item mesh - void wield(const ItemStack &item); + void wield(const ItemStack &item, HandIndex hand); // Draw the wielded tool. // This has to happen *after* the main scene is drawn. @@ -219,8 +262,9 @@ class Camera scene::ISceneNode *m_headnode = nullptr; scene::ICameraSceneNode *m_cameranode = nullptr; + WieldNode *m_wieldnodes[2]; + scene::ISceneManager *m_wieldmgr = nullptr; - WieldMeshSceneNode *m_wieldnode = nullptr; // draw control MapDrawControl& m_draw_control; @@ -247,12 +291,6 @@ class Camera bool m_fov_transition_active = false; f32 m_fov_diff, m_transition_time; - v2f m_wieldmesh_offset = v2f(55.0f, -35.0f); - v2f m_arm_dir; - v2f m_cam_vel; - v2f m_cam_vel_old; - v2f m_last_cam_pos; - // Field of view and aspect ratio stuff f32 m_aspect = 1.0f; f32 m_fov_x = 1.0f; @@ -269,17 +307,6 @@ class Camera // Fall view bobbing f32 m_view_bobbing_fall = 0.0f; - // Digging animation frame (0 <= m_digging_anim < 1) - f32 m_digging_anim = 0.0f; - // If -1, no digging animation - // If 0, left-click digging animation - // If 1, right-click digging animation - s32 m_digging_button = -1; - - // Animation when changing wielded item - f32 m_wield_change_timer = 0.125f; - ItemStack m_wield_item_next; - CameraMode m_camera_mode = CAMERA_MODE_FIRST; f32 m_cache_fall_bobbing_amount; diff --git a/src/client/client.cpp b/src/client/client.cpp index ca3f91e78..0572f5207 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -1570,6 +1570,8 @@ bool Client::updateWieldedItem() list->setModified(false); if (auto *list = player->inventory.getList("hand")) list->setModified(false); + if (auto *list = player->inventory.getList("offhand")) + list->setModified(false); return true; } diff --git a/src/client/game.cpp b/src/client/game.cpp index 55ec480b5..0084c795c 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -777,7 +777,8 @@ class Game { bool look_for_object, const v3s16 &camera_offset); void handlePointingAtNothing(const ItemStack &playerItem); void handlePointingAtNode(const PointedThing &pointed, - const ItemStack &selected_item, const ItemStack &hand_item, f32 dtime); + const ItemStack &selected_item, const ItemStack &hand_item, + const ItemStack &place_item, f32 dtime); void handlePointingAtObject(const PointedThing &pointed, const ItemStack &playeritem, const v3f &player_position, bool show_debug); void handleDigging(const PointedThing &pointed, const v3s16 &nodepos, @@ -1834,19 +1835,19 @@ inline bool Game::handleCallbacks() if (g_gamecallback->changepassword_requested) { (new GUIPasswordChange(guienv, guiroot, -1, - &g_menumgr, client, texture_src))->drop(); + &g_menumgr, client, texture_src))->drop(); g_gamecallback->changepassword_requested = false; } if (g_gamecallback->changevolume_requested) { (new GUIVolumeChange(guienv, guiroot, -1, - &g_menumgr, texture_src))->drop(); + &g_menumgr, texture_src))->drop(); g_gamecallback->changevolume_requested = false; } if (g_gamecallback->keyconfig_requested) { (new GUIKeyChangeMenu(guienv, guiroot, -1, - &g_menumgr, texture_src))->drop(); + &g_menumgr, texture_src))->drop(); g_gamecallback->keyconfig_requested = false; } @@ -3270,7 +3271,7 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud) Calculate what block is the crosshair pointing to */ - ItemStack selected_item, hand_item; + ItemStack selected_item, hand_item, offhand_item, place_item; const ItemStack &tool_item = player->getWieldedItem(&selected_item, &hand_item); const ItemDefinition &selected_def = selected_item.getDefinition(itemdef_manager); @@ -3310,6 +3311,8 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud) !runData.btn_down_for_dig, camera_offset); + player->getOffhandWieldedItem(&offhand_item, &place_item, itemdef_manager, pointed); + if (pointed != runData.pointed_old) infostream << "Pointing at " << pointed.dump() << std::endl; @@ -3380,7 +3383,7 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud) !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); + handlePointingAtNode(pointed, selected_item, hand_item, place_item, dtime); } else if (pointed.type == POINTEDTHING_OBJECT) { v3f player_position = player->getPosition(); bool basic_debug_allowed = client->checkPrivilege("debug") || (player->hud_flags & HUD_FLAG_BASIC_DEBUG); @@ -3393,13 +3396,13 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud) if (wasKeyPressed(KeyType::DIG) && client->modsLoaded()) client->getScript()->on_item_use(selected_item, pointed); } else if (wasKeyPressed(KeyType::PLACE)) { - handlePointingAtNothing(selected_item); + handlePointingAtNothing(place_item); } runData.pointed_old = pointed; if (runData.punching || wasKeyPressed(KeyType::DIG)) - camera->setDigging(0); // dig animation + camera->setDigging(0, MAINHAND); // dig animation input->clearWasKeyPressed(); input->clearWasKeyReleased(); @@ -3524,7 +3527,8 @@ void Game::handlePointingAtNothing(const ItemStack &playerItem) void Game::handlePointingAtNode(const PointedThing &pointed, - const ItemStack &selected_item, const ItemStack &hand_item, f32 dtime) + const ItemStack &selected_item, const ItemStack &hand_item, + const ItemStack &place_item, f32 dtime) { v3s16 nodepos = pointed.node_undersurface; v3s16 neighborpos = pointed.node_abovesurface; @@ -3562,7 +3566,10 @@ void Game::handlePointingAtNode(const PointedThing &pointed, infostream << "Place button pressed while looking at ground" << std::endl; // Placing animation (always shown for feedback) - camera->setDigging(1); + if (place_item == selected_item) + camera->setDigging(1, MAINHAND); + else + camera->setDigging(1, OFFHAND); soundmaker->m_player_rightpunch_sound = SoundSpec(); @@ -3570,8 +3577,8 @@ void Game::handlePointingAtNode(const PointedThing &pointed, // make that happen // And also set the sound and send the interact // But first check for meta formspec and rightclickable - auto &def = selected_item.getDefinition(itemdef_manager); - bool placed = nodePlacement(def, selected_item, nodepos, neighborpos, + auto &def = place_item.getDefinition(itemdef_manager); + bool placed = nodePlacement(def, place_item, nodepos, neighborpos, pointed, meta); if (placed && client->modsLoaded()) @@ -3995,7 +4002,7 @@ void Game::handleDigging(const PointedThing &pointed, const v3s16 &nodepos, client->setCrack(-1, nodepos); } - camera->setDigging(0); // Dig animation + camera->setDigging(0, MAINHAND); // Dig animation } void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, @@ -4042,7 +4049,7 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, direct_brightness = client->getEnv().getClientMap() .getBackgroundBrightness(MYMIN(runData.fog_range * 1.2, 60 * BS), daynight_ratio, (int)(old_brightness * 255.5), &sunlight_seen) - / 255.0; + / 255.0; } float time_of_day_smooth = runData.time_of_day_smooth; @@ -4113,9 +4120,11 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, if (client->updateWieldedItem()) { // Update wielded tool - ItemStack selected_item, hand_item; + ItemStack selected_item, hand_item, offhand_item; ItemStack &tool_item = player->getWieldedItem(&selected_item, &hand_item); - camera->wield(tool_item); + camera->wield(tool_item, MAINHAND); + player->getOffhandWieldedItem(&offhand_item, nullptr, itemdef_manager, PointedThing()); + camera->wield(offhand_item, OFFHAND); } /* diff --git a/src/itemdef.cpp b/src/itemdef.cpp index ad2ed4847..51c6215a5 100644 --- a/src/itemdef.cpp +++ b/src/itemdef.cpp @@ -142,6 +142,8 @@ ItemDefinition& ItemDefinition::operator=(const ItemDefinition &def) node_placement_prediction = def.node_placement_prediction; place_param2 = def.place_param2; wallmounted_rotate_vertical = def.wallmounted_rotate_vertical; + has_on_place = def.has_on_place; + has_on_secondary_use = def.has_on_secondary_use; sound_place = def.sound_place; sound_place_failed = def.sound_place_failed; sound_use = def.sound_use; @@ -195,6 +197,8 @@ void ItemDefinition::reset() node_placement_prediction.clear(); place_param2.reset(); wallmounted_rotate_vertical = false; + has_on_place = false; + has_on_secondary_use = false; touch_interaction = TouchInteraction(); } @@ -252,6 +256,9 @@ void ItemDefinition::serialize(std::ostream &os, u16 protocol_version) const sound_use.serializeSimple(os, protocol_version); sound_use_air.serializeSimple(os, protocol_version); + writeU8(os, has_on_place); + writeU8(os, has_on_secondary_use); + os << (u8)place_param2.has_value(); // protocol_version >= 43 if (place_param2) os << *place_param2; @@ -340,6 +347,9 @@ void ItemDefinition::deSerialize(std::istream &is, u16 protocol_version) sound_use.deSerializeSimple(is, protocol_version); sound_use_air.deSerializeSimple(is, protocol_version); + has_on_place = readU8(is); + has_on_secondary_use = readU8(is); + if (is.eof()) throw SerializationError(""); diff --git a/src/itemdef.h b/src/itemdef.h index 782dad816..3020afdd3 100644 --- a/src/itemdef.h +++ b/src/itemdef.h @@ -121,6 +121,10 @@ struct ItemDefinition // "" = no prediction std::string node_placement_prediction; std::optional place_param2; + + bool has_on_place; + bool has_on_secondary_use; + bool wallmounted_rotate_vertical; TouchInteraction touch_interaction; diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index 2c6f024ee..8a8498892 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -893,9 +893,9 @@ void Server::handleCommand_Respawn(NetworkPacket* pkt) bool Server::checkInteractDistance(RemotePlayer *player, const f32 d, const std::string &what) { - ItemStack selected_item, hand_item; - player->getWieldedItem(&selected_item, &hand_item); - f32 max_d = BS * getToolRange(selected_item, hand_item, m_itemdef); + ItemStack selected_item, main_item; + player->getWieldedItem(&selected_item, &main_item); + f32 max_d = BS * getToolRange(selected_item, main_item, m_itemdef); // Cube diagonal * 1.5 for maximal supported node extents: // sqrt(3) * 1.5 ≅ 2.6 @@ -919,6 +919,14 @@ static inline void getWieldedItem(const PlayerSAO *playersao, std::optionalgetWieldedItem(&(*ret)); } +static inline bool getOffhandWieldedItem(const PlayerSAO *playersao, Optional &offhand, + Optional &place, IItemDefManager *idef, const PointedThing &pointed) +{ + offhand = ItemStack(); + place = ItemStack(); + return playersao->getOffhandWieldedItem(&(*offhand), &(*place), idef, pointed); +} + void Server::handleCommand_Interact(NetworkPacket *pkt) { /* @@ -1159,8 +1167,8 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) // Get player's wielded item // See also: Game::handleDigging - ItemStack selected_item, hand_item; - player->getWieldedItem(&selected_item, &hand_item); + ItemStack selected_item, main_item; + player->getWieldedItem(&selected_item, &main_item); // Get diggability and expected digging time DigParams params = getDigParams(m_nodedef->get(n).groups, @@ -1169,7 +1177,7 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) // If can't dig, try hand if (!params.diggable) { params = getDigParams(m_nodedef->get(n).groups, - &hand_item.getToolCapabilities(m_itemdef)); + &main_item.getToolCapabilities(m_itemdef)); } // If can't dig, ignore dig if (!params.diggable) { @@ -1227,15 +1235,16 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) // Place block or right-click object case INTERACT_PLACE: { - std::optional selected_item; - getWieldedItem(playersao, selected_item); + std::optional main_item, offhand_item, place_item; + getWieldedItem(playersao, main_item); + bool use_offhand = getOffhandWieldedItem(playersao, offhand_item, place_item, m_itemdef, pointed); // Reset build time counter if (pointed.type == POINTEDTHING_NODE && - selected_item->getDefinition(m_itemdef).type == ITEM_NODE) + place_item->getDefinition(m_itemdef).type == ITEM_NODE) getClient(peer_id)->m_time_from_building = 0.0; - const bool had_prediction = !selected_item->getDefinition(m_itemdef). + const bool had_prediction = !place_item->getDefinition(m_itemdef). node_placement_prediction.empty(); if (pointed.type == POINTEDTHING_OBJECT) { @@ -1250,18 +1259,22 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) << pointed_object->getDescription() << std::endl; // Do stuff - if (m_script->item_OnSecondaryUse(selected_item, playersao, pointed)) { - if (selected_item.has_value() && playersao->setWieldedItem(*selected_item)) - SendInventory(player, true); + if (m_script->item_OnSecondaryUse(use_offhand ? offhand_item : main_item, playersao, pointed)) { + if (use_offhand + ? (offhand_item.has_value() && playersao->setOffhandWieldedItem(*offhand_item)) + : (main_item.has_value() && playersao->setWieldedItem(*main_item))) + SendInventory(playersao, true); } pointed_object->rightClick(playersao); - } else if (m_script->item_OnPlace(selected_item, playersao, pointed)) { + } else if (m_script->item_OnPlace(use_offhand ? offhand_item : main_item, playersao, pointed)) { // Placement was handled in lua // Apply returned ItemStack - if (selected_item.has_value() && playersao->setWieldedItem(*selected_item)) - SendInventory(player, true); + if (use_offhand + ? (offhand_item.has_value() && playersao->setOffhandWieldedItem(*offhand_item)) + : (main_item.has_value() && playersao->setWieldedItem(*main_item))) + SendInventory(playersao, true); } if (pointed.type != POINTEDTHING_NODE) @@ -1303,17 +1316,20 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) // Rightclick air case INTERACT_ACTIVATE: { - std::optional selected_item; - getWieldedItem(playersao, selected_item); + std::optional main_item, offhand_item, place_item; + getWieldedItem(playersao, main_item); + bool use_offhand = getOffhandWieldedItem(playersao, offhand_item, place_item, m_itemdef, pointed); actionstream << player->getName() << " activates " - << selected_item->name << std::endl; + << place_item->name << std::endl; pointed.type = POINTEDTHING_NOTHING; // can only ever be NOTHING - if (m_script->item_OnSecondaryUse(selected_item, playersao, pointed)) { + if (m_script->item_OnSecondaryUse(use_offhand ? offhand_item : main_item, playersao, pointed)) { // Apply returned ItemStack - if (selected_item.has_value() && playersao->setWieldedItem(*selected_item)) + if (use_offhand + ? (offhand_item.has_value() && playersao->setOffhandWieldedItem(*offhand_item)) + : (main_item.has_value() && playersao->setWieldedItem(*main_item))) SendInventory(player, true); } diff --git a/src/player.cpp b/src/player.cpp index c876b6948..2079bfea3 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -105,6 +105,46 @@ ItemStack &Player::getWieldedItem(ItemStack *selected, ItemStack *hand) const return (hand && selected->name.empty()) ? *hand : *selected; } +bool Player::getOffhandWieldedItem(ItemStack *offhand, ItemStack *place, IItemDefManager *idef, const PointedThing &pointed) const +{ + assert(offhand); + + ItemStack main; + + const InventoryList *mlist = inventory.getList("main"); + const InventoryList *olist = inventory.getList("offhand"); + + if (olist) + *offhand = olist->getItem(0); + + if (mlist && m_wield_index < mlist->getSize()) + main = mlist->getItem(m_wield_index); + + const ItemDefinition &main_def = main.getDefinition(idef); + const ItemDefinition &offhand_def = offhand->getDefinition(idef); + bool main_usable, offhand_usable; + + // figure out which item to use for placements + + if (pointed.type == POINTEDTHING_NODE) { + // an item can be used on nodes if it has a place handler or prediction + main_usable = main_def.has_on_place || main_def.node_placement_prediction != ""; + offhand_usable = offhand_def.has_on_place || offhand_def.node_placement_prediction != ""; + } else { + // an item can be used on anything else if it has a secondary use handler + main_usable = main_def.has_on_secondary_use; + offhand_usable = offhand_def.has_on_secondary_use; + } + + // main hand has priority + bool use_offhand = offhand_usable && !main_usable; + + if (place) + *place = use_offhand ? *offhand : main; + + return use_offhand; +} + u32 Player::addHud(HudElement *toadd) { MutexAutoLock lock(m_mutex); diff --git a/src/player.h b/src/player.h index a79a13228..791b6c999 100644 --- a/src/player.h +++ b/src/player.h @@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "constants.h" #include "network/networkprotocol.h" #include "util/basic_macros.h" +#include "util/pointedthing.h" #include #include #include @@ -219,6 +220,13 @@ class Player // Returns non-empty `selected` ItemStack. `hand` is a fallback, if specified ItemStack &getWieldedItem(ItemStack *selected, ItemStack *hand) const; + + // item currently in secondary hand is returned in `offhand` + // item to use for place / secondary_use (either main or offhand) is (optionally) returned in `place` + // return value: whether to use main or offhand for placing + bool getOffhandWieldedItem(ItemStack *offhand, ItemStack *place, + IItemDefManager *idef, const PointedThing &pointed) const; + void setWieldIndex(u16 index); u16 getWieldIndex() const { return m_wield_index; } diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index de4f2fe02..8b8a37df9 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -83,6 +83,16 @@ void read_item_definition(lua_State* L, int index, def.usable = lua_isfunction(L, -1); lua_pop(L, 1); + lua_pushstring(L, "on_place"); + lua_rawget(L, index); + def.has_on_place = lua_isfunction(L, -1); + lua_pop(L, 1); + + lua_pushstring(L, "on_secondary_use"); + lua_rawget(L, index); + def.has_on_secondary_use = lua_isfunction(L, -1); + lua_pop(L, 1); + getboolfield(L, index, "liquids_pointable", def.liquids_pointable); lua_getfield(L, index, "pointabilities"); diff --git a/src/server/player_sao.cpp b/src/server/player_sao.cpp index 0806ffd73..fadd3ef3f 100644 --- a/src/server/player_sao.cpp +++ b/src/server/player_sao.cpp @@ -570,6 +570,11 @@ ItemStack PlayerSAO::getWieldedItem(ItemStack *selected, ItemStack *hand) const return m_player->getWieldedItem(selected, hand); } +bool PlayerSAO::getOffhandWieldedItem(ItemStack *offhand, ItemStack *place, IItemDefManager *itemdef_manager, PointedThing pointed) const +{ + return m_player->getOffhandWieldedItem(offhand, place, itemdef_manager, pointed); +} + bool PlayerSAO::setWieldedItem(const ItemStack &item) { InventoryList *mlist = m_player->inventory.getList(getWieldList()); @@ -580,6 +585,16 @@ bool PlayerSAO::setWieldedItem(const ItemStack &item) return false; } +bool PlayerSAO::setOffhandWieldedItem(const ItemStack &item) +{ + InventoryList *olist = m_player->inventory.getList("offhand"); + if (olist) { + olist->changeItem(0, item); + return true; + } + return false; +} + void PlayerSAO::disconnected() { markForRemoval(); diff --git a/src/server/player_sao.h b/src/server/player_sao.h index b26304589..3b54d642a 100644 --- a/src/server/player_sao.h +++ b/src/server/player_sao.h @@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "network/networkprotocol.h" #include "unit_sao.h" #include "util/numeric.h" +#include "util/pointedthing.h" /* PlayerSAO needs some internals exposed. @@ -134,7 +135,9 @@ class PlayerSAO : public UnitSAO std::string getWieldList() const override { return "main"; } u16 getWieldIndex() const override; ItemStack getWieldedItem(ItemStack *selected, ItemStack *hand = nullptr) const override; + bool getOffhandWieldedItem(ItemStack *offhand, ItemStack *place, IItemDefManager *itemdef_manager, PointedThing pointed) const; bool setWieldedItem(const ItemStack &item) override; + bool setOffhandWieldedItem(const ItemStack &item); /* PlayerSAO-specific From 9e45cc0fd94e88613389366f347eedca02107e22 Mon Sep 17 00:00:00 2001 From: Elias Fleckenstein Date: Sun, 24 Jul 2022 00:18:39 +0200 Subject: [PATCH 2/6] Add setting to mirror hands --- builtin/settingtypes.txt | 3 +++ src/client/camera.cpp | 18 ++++++++++++------ src/client/camera.h | 1 + src/defaultsettings.cpp | 1 + 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 8ffb1c3c1..5b502f795 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -320,6 +320,9 @@ view_bobbing_amount (View bobbing factor) float 1.0 0.0 7.9 # For example: 0 for no view bobbing; 1.0 for normal; 2.0 for double. fall_bobbing_amount (Fall bobbing factor) float 0.03 0.0 100.0 +# Draw main hand on left side of screen, offhand on right side. +mirror_hands (Mirror hands) bool false + [**Camera] # Field of view in degrees. diff --git a/src/client/camera.cpp b/src/client/camera.cpp index 674fe3757..8658212a8 100644 --- a/src/client/camera.cpp +++ b/src/client/camera.cpp @@ -63,6 +63,11 @@ WieldNode::WieldNode(HandIndex index, Client *client, scene::ISceneManager *mgr) m_meshnode->drop(); // mgr grabbed it } +int WieldNode::getDirection() +{ + return g_settings->getBool("mirror_hands") ? -m_direction : m_direction; +} + void WieldNode::step(f32 dtime) { bool was_under_zero = m_change_timer < 0; @@ -135,7 +140,7 @@ void WieldNode::addArmInertia(f32 player_yaw, v3f camera_direction) m_cam_vel_old.X = m_cam_vel.X; f32 acc_X = 0.12f * (m_cam_vel.X - (gap_X * 0.1f)); - m_offset.X += (m_last_cam_pos.X < player_yaw ? acc_X : -acc_X) * m_direction; + m_offset.X += (m_last_cam_pos.X < player_yaw ? acc_X : -acc_X) * getDirection(); if (m_last_cam_pos.X != player_yaw) m_last_cam_pos.X = player_yaw; @@ -190,6 +195,7 @@ void WieldNode::addArmInertia(f32 player_yaw, v3f camera_direction) void WieldNode::update(video::SColor player_light_color, f32 view_bobbing_anim, f32 tool_reload_ratio) { + int direction = getDirection(); m_player_light_color = player_light_color; // Position the wielded item @@ -231,15 +237,15 @@ void WieldNode::update(video::SColor player_light_color, f32 view_bobbing_anim, core::quaternion quat_end(v3f(80, 30, 100) * core::DEGTORAD); core::quaternion quat_slerp; quat_slerp.slerp(quat_begin, quat_end, sin(digfrac * M_PI)); - quat_slerp.W *= m_direction; - quat_slerp.X *= m_direction; + quat_slerp.W *= direction; + quat_slerp.X *= direction; quat_slerp.toEuler(rot); rot *= core::RADTODEG; - pos.X *= m_direction; + pos.X *= direction; } else { f32 bobfrac = my_modf(view_bobbing_anim); - pos.X *= m_direction; - pos.X -= sin(bobfrac*M_PI*2.0+M_PI*m_index) * 3.0 * m_direction; + pos.X *= direction; + pos.X -= sin(bobfrac*M_PI*2.0+M_PI*m_index) * 3.0 * direction; pos.Y += sin(my_modf(bobfrac*2.0)*M_PI+M_PI*m_index) * 3.0; } diff --git a/src/client/camera.h b/src/client/camera.h index d33b99039..d67b67992 100644 --- a/src/client/camera.h +++ b/src/client/camera.h @@ -78,6 +78,7 @@ class WieldNode { public: WieldNode(HandIndex index, Client *client, scene::ISceneManager *mgr); + int getDirection(); void step(f32 dtime); void addArmInertia(f32 player_yaw, v3f camera_direction); void update(video::SColor player_light_color, f32 view_bobbing_anim, f32 tool_reload_ratio); diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 0907c4c6d..0e0ba72b2 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -222,6 +222,7 @@ void set_default_settings() settings->setDefault("enable_clouds", "true"); settings->setDefault("view_bobbing_amount", "1.0"); settings->setDefault("fall_bobbing_amount", "0.03"); + settings->setDefault("mirror_hands", "false"); settings->setDefault("enable_3d_clouds", "true"); settings->setDefault("cloud_radius", "12"); settings->setDefault("menu_clouds", "true"); From a71d47edb670f82637f2fedb607ac881f3d92625 Mon Sep 17 00:00:00 2001 From: Elias Fleckenstein Date: Sun, 24 Jul 2022 01:05:31 +0200 Subject: [PATCH 3/6] Add keybind to swap items between hands --- builtin/settingtypes.txt | 4 ++++ src/client/game.cpp | 39 ++++++++++++++++++++++++++++++++++++ src/client/inputhandler.cpp | 1 + src/client/keys.h | 1 + src/defaultsettings.cpp | 1 + src/gui/guiKeyChangeMenu.cpp | 2 ++ 6 files changed, 48 insertions(+) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 5b502f795..664a7d1eb 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -2401,6 +2401,10 @@ keymap_screenshot (Screenshot) key KEY_F12 # Key for dropping the currently selected item. keymap_drop (Drop item key) key KEY_KEY_Q +# Key for swapping items between main hand and offhand. +# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 +keymap_swap_offhand (Swap hand items) key KEY_KEY_F + # Key to use view zoom when possible. keymap_zoom (View zoom key) key KEY_KEY_Z diff --git a/src/client/game.cpp b/src/client/game.cpp index 0084c795c..6e374b7d7 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -727,6 +727,7 @@ class Game { void processItemSelection(u16 *new_playeritem); void dropSelectedItem(bool single_item = false); + void swapOffhand(); void openInventory(); void openConsole(float scale, const wchar_t *line=NULL); void toggleFreeMove(); @@ -2037,6 +2038,8 @@ void Game::processKeyInput() { if (wasKeyDown(KeyType::DROP)) { dropSelectedItem(isKeyDown(KeyType::SNEAK)); + } else if (wasKeyDown(KeyType::SWAP_OFFHAND)) { + swapOffhand(); } else if (wasKeyDown(KeyType::AUTOFORWARD)) { toggleAutoforward(); } else if (wasKeyDown(KeyType::BACKWARD)) { @@ -2216,6 +2219,34 @@ void Game::dropSelectedItem(bool single_item) } +void Game::swapOffhand() +{ + + IMoveAction *a = new IMoveAction(); + a->count = 0; + a->from_inv.setCurrentPlayer(); + a->from_list = "main"; + a->from_i = client->getEnv().getLocalPlayer()->getWieldIndex(); + a->to_inv.setCurrentPlayer(); + a->to_list = "offhand"; + a->to_i = 0; + + ItemStack selected; + client->getEnv().getLocalPlayer()->getWieldedItem(&selected, nullptr); + + if (selected.name == "") { + auto tmp_list = a->from_list; + auto tmp_i = a->from_i; + a->from_list = a->to_list; + a->from_i = a->to_i; + a->to_list = tmp_list; + a->to_i = tmp_i; + } + + client->inventoryAction(a); +} + + void Game::openInventory() { /* @@ -4200,6 +4231,14 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, runData.damage_flash -= 384.0f * dtime; } + /* + ==================== End scene ==================== + */ + + driver->endScene(); + + stats->drawtime = tt_draw.stop(true); + g_profiler->graphAdd("Draw scene [us]", stats->drawtime); g_profiler->avg("Game::updateFrame(): update frame [ms]", tt_update.stop(true)); } diff --git a/src/client/inputhandler.cpp b/src/client/inputhandler.cpp index 5c91898bc..b113e16a9 100644 --- a/src/client/inputhandler.cpp +++ b/src/client/inputhandler.cpp @@ -44,6 +44,7 @@ void KeyCache::populate() key[KeyType::AUTOFORWARD] = getKeySetting("keymap_autoforward"); key[KeyType::DROP] = getKeySetting("keymap_drop"); + key[KeyType::SWAP_OFFHAND] = getKeySetting("keymap_swap_offhand"); key[KeyType::INVENTORY] = getKeySetting("keymap_inventory"); key[KeyType::CHAT] = getKeySetting("keymap_chat"); key[KeyType::CMD] = getKeySetting("keymap_cmd"); diff --git a/src/client/keys.h b/src/client/keys.h index e120a2d92..989eb528c 100644 --- a/src/client/keys.h +++ b/src/client/keys.h @@ -42,6 +42,7 @@ class KeyType // Other DROP, + SWAP_OFFHAND, INVENTORY, CHAT, CMD, diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 0e0ba72b2..cb47c8be7 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -86,6 +86,7 @@ void set_default_settings() settings->setDefault("keymap_dig", "KEY_LBUTTON"); settings->setDefault("keymap_place", "KEY_RBUTTON"); settings->setDefault("keymap_drop", "KEY_KEY_Q"); + settings->setDefault("keymap_swap_offhand", "KEY_KEY_F"); settings->setDefault("keymap_zoom", "KEY_KEY_Z"); settings->setDefault("keymap_inventory", "KEY_KEY_I"); settings->setDefault("keymap_aux1", "KEY_KEY_E"); diff --git a/src/gui/guiKeyChangeMenu.cpp b/src/gui/guiKeyChangeMenu.cpp index 3d8c13e5e..87024b4d8 100644 --- a/src/gui/guiKeyChangeMenu.cpp +++ b/src/gui/guiKeyChangeMenu.cpp @@ -58,6 +58,7 @@ enum GUI_ID_KEY_CONSOLE_BUTTON, GUI_ID_KEY_SNEAK_BUTTON, GUI_ID_KEY_DROP_BUTTON, + GUI_ID_KEY_SWAP_OFFHAND_BUTTON, GUI_ID_KEY_INVENTORY_BUTTON, GUI_ID_KEY_HOTBAR_PREV_BUTTON, GUI_ID_KEY_HOTBAR_NEXT_BUTTON, @@ -391,6 +392,7 @@ void GUIKeyChangeMenu::init_keys() this->add_key(GUI_ID_KEY_JUMP_BUTTON, wstrgettext("Jump"), "keymap_jump"); this->add_key(GUI_ID_KEY_SNEAK_BUTTON, wstrgettext("Sneak"), "keymap_sneak"); this->add_key(GUI_ID_KEY_DROP_BUTTON, wstrgettext("Drop"), "keymap_drop"); + this->add_key(GUI_ID_KEY_SWAP_OFFHAND_BUTTON, wstrgettext("Swap hand items"), "keymap_swap_offhand"); this->add_key(GUI_ID_KEY_INVENTORY_BUTTON, wstrgettext("Inventory"), "keymap_inventory"); this->add_key(GUI_ID_KEY_HOTBAR_PREV_BUTTON, wstrgettext("Prev. item"), "keymap_hotbar_previous"); this->add_key(GUI_ID_KEY_HOTBAR_NEXT_BUTTON, wstrgettext("Next item"), "keymap_hotbar_next"); From 147e481b2bcec759efd01c8fe33c6ee38601bc49 Mon Sep 17 00:00:00 2001 From: Andrey2470T Date: Fri, 1 Mar 2024 22:56:26 +0300 Subject: [PATCH 4/6] Some fixes of the merge conflicts --- src/client/camera.cpp | 35 ----------------------------- src/client/game.cpp | 8 ------- src/network/serverpackethandler.cpp | 8 +++---- src/server/player_sao.h | 2 ++ 4 files changed, 6 insertions(+), 47 deletions(-) diff --git a/src/client/camera.cpp b/src/client/camera.cpp index 8658212a8..0a8b4b0a7 100644 --- a/src/client/camera.cpp +++ b/src/client/camera.cpp @@ -373,13 +373,6 @@ void Camera::notifyFovChange() } } -// Returns the fractional part of x -inline f32 my_modf(f32 x) -{ - float dummy; - return std::modf(x, &dummy); -} - void Camera::step(f32 dtime) { for (auto node : m_wieldnodes) @@ -392,14 +385,6 @@ void Camera::step(f32 dtime) m_view_bobbing_fall = -1; // Mark the effect as finished } - bool was_under_zero = m_wield_change_timer < 0; - m_wield_change_timer = MYMIN(m_wield_change_timer + dtime, 0.125); - - if (m_wield_change_timer >= 0 && was_under_zero) { - m_wieldnode->setItem(m_wield_item_next, m_client); - m_wieldnode->setNodeLightColor(m_player_light_color); - } - if (m_view_bobbing_state != 0) { //f32 offset = dtime * m_view_bobbing_speed * 0.035; @@ -437,26 +422,6 @@ void Camera::step(f32 dtime) } } } - - if (m_digging_button != -1) { - f32 offset = dtime * 3.5f; - float m_digging_anim_was = m_digging_anim; - m_digging_anim += offset; - if (m_digging_anim >= 1) - { - m_digging_anim = 0; - m_digging_button = -1; - } - float lim = 0.15; - if(m_digging_anim_was < lim && m_digging_anim >= lim) - { - if (m_digging_button == 0) { - m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::CAMERA_PUNCH_LEFT)); - } else if(m_digging_button == 1) { - m_client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::CAMERA_PUNCH_RIGHT)); - } - } - } } void Camera::addArmInertia(f32 player_yaw) diff --git a/src/client/game.cpp b/src/client/game.cpp index 6e374b7d7..0a2120a4c 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -4231,14 +4231,6 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, runData.damage_flash -= 384.0f * dtime; } - /* - ==================== End scene ==================== - */ - - driver->endScene(); - - stats->drawtime = tt_draw.stop(true); - g_profiler->graphAdd("Draw scene [us]", stats->drawtime); g_profiler->avg("Game::updateFrame(): update frame [ms]", tt_update.stop(true)); } diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index 8a8498892..1489d84c1 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -919,8 +919,8 @@ static inline void getWieldedItem(const PlayerSAO *playersao, std::optionalgetWieldedItem(&(*ret)); } -static inline bool getOffhandWieldedItem(const PlayerSAO *playersao, Optional &offhand, - Optional &place, IItemDefManager *idef, const PointedThing &pointed) +static inline bool getOffhandWieldedItem(const PlayerSAO *playersao, std::optional &offhand, + std::optional &place, IItemDefManager *idef, const PointedThing &pointed) { offhand = ItemStack(); place = ItemStack(); @@ -1263,7 +1263,7 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) if (use_offhand ? (offhand_item.has_value() && playersao->setOffhandWieldedItem(*offhand_item)) : (main_item.has_value() && playersao->setWieldedItem(*main_item))) - SendInventory(playersao, true); + SendInventory(player, true); } pointed_object->rightClick(playersao); @@ -1274,7 +1274,7 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) if (use_offhand ? (offhand_item.has_value() && playersao->setOffhandWieldedItem(*offhand_item)) : (main_item.has_value() && playersao->setWieldedItem(*main_item))) - SendInventory(playersao, true); + SendInventory(player, true); } if (pointed.type != POINTEDTHING_NODE) diff --git a/src/server/player_sao.h b/src/server/player_sao.h index 3b54d642a..cde1ab77e 100644 --- a/src/server/player_sao.h +++ b/src/server/player_sao.h @@ -27,6 +27,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/numeric.h" #include "util/pointedthing.h" +class IItemDefManager; + /* PlayerSAO needs some internals exposed. */ From 8167c04b83aa3cdc19da11003769eb8776b3b817 Mon Sep 17 00:00:00 2001 From: Andrey2470T Date: Sat, 9 Mar 2024 16:50:28 +0300 Subject: [PATCH 5/6] Force set/get_wielded_item() to access to itemstack of current used hand --- src/client/camera.h | 3 +- src/client/game.cpp | 36 ++++++++++++------- src/network/serverpackethandler.cpp | 55 ++++++++++++++++++++--------- src/player.cpp | 51 +++++++++++++++++++++++--- src/player.h | 9 +++-- src/server/player_sao.cpp | 26 +++++++++++--- src/server/player_sao.h | 4 ++- 7 files changed, 140 insertions(+), 44 deletions(-) diff --git a/src/client/camera.h b/src/client/camera.h index d67b67992..ee646aabb 100644 --- a/src/client/camera.h +++ b/src/client/camera.h @@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include +#include "player.h" class LocalPlayer; struct MapDrawControl; @@ -72,8 +73,6 @@ struct Nametag } }; -enum HandIndex { MAINHAND = 0, OFFHAND = 1 }; - class WieldNode { public: diff --git a/src/client/game.cpp b/src/client/game.cpp index 0a2120a4c..1bfe0a296 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -776,10 +776,10 @@ class Game { 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 handlePointingAtNothing(HandIndex used_hand); void handlePointingAtNode(const PointedThing &pointed, const ItemStack &selected_item, const ItemStack &hand_item, - const ItemStack &place_item, f32 dtime); + HandIndex used_hand, f32 dtime); void handlePointingAtObject(const PointedThing &pointed, const ItemStack &playeritem, const v3f &player_position, bool show_debug); void handleDigging(const PointedThing &pointed, const v3s16 &nodepos, @@ -3342,8 +3342,6 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud) !runData.btn_down_for_dig, camera_offset); - player->getOffhandWieldedItem(&offhand_item, &place_item, itemdef_manager, pointed); - if (pointed != runData.pointed_old) infostream << "Pointing at " << pointed.dump() << std::endl; @@ -3409,12 +3407,14 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud) else runData.repeat_place_timer = 0; + HandIndex cur_used_hand = player->getCurrentUsedHand(itemdef_manager, 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, place_item, dtime); + handlePointingAtNode(pointed, selected_item, hand_item, cur_used_hand, dtime); } else if (pointed.type == POINTEDTHING_OBJECT) { v3f player_position = player->getPosition(); bool basic_debug_allowed = client->checkPrivilege("debug") || (player->hud_flags & HUD_FLAG_BASIC_DEBUG); @@ -3427,7 +3427,7 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud) if (wasKeyPressed(KeyType::DIG) && client->modsLoaded()) client->getScript()->on_item_use(selected_item, pointed); } else if (wasKeyPressed(KeyType::PLACE)) { - handlePointingAtNothing(place_item); + handlePointingAtNothing(cur_used_hand); } runData.pointed_old = pointed; @@ -3548,7 +3548,7 @@ PointedThing Game::updatePointedThing( } -void Game::handlePointingAtNothing(const ItemStack &playerItem) +void Game::handlePointingAtNothing(HandIndex used_hand) { infostream << "Attempted to place item while pointing at nothing" << std::endl; PointedThing fauxPointed; @@ -3559,7 +3559,7 @@ void Game::handlePointingAtNothing(const ItemStack &playerItem) void Game::handlePointingAtNode(const PointedThing &pointed, const ItemStack &selected_item, const ItemStack &hand_item, - const ItemStack &place_item, f32 dtime) + HandIndex used_hand, f32 dtime) { v3s16 nodepos = pointed.node_undersurface; v3s16 neighborpos = pointed.node_abovesurface; @@ -3593,14 +3593,14 @@ void Game::handlePointingAtNode(const PointedThing &pointed, if ((wasKeyPressed(KeyType::PLACE) || runData.repeat_place_timer >= m_repeat_place_time) && client->checkPrivilege("interact")) { + LocalPlayer *player = client->getEnv().getLocalPlayer(); + player->current_used_hand = used_hand; + runData.repeat_place_timer = 0; infostream << "Place button pressed while looking at ground" << std::endl; // Placing animation (always shown for feedback) - if (place_item == selected_item) - camera->setDigging(1, MAINHAND); - else - camera->setDigging(1, OFFHAND); + camera->setDigging(1, used_hand); soundmaker->m_player_rightpunch_sound = SoundSpec(); @@ -3608,12 +3608,22 @@ void Game::handlePointingAtNode(const PointedThing &pointed, // make that happen // And also set the sound and send the interact // But first check for meta formspec and rightclickable + ItemStack place_item; + + if (used_hand == MAINHAND) + player->getWieldedItem(&place_item, nullptr); + else + player->getOffhandWieldedItem(&place_item); + auto &def = place_item.getDefinition(itemdef_manager); bool placed = nodePlacement(def, place_item, nodepos, neighborpos, pointed, meta); if (placed && client->modsLoaded()) client->getScript()->on_placenode(pointed, def); + + // Resets the hand index after 'on_place' callback run. + player->current_used_hand = MAINHAND; } } @@ -4154,7 +4164,7 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, ItemStack selected_item, hand_item, offhand_item; ItemStack &tool_item = player->getWieldedItem(&selected_item, &hand_item); camera->wield(tool_item, MAINHAND); - player->getOffhandWieldedItem(&offhand_item, nullptr, itemdef_manager, PointedThing()); + player->getOffhandWieldedItem(&offhand_item); camera->wield(offhand_item, OFFHAND); } diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index 1489d84c1..db66cc339 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -919,12 +919,10 @@ static inline void getWieldedItem(const PlayerSAO *playersao, std::optionalgetWieldedItem(&(*ret)); } -static inline bool getOffhandWieldedItem(const PlayerSAO *playersao, std::optional &offhand, - std::optional &place, IItemDefManager *idef, const PointedThing &pointed) +static inline void getOffhandWieldedItem(const PlayerSAO *playersao, std::optional &offhand) { offhand = ItemStack(); - place = ItemStack(); - return playersao->getOffhandWieldedItem(&(*offhand), &(*place), idef, pointed); + playersao->getOffhandWieldedItem(&(*offhand)); } void Server::handleCommand_Interact(NetworkPacket *pkt) @@ -1237,7 +1235,14 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) case INTERACT_PLACE: { std::optional main_item, offhand_item, place_item; getWieldedItem(playersao, main_item); - bool use_offhand = getOffhandWieldedItem(playersao, offhand_item, place_item, m_itemdef, pointed); + getOffhandWieldedItem(playersao, offhand_item); + + HandIndex used_hand = playersao->getCurrentUsedHand(m_itemdef, pointed); + + if (used_hand == MAINHAND) + place_item = main_item; + else + place_item = offhand_item; // Reset build time counter if (pointed.type == POINTEDTHING_NODE && @@ -1259,22 +1264,30 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) << pointed_object->getDescription() << std::endl; // Do stuff - if (m_script->item_OnSecondaryUse(use_offhand ? offhand_item : main_item, playersao, pointed)) { - if (use_offhand + playersao->getPlayer()->current_used_hand = used_hand; + if (m_script->item_OnSecondaryUse(used_hand == MAINHAND ? main_item : offhand_item, playersao, pointed)) { + if (used_hand == OFFHAND ? (offhand_item.has_value() && playersao->setOffhandWieldedItem(*offhand_item)) : (main_item.has_value() && playersao->setWieldedItem(*main_item))) SendInventory(player, true); } - pointed_object->rightClick(playersao); - } else if (m_script->item_OnPlace(use_offhand ? offhand_item : main_item, playersao, pointed)) { - // Placement was handled in lua + playersao->getPlayer()->current_used_hand = MAINHAND; - // Apply returned ItemStack - if (use_offhand - ? (offhand_item.has_value() && playersao->setOffhandWieldedItem(*offhand_item)) - : (main_item.has_value() && playersao->setWieldedItem(*main_item))) + pointed_object->rightClick(playersao); + } + else { + playersao->getPlayer()->current_used_hand = used_hand; + if (m_script->item_OnPlace(used_hand == MAINHAND ? main_item : offhand_item, playersao, pointed)) { + // Placement was handled in lua + + // Apply returned ItemStack + if (used_hand == OFFHAND + ? (offhand_item.has_value() && playersao->setOffhandWieldedItem(*offhand_item)) + : (main_item.has_value() && playersao->setWieldedItem(*main_item))) SendInventory(player, true); + } + playersao->getPlayer()->current_used_hand = MAINHAND; } if (pointed.type != POINTEDTHING_NODE) @@ -1318,20 +1331,28 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) case INTERACT_ACTIVATE: { std::optional main_item, offhand_item, place_item; getWieldedItem(playersao, main_item); - bool use_offhand = getOffhandWieldedItem(playersao, offhand_item, place_item, m_itemdef, pointed); + getOffhandWieldedItem(playersao, offhand_item); + HandIndex used_hand = playersao->getCurrentUsedHand(m_itemdef, pointed); + + if (used_hand == MAINHAND) + place_item = main_item; + else + place_item = offhand_item; actionstream << player->getName() << " activates " << place_item->name << std::endl; pointed.type = POINTEDTHING_NOTHING; // can only ever be NOTHING - if (m_script->item_OnSecondaryUse(use_offhand ? offhand_item : main_item, playersao, pointed)) { + playersao->getPlayer()->current_used_hand = used_hand; + if (m_script->item_OnSecondaryUse(used_hand == MAINHAND ? main_item : offhand_item, playersao, pointed)) { // Apply returned ItemStack - if (use_offhand + if (used_hand == OFFHAND ? (offhand_item.has_value() && playersao->setOffhandWieldedItem(*offhand_item)) : (main_item.has_value() && playersao->setWieldedItem(*main_item))) SendInventory(player, true); } + playersao->getPlayer()->current_used_hand = MAINHAND; return; } diff --git a/src/player.cpp b/src/player.cpp index 2079bfea3..5b7edea90 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -41,6 +41,7 @@ Player::Player(const char *name, IItemDefManager *idef): craft->setWidth(3); inventory.addList("craftpreview", 1); inventory.addList("craftresult", 1); + inventory.addList("offhand", 1); inventory.setModified(false); // Can be redefined via Lua @@ -95,8 +96,12 @@ ItemStack &Player::getWieldedItem(ItemStack *selected, ItemStack *hand) const const InventoryList *mlist = inventory.getList("main"); // TODO: Make this generic const InventoryList *hlist = inventory.getList("hand"); - if (mlist && m_wield_index < mlist->getSize()) - *selected = mlist->getItem(m_wield_index); + if (current_used_hand == MAINHAND) { + if (mlist && m_wield_index < mlist->getSize()) + *selected = mlist->getItem(m_wield_index); + } + else + getOffhandWieldedItem(selected); if (hand && hlist) *hand = hlist->getItem(0); @@ -105,7 +110,45 @@ ItemStack &Player::getWieldedItem(ItemStack *selected, ItemStack *hand) const return (hand && selected->name.empty()) ? *hand : *selected; } -bool Player::getOffhandWieldedItem(ItemStack *offhand, ItemStack *place, IItemDefManager *idef, const PointedThing &pointed) const +void Player::getOffhandWieldedItem(ItemStack *offhand) const +{ + assert(offhand); + + const InventoryList *olist = inventory.getList("offhand"); + + if (olist) + *offhand = olist->getItem(0); +} + +HandIndex Player::getCurrentUsedHand(IItemDefManager *idef, const PointedThing &pointed) const +{ + ItemStack main, offhand; + + getWieldedItem(&main, nullptr); + getOffhandWieldedItem(&offhand); + + const ItemDefinition &main_def = main.getDefinition(idef); + const ItemDefinition &offhand_def = offhand.getDefinition(idef); + + bool main_usable, offhand_usable; + + // figure out which item to use for placements + + if (pointed.type == POINTEDTHING_NODE) { + // an item can be used on nodes if it has a place handler or prediction + main_usable = main_def.has_on_place || main_def.node_placement_prediction != ""; + offhand_usable = offhand_def.has_on_place || offhand_def.node_placement_prediction != ""; + } else { + // an item can be used on anything else if it has a secondary use handler + main_usable = main_def.has_on_secondary_use; + offhand_usable = offhand_def.has_on_secondary_use; + } + + // main hand has priority + return (HandIndex)(offhand_usable && !main_usable); +} + +/*bool Player::getOffhandWieldedItem(ItemStack *offhand, ItemStack *place, IItemDefManager *idef, const PointedThing &pointed) const { assert(offhand); @@ -143,7 +186,7 @@ bool Player::getOffhandWieldedItem(ItemStack *offhand, ItemStack *place, IItemDe *place = use_offhand ? *offhand : main; return use_offhand; -} +}*/ u32 Player::addHud(HudElement *toadd) { diff --git a/src/player.h b/src/player.h index 791b6c999..c73a95925 100644 --- a/src/player.h +++ b/src/player.h @@ -35,6 +35,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #define PLAYERNAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_" #define PLAYERNAME_ALLOWED_CHARS_USER_EXPL "'a' to 'z', 'A' to 'Z', '0' to '9', '-', '_'" +enum HandIndex { MAINHAND = 0, OFFHAND = 1 }; + struct PlayerFovSpec { f32 fov; @@ -224,8 +226,9 @@ class Player // item currently in secondary hand is returned in `offhand` // item to use for place / secondary_use (either main or offhand) is (optionally) returned in `place` // return value: whether to use main or offhand for placing - bool getOffhandWieldedItem(ItemStack *offhand, ItemStack *place, - IItemDefManager *idef, const PointedThing &pointed) const; + void getOffhandWieldedItem(ItemStack *offhand) const; + + HandIndex getCurrentUsedHand(IItemDefManager *idef, const PointedThing &pointed) const; void setWieldIndex(u16 index); u16 getWieldIndex() const { return m_wield_index; } @@ -252,6 +255,8 @@ class Player u32 hud_flags; s32 hud_hotbar_itemcount; + HandIndex current_used_hand { MAINHAND }; + protected: char m_name[PLAYERNAME_SIZE]; v3f m_speed; // velocity; in BS-space diff --git a/src/server/player_sao.cpp b/src/server/player_sao.cpp index fadd3ef3f..21f2a2c54 100644 --- a/src/server/player_sao.cpp +++ b/src/server/player_sao.cpp @@ -570,16 +570,32 @@ ItemStack PlayerSAO::getWieldedItem(ItemStack *selected, ItemStack *hand) const return m_player->getWieldedItem(selected, hand); } -bool PlayerSAO::getOffhandWieldedItem(ItemStack *offhand, ItemStack *place, IItemDefManager *itemdef_manager, PointedThing pointed) const +void PlayerSAO::getOffhandWieldedItem(ItemStack *offhand) const { - return m_player->getOffhandWieldedItem(offhand, place, itemdef_manager, pointed); + m_player->getOffhandWieldedItem(offhand); +} + +HandIndex PlayerSAO::getCurrentUsedHand(IItemDefManager *idef, const PointedThing &pointed) const +{ + return m_player->getCurrentUsedHand(idef, pointed); } bool PlayerSAO::setWieldedItem(const ItemStack &item) { - InventoryList *mlist = m_player->inventory.getList(getWieldList()); - if (mlist) { - mlist->changeItem(m_player->getWieldIndex(), item); + InventoryList *list; + u16 list_index; + + if (m_player->current_used_hand == MAINHAND) { + list = m_player->inventory.getList(getWieldList()); + list_index = m_player->getWieldIndex(); + } + else { + list = m_player->inventory.getList("offhand"); + list_index = 0; + } + + if (list) { + list->changeItem(list_index, item); return true; } return false; diff --git a/src/server/player_sao.h b/src/server/player_sao.h index cde1ab77e..16b6d9044 100644 --- a/src/server/player_sao.h +++ b/src/server/player_sao.h @@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "unit_sao.h" #include "util/numeric.h" #include "util/pointedthing.h" +#include "player.h" class IItemDefManager; @@ -137,7 +138,8 @@ class PlayerSAO : public UnitSAO std::string getWieldList() const override { return "main"; } u16 getWieldIndex() const override; ItemStack getWieldedItem(ItemStack *selected, ItemStack *hand = nullptr) const override; - bool getOffhandWieldedItem(ItemStack *offhand, ItemStack *place, IItemDefManager *itemdef_manager, PointedThing pointed) const; + void getOffhandWieldedItem(ItemStack *offhand) const; + HandIndex getCurrentUsedHand(IItemDefManager *idef, const PointedThing &pointed) const; bool setWieldedItem(const ItemStack &item) override; bool setOffhandWieldedItem(const ItemStack &item); From f3fd2f6eaa8d078b6fe667eea5cfbe4e830599ce Mon Sep 17 00:00:00 2001 From: Andrey2470T Date: Sat, 9 Mar 2024 20:24:52 +0300 Subject: [PATCH 6/6] Added 'offhand' parameter to the callbacks, some code cleanups --- builtin/settingtypes.txt | 3 +-- doc/lua_api.md | 8 +++++--- src/client/camera.cpp | 20 ++++++++++---------- src/client/game.cpp | 9 ++------- src/defaultsettings.cpp | 2 +- src/network/serverpackethandler.cpp | 9 ++++++--- src/script/cpp_api/s_item.cpp | 12 ++++++++---- src/script/cpp_api/s_item.h | 4 ++-- src/script/lua_api/l_env.cpp | 2 +- 9 files changed, 36 insertions(+), 33 deletions(-) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 664a7d1eb..caad14889 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -321,7 +321,7 @@ view_bobbing_amount (View bobbing factor) float 1.0 0.0 7.9 fall_bobbing_amount (Fall bobbing factor) float 0.03 0.0 100.0 # Draw main hand on left side of screen, offhand on right side. -mirror_hands (Mirror hands) bool false +swap_hands (Swap hands) bool false [**Camera] @@ -2402,7 +2402,6 @@ keymap_screenshot (Screenshot) key KEY_F12 keymap_drop (Drop item key) key KEY_KEY_Q # Key for swapping items between main hand and offhand. -# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 keymap_swap_offhand (Swap hand items) key KEY_KEY_F # Key to use view zoom when possible. diff --git a/doc/lua_api.md b/doc/lua_api.md index 1c16fbe1f..c9e03cf09 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -9182,17 +9182,19 @@ Used by `minetest.register_node`, `minetest.register_craftitem`, and -- When item is used with the 'punch/mine' key pointing at nothing (air) }, - on_place = function(itemstack, placer, pointed_thing), - -- When the 'place' key was pressed with the item one of the hands + on_place = function(itemstack, placer, pointed_thing, offhand), + -- When the 'place' key was pressed with the item in one of the hands -- and a node was pointed at. -- 'itemstack' may be the offhand item in cases where the main hand has -- no on_place handler and no node_placement_prediction. + -- 'offhand' is a boolean indicating whether the callback was called + -- from the item of the main hand (false) or the offhand (true). -- Shall place item and return the leftover itemstack -- or nil to not modify the inventory. -- The placer may be any ObjectRef or nil. -- default: minetest.item_place - on_secondary_use = function(itemstack, user, pointed_thing), + on_secondary_use = function(itemstack, user, pointed_thing, offhand), -- Same as on_place but called when not pointing at a node, -- whereas `user` is the same as `placer` above. -- default: nil diff --git a/src/client/camera.cpp b/src/client/camera.cpp index 0a8b4b0a7..25d002126 100644 --- a/src/client/camera.cpp +++ b/src/client/camera.cpp @@ -46,10 +46,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #define WIELDMESH_AMPLITUDE_Y 10.0f // Returns the fractional part of x -inline f32 my_modf(f32 x) +inline f32 frac_part(f32 x) { - f32 dummy; - return modff(x, &dummy); + f32 integral_part; + return modff(x, &integral_part); } WieldNode::WieldNode(HandIndex index, Client *client, scene::ISceneManager *mgr) : @@ -65,7 +65,7 @@ WieldNode::WieldNode(HandIndex index, Client *client, scene::ISceneManager *mgr) int WieldNode::getDirection() { - return g_settings->getBool("mirror_hands") ? -m_direction : m_direction; + return g_settings->getBool("swap_hands") ? -m_direction : m_direction; } void WieldNode::step(f32 dtime) @@ -100,7 +100,7 @@ void WieldNode::step(f32 dtime) } } -static inline v2f dir(const v2f &pos_dist) +static inline v2f get_arm_dir(const v2f &pos_dist) { f32 x = pos_dist.X - WIELDMESH_OFFSET_X; f32 y = pos_dist.Y - WIELDMESH_OFFSET_Y; @@ -166,7 +166,7 @@ void WieldNode::addArmInertia(f32 player_yaw, v3f camera_direction) WIELDMESH_OFFSET_Y + (WIELDMESH_AMPLITUDE_Y * 0.5f)); } - m_arm_dir = dir(m_offset); + m_arm_dir = get_arm_dir(m_offset); } else { /* Now the arm gets back to its default position when the camera stops, @@ -243,10 +243,10 @@ void WieldNode::update(video::SColor player_light_color, f32 view_bobbing_anim, rot *= core::RADTODEG; pos.X *= direction; } else { - f32 bobfrac = my_modf(view_bobbing_anim); + f32 bobfrac = frac_part(view_bobbing_anim); pos.X *= direction; pos.X -= sin(bobfrac*M_PI*2.0+M_PI*m_index) * 3.0 * direction; - pos.Y += sin(my_modf(bobfrac*2.0)*M_PI+M_PI*m_index) * 3.0; + pos.Y += sin(frac_part(bobfrac*2.0)*M_PI+M_PI*m_index) * 3.0; } m_meshnode->setPosition(pos); @@ -413,7 +413,7 @@ void Camera::step(f32 dtime) } else { float was = m_view_bobbing_anim; - m_view_bobbing_anim = my_modf(m_view_bobbing_anim + offset); + m_view_bobbing_anim = frac_part(m_view_bobbing_anim + offset); bool step = (was == 0 || (was < 0.5f && m_view_bobbing_anim >= 0.5f) || (was > 0.5f && m_view_bobbing_anim <= 0.5f)); @@ -522,7 +522,7 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 tool_reload_ratio) if (m_cache_view_bobbing_amount != 0.0f && m_view_bobbing_anim != 0.0f && m_camera_mode < CAMERA_MODE_THIRD) { - f32 bobfrac = my_modf(m_view_bobbing_anim * 2); + f32 bobfrac = frac_part(m_view_bobbing_anim * 2); f32 bobdir = (m_view_bobbing_anim < 0.5) ? 1.0 : -1.0; f32 bobknob = 1.2; diff --git a/src/client/game.cpp b/src/client/game.cpp index 1bfe0a296..166e79a47 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -2221,7 +2221,6 @@ void Game::dropSelectedItem(bool single_item) void Game::swapOffhand() { - IMoveAction *a = new IMoveAction(); a->count = 0; a->from_inv.setCurrentPlayer(); @@ -2235,12 +2234,8 @@ void Game::swapOffhand() client->getEnv().getLocalPlayer()->getWieldedItem(&selected, nullptr); if (selected.name == "") { - auto tmp_list = a->from_list; - auto tmp_i = a->from_i; - a->from_list = a->to_list; - a->from_i = a->to_i; - a->to_list = tmp_list; - a->to_i = tmp_i; + std::swap(a->from_list, a->to_list); + std::swap(a->from_i, a->to_i); } client->inventoryAction(a); diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index cb47c8be7..51f764b7b 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -223,7 +223,7 @@ void set_default_settings() settings->setDefault("enable_clouds", "true"); settings->setDefault("view_bobbing_amount", "1.0"); settings->setDefault("fall_bobbing_amount", "0.03"); - settings->setDefault("mirror_hands", "false"); + settings->setDefault("swap_hands", "false"); settings->setDefault("enable_3d_clouds", "true"); settings->setDefault("cloud_radius", "12"); settings->setDefault("menu_clouds", "true"); diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index db66cc339..c9ce40a5a 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -1265,7 +1265,8 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) // Do stuff playersao->getPlayer()->current_used_hand = used_hand; - if (m_script->item_OnSecondaryUse(used_hand == MAINHAND ? main_item : offhand_item, playersao, pointed)) { + if (m_script->item_OnSecondaryUse(used_hand == MAINHAND ? main_item : offhand_item, + playersao, pointed, (bool)used_hand)) { if (used_hand == OFFHAND ? (offhand_item.has_value() && playersao->setOffhandWieldedItem(*offhand_item)) : (main_item.has_value() && playersao->setWieldedItem(*main_item))) @@ -1278,7 +1279,8 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) } else { playersao->getPlayer()->current_used_hand = used_hand; - if (m_script->item_OnPlace(used_hand == MAINHAND ? main_item : offhand_item, playersao, pointed)) { + if (m_script->item_OnPlace(used_hand == MAINHAND ? main_item : offhand_item, + playersao, pointed, (bool)used_hand)) { // Placement was handled in lua // Apply returned ItemStack @@ -1345,7 +1347,8 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) pointed.type = POINTEDTHING_NOTHING; // can only ever be NOTHING playersao->getPlayer()->current_used_hand = used_hand; - if (m_script->item_OnSecondaryUse(used_hand == MAINHAND ? main_item : offhand_item, playersao, pointed)) { + if (m_script->item_OnSecondaryUse(used_hand == MAINHAND ? main_item : offhand_item, + playersao, pointed, (bool)used_hand)) { // Apply returned ItemStack if (used_hand == OFFHAND ? (offhand_item.has_value() && playersao->setOffhandWieldedItem(*offhand_item)) diff --git a/src/script/cpp_api/s_item.cpp b/src/script/cpp_api/s_item.cpp index feba36ffb..04065dee5 100644 --- a/src/script/cpp_api/s_item.cpp +++ b/src/script/cpp_api/s_item.cpp @@ -61,7 +61,7 @@ bool ScriptApiItem::item_OnDrop(ItemStack &item, } bool ScriptApiItem::item_OnPlace(std::optional &ret_item, - ServerActiveObject *placer, const PointedThing &pointed) + ServerActiveObject *placer, const PointedThing &pointed, bool offhand) { SCRIPTAPI_PRECHECKHEADER @@ -81,7 +81,9 @@ bool ScriptApiItem::item_OnPlace(std::optional &ret_item, objectrefGetOrCreate(L, placer); pushPointedThing(pointed); - PCALL_RES(lua_pcall(L, 3, 1, error_handler)); + lua_pushboolean(L, (int)offhand); + + PCALL_RES(lua_pcall(L, 4, 1, error_handler)); if (!lua_isnil(L, -1)) { try { ret_item = read_item(L, -1, getServer()->idef()); @@ -126,7 +128,7 @@ bool ScriptApiItem::item_OnUse(std::optional &ret_item, } bool ScriptApiItem::item_OnSecondaryUse(std::optional &ret_item, - ServerActiveObject *user, const PointedThing &pointed) + ServerActiveObject *user, const PointedThing &pointed, bool offhand) { SCRIPTAPI_PRECHECKHEADER @@ -139,7 +141,9 @@ bool ScriptApiItem::item_OnSecondaryUse(std::optional &ret_item, LuaItemStack::create(L, item); objectrefGetOrCreate(L, user); pushPointedThing(pointed); - PCALL_RES(lua_pcall(L, 3, 1, error_handler)); + lua_pushboolean(L, (int)offhand); + + PCALL_RES(lua_pcall(L, 4, 1, error_handler)); if (!lua_isnil(L, -1)) { try { ret_item = read_item(L, -1, getServer()->idef()); diff --git a/src/script/cpp_api/s_item.h b/src/script/cpp_api/s_item.h index e5088c47e..83cf39ab3 100644 --- a/src/script/cpp_api/s_item.h +++ b/src/script/cpp_api/s_item.h @@ -46,11 +46,11 @@ class ScriptApiItem bool item_OnDrop(ItemStack &item, ServerActiveObject *dropper, v3f pos); bool item_OnPlace(std::optional &item, - ServerActiveObject *placer, const PointedThing &pointed); + ServerActiveObject *placer, const PointedThing &pointed, bool offhand); bool item_OnUse(std::optional &item, ServerActiveObject *user, const PointedThing &pointed); bool item_OnSecondaryUse(std::optional &item, - ServerActiveObject *user, const PointedThing &pointed); + ServerActiveObject *user, const PointedThing &pointed, bool offhand); bool item_OnCraft(ItemStack &item, ServerActiveObject *user, const InventoryList *old_craft_grid, const InventoryLocation &craft_inv); bool item_CraftPredict(ItemStack &item, ServerActiveObject *user, diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index 06dc27b2d..1c11c6722 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -455,7 +455,7 @@ int ModApiEnv::l_place_node(lua_State *L) // Place it with a nullptr placer (appears in Lua as nil) // or the given ObjectRef - bool success = scriptIfaceItem->item_OnPlace(item, placer, pointed); + bool success = scriptIfaceItem->item_OnPlace(item, placer, pointed, false); lua_pushboolean(L, success); return 1; }