This commit is contained in:
Andrey 2024-05-17 17:12:46 -04:00 committed by GitHub
commit 8c780a0587
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 635 additions and 260 deletions

View File

@ -318,6 +318,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.
swap_hands (Swap hands) bool false
[**Camera]
# Field of view in degrees.
@ -2402,6 +2405,9 @@ 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.
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

View File

@ -3664,6 +3664,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
---------------------------
@ -9196,19 +9206,21 @@ 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 in hand
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),
-- 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.
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
on_drop = function(itemstack, dropper, pos),

View File

@ -45,6 +45,242 @@ 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 frac_part(f32 x)
{
f32 integral_part;
return modff(x, &integral_part);
}
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
}
int WieldNode::getDirection()
{
return g_settings->getBool("swap_hands") ? -m_direction : m_direction;
}
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 get_arm_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) * getDirection();
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 = get_arm_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)
{
int direction = getDirection();
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 *= direction;
quat_slerp.X *= direction;
quat_slerp.toEuler(rot);
rot *= core::RADTODEG;
pos.X *= direction;
} else {
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(frac_part(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 +298,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 +323,8 @@ Camera::Camera(MapDrawControl &draw_control, Client *client, RenderingEngine *re
Camera::~Camera()
{
for (auto node : m_wieldnodes)
delete node;
m_wieldmgr->drop();
}
@ -118,15 +356,11 @@ 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)
node->step(dtime);
if(m_view_bobbing_fall > 0)
{
m_view_bobbing_fall -= 3 * dtime;
@ -134,14 +368,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;
@ -170,7 +396,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));
@ -179,119 +405,12 @@ 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));
}
}
}
}
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)
@ -386,7 +505,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;
@ -513,46 +632,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();
@ -594,22 +677,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)

View File

@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <array>
#include <list>
#include <optional>
#include "player.h"
class LocalPlayer;
struct MapDrawControl;
@ -72,6 +73,48 @@ struct Nametag
}
};
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);
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 @@ public:
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 @@ private:
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 @@ private:
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 @@ private:
// 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;

View File

@ -1568,6 +1568,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;
}

View File

@ -727,6 +727,7 @@ protected:
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();
@ -775,9 +776,10 @@ protected:
const core::line3d<f32> &shootline, bool liquids_pointable,
const std::optional<Pointabilities> &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, f32 dtime);
const ItemStack &selected_item, const ItemStack &hand_item,
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,
@ -1852,19 +1854,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;
}
@ -2057,6 +2059,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)) {
@ -2236,6 +2240,29 @@ 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 == "") {
std::swap(a->from_list, a->to_list);
std::swap(a->from_i, a->to_i);
}
client->inventoryAction(a);
}
void Game::openInventory()
{
/*
@ -3299,7 +3326,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);
@ -3404,12 +3431,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, 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);
@ -3422,13 +3451,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(cur_used_hand);
}
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();
@ -3543,7 +3572,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;
@ -3553,7 +3582,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,
HandIndex used_hand, f32 dtime)
{
v3s16 nodepos = pointed.node_undersurface;
v3s16 neighborpos = pointed.node_abovesurface;
@ -3587,11 +3617,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)
camera->setDigging(1);
camera->setDigging(1, used_hand);
soundmaker->m_player_rightpunch_sound = SoundSpec();
@ -3599,12 +3632,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
auto &def = selected_item.getDefinition(itemdef_manager);
bool placed = nodePlacement(def, selected_item, nodepos, neighborpos,
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;
}
}
@ -4024,7 +4067,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,
@ -4071,7 +4114,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;
@ -4142,9 +4185,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);
camera->wield(offhand_item, OFFHAND);
}
/*

View File

@ -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");

View File

@ -42,6 +42,7 @@ public:
// Other
DROP,
SWAP_OFFHAND,
INVENTORY,
CHAT,
CMD,

View File

@ -147,6 +147,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");
@ -279,6 +280,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("swap_hands", "false");
settings->setDefault("enable_3d_clouds", "true");
settings->setDefault("cloud_radius", "12");
settings->setDefault("menu_clouds", "true");

View File

@ -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");

View File

@ -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("");

View File

@ -121,6 +121,10 @@ struct ItemDefinition
// "" = no prediction
std::string node_placement_prediction;
std::optional<u8> place_param2;
bool has_on_place;
bool has_on_secondary_use;
bool wallmounted_rotate_vertical;
TouchInteraction touch_interaction;

View File

@ -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,12 @@ static inline void getWieldedItem(const PlayerSAO *playersao, std::optional<Item
playersao->getWieldedItem(&(*ret));
}
static inline void getOffhandWieldedItem(const PlayerSAO *playersao, std::optional<ItemStack> &offhand)
{
offhand = ItemStack();
playersao->getOffhandWieldedItem(&(*offhand));
}
void Server::handleCommand_Interact(NetworkPacket *pkt)
{
/*
@ -1159,8 +1165,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 +1175,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 +1233,23 @@ void Server::handleCommand_Interact(NetworkPacket *pkt)
// Place block or right-click object
case INTERACT_PLACE: {
std::optional<ItemStack> selected_item;
getWieldedItem(playersao, selected_item);
std::optional<ItemStack> main_item, offhand_item, place_item;
getWieldedItem(playersao, main_item);
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 &&
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 +1264,32 @@ 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))
playersao->getPlayer()->current_used_hand = used_hand;
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)))
SendInventory(player, true);
}
pointed_object->rightClick(playersao);
} else if (m_script->item_OnPlace(selected_item, playersao, pointed)) {
// Placement was handled in lua
playersao->getPlayer()->current_used_hand = MAINHAND;
// Apply returned ItemStack
if (selected_item.has_value() && playersao->setWieldedItem(*selected_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, (bool)used_hand)) {
// 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)
@ -1303,19 +1331,31 @@ void Server::handleCommand_Interact(NetworkPacket *pkt)
// Rightclick air
case INTERACT_ACTIVATE: {
std::optional<ItemStack> selected_item;
getWieldedItem(playersao, selected_item);
std::optional<ItemStack> main_item, offhand_item, place_item;
getWieldedItem(playersao, main_item);
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 "
<< 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)) {
playersao->getPlayer()->current_used_hand = used_hand;
if (m_script->item_OnSecondaryUse(used_hand == MAINHAND ? main_item : offhand_item,
playersao, pointed, (bool)used_hand)) {
// Apply returned ItemStack
if (selected_item.has_value() && playersao->setWieldedItem(*selected_item))
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;
}

View File

@ -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,6 +110,84 @@ ItemStack &Player::getWieldedItem(ItemStack *selected, ItemStack *hand) const
return (hand && selected->name.empty()) ? *hand : *selected;
}
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);
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);

View File

@ -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 <list>
#include <mutex>
#include <functional>
@ -34,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;
@ -222,6 +225,14 @@ public:
// 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
void getOffhandWieldedItem(ItemStack *offhand) const;
HandIndex getCurrentUsedHand(IItemDefManager *idef, const PointedThing &pointed) const;
void setWieldIndex(u16 index);
u16 getWieldIndex() const { return m_wield_index; }
@ -247,6 +258,8 @@ public:
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

View File

@ -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");

View File

@ -61,7 +61,7 @@ bool ScriptApiItem::item_OnDrop(ItemStack &item,
}
bool ScriptApiItem::item_OnPlace(std::optional<ItemStack> &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<ItemStack> &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<ItemStack> &ret_item,
}
bool ScriptApiItem::item_OnSecondaryUse(std::optional<ItemStack> &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<ItemStack> &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());

View File

@ -46,11 +46,11 @@ public:
bool item_OnDrop(ItemStack &item,
ServerActiveObject *dropper, v3f pos);
bool item_OnPlace(std::optional<ItemStack> &item,
ServerActiveObject *placer, const PointedThing &pointed);
ServerActiveObject *placer, const PointedThing &pointed, bool offhand);
bool item_OnUse(std::optional<ItemStack> &item,
ServerActiveObject *user, const PointedThing &pointed);
bool item_OnSecondaryUse(std::optional<ItemStack> &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,

View File

@ -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;
}

View File

@ -576,11 +576,42 @@ ItemStack PlayerSAO::getWieldedItem(ItemStack *selected, ItemStack *hand) const
return m_player->getWieldedItem(selected, hand);
}
void PlayerSAO::getOffhandWieldedItem(ItemStack *offhand) const
{
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;
}
bool PlayerSAO::setOffhandWieldedItem(const ItemStack &item)
{
InventoryList *olist = m_player->inventory.getList("offhand");
if (olist) {
olist->changeItem(0, item);
return true;
}
return false;

View File

@ -25,6 +25,10 @@ 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"
#include "player.h"
class IItemDefManager;
/*
PlayerSAO needs some internals exposed.
@ -134,7 +138,10 @@ public:
std::string getWieldList() const override { return "main"; }
u16 getWieldIndex() const override;
ItemStack getWieldedItem(ItemStack *selected, ItemStack *hand = nullptr) const override;
void getOffhandWieldedItem(ItemStack *offhand) const;
HandIndex getCurrentUsedHand(IItemDefManager *idef, const PointedThing &pointed) const;
bool setWieldedItem(const ItemStack &item) override;
bool setOffhandWieldedItem(const ItemStack &item);
/*
PlayerSAO-specific