mirror of
https://github.com/minetest/minetest.git
synced 2024-07-04 15:05:27 +02:00
Dual wielding
This commit is contained in:
parent
e12db0c182
commit
4acba9b648
@ -3660,6 +3660,16 @@ Player Inventory lists
|
|||||||
* `hand`: list containing an override for the empty hand
|
* `hand`: list containing an override for the empty hand
|
||||||
* Is not created automatically, use `InvRef:set_size`
|
* Is not created automatically, use `InvRef:set_size`
|
||||||
* Is only used to enhance the empty hand's tool capabilities
|
* 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
|
ItemStack transaction order
|
||||||
---------------------------
|
---------------------------
|
||||||
@ -9173,18 +9183,18 @@ Used by `minetest.register_node`, `minetest.register_craftitem`, and
|
|||||||
},
|
},
|
||||||
|
|
||||||
on_place = function(itemstack, placer, pointed_thing),
|
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.
|
-- 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
|
-- Shall place item and return the leftover itemstack
|
||||||
-- or nil to not modify the inventory.
|
-- or nil to not modify the inventory.
|
||||||
-- The placer may be any ObjectRef or nil.
|
-- The placer may be any ObjectRef or nil.
|
||||||
-- default: minetest.item_place
|
-- default: minetest.item_place
|
||||||
|
|
||||||
on_secondary_use = function(itemstack, user, pointed_thing),
|
on_secondary_use = function(itemstack, user, pointed_thing),
|
||||||
-- Same as on_place but called when not pointing at a node.
|
-- Same as on_place but called when not pointing at a node,
|
||||||
-- Function must return either nil if inventory shall not be modified,
|
-- whereas `user` is the same as `placer` above.
|
||||||
-- or an itemstack to replace the original itemstack.
|
|
||||||
-- The user may be any ObjectRef or nil.
|
|
||||||
-- default: nil
|
-- default: nil
|
||||||
|
|
||||||
on_drop = function(itemstack, dropper, pos),
|
on_drop = function(itemstack, dropper, pos),
|
||||||
|
@ -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_X 7.0f
|
||||||
#define WIELDMESH_AMPLITUDE_Y 10.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):
|
Camera::Camera(MapDrawControl &draw_control, Client *client, RenderingEngine *rendering_engine):
|
||||||
m_draw_control(draw_control),
|
m_draw_control(draw_control),
|
||||||
m_client(client),
|
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.
|
// all other 3D scene nodes and before the GUI.
|
||||||
m_wieldmgr = smgr->createNewSceneManager();
|
m_wieldmgr = smgr->createNewSceneManager();
|
||||||
m_wieldmgr->addCameraSceneNode();
|
m_wieldmgr->addCameraSceneNode();
|
||||||
m_wieldnode = new WieldMeshSceneNode(m_wieldmgr, -1, false);
|
|
||||||
m_wieldnode->setItem(ItemStack(), m_client);
|
m_wieldnodes[MAINHAND] = new WieldNode(MAINHAND, m_client, m_wieldmgr);
|
||||||
m_wieldnode->drop(); // m_wieldmgr grabbed it
|
m_wieldnodes[ OFFHAND] = new WieldNode( OFFHAND, m_client, m_wieldmgr);
|
||||||
|
|
||||||
/* TODO: Add a callback function so these can be updated when a setting
|
/* 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
|
* 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()
|
Camera::~Camera()
|
||||||
{
|
{
|
||||||
|
for (auto node : m_wieldnodes)
|
||||||
|
delete node;
|
||||||
m_wieldmgr->drop();
|
m_wieldmgr->drop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,6 +376,9 @@ inline f32 my_modf(f32 x)
|
|||||||
|
|
||||||
void Camera::step(f32 dtime)
|
void Camera::step(f32 dtime)
|
||||||
{
|
{
|
||||||
|
for (auto node : m_wieldnodes)
|
||||||
|
node->step(dtime);
|
||||||
|
|
||||||
if(m_view_bobbing_fall > 0)
|
if(m_view_bobbing_fall > 0)
|
||||||
{
|
{
|
||||||
m_view_bobbing_fall -= 3 * dtime;
|
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)
|
void Camera::addArmInertia(f32 player_yaw)
|
||||||
{
|
{
|
||||||
m_cam_vel.X = std::fabs(rangelim(m_last_cam_pos.X - player_yaw,
|
for (auto node : m_wieldnodes)
|
||||||
-100.0f, 100.0f) / 0.016f) * 0.01f;
|
node->addArmInertia(player_yaw, m_camera_direction);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Camera::update(LocalPlayer* player, f32 frametime, f32 tool_reload_ratio)
|
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)
|
if (m_arm_inertia)
|
||||||
addArmInertia(yaw);
|
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_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
|
// Set render distance
|
||||||
updateViewingRange();
|
updateViewingRange();
|
||||||
@ -611,22 +723,14 @@ void Camera::updateViewingRange()
|
|||||||
m_cameranode->setFarValue((viewing_range < 2000) ? 2000 * BS : viewing_range * BS);
|
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_wieldnodes[hand]->setDigging(button);
|
||||||
m_digging_button = button;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Camera::wield(const ItemStack &item)
|
void Camera::wield(const ItemStack &item, HandIndex hand)
|
||||||
{
|
{
|
||||||
if (item.name != m_wield_item_next.name ||
|
m_wieldnodes[hand]->wield(item);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Camera::drawWieldedTool(irr::core::matrix4* translation)
|
void Camera::drawWieldedTool(irr::core::matrix4* translation)
|
||||||
|
@ -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};
|
enum CameraMode {CAMERA_MODE_FIRST, CAMERA_MODE_THIRD, CAMERA_MODE_THIRD_FRONT};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -166,11 +209,11 @@ class Camera
|
|||||||
void updateViewingRange();
|
void updateViewingRange();
|
||||||
|
|
||||||
// Start digging animation
|
// Start digging animation
|
||||||
// Pass 0 for left click, 1 for right click
|
// button: Pass 0 for left click, 1 for right click
|
||||||
void setDigging(s32 button);
|
void setDigging(s32 button, HandIndex hand);
|
||||||
|
|
||||||
// Replace the wielded item mesh
|
// Replace the wielded item mesh
|
||||||
void wield(const ItemStack &item);
|
void wield(const ItemStack &item, HandIndex hand);
|
||||||
|
|
||||||
// Draw the wielded tool.
|
// Draw the wielded tool.
|
||||||
// This has to happen *after* the main scene is drawn.
|
// This has to happen *after* the main scene is drawn.
|
||||||
@ -219,8 +262,9 @@ class Camera
|
|||||||
scene::ISceneNode *m_headnode = nullptr;
|
scene::ISceneNode *m_headnode = nullptr;
|
||||||
scene::ICameraSceneNode *m_cameranode = nullptr;
|
scene::ICameraSceneNode *m_cameranode = nullptr;
|
||||||
|
|
||||||
|
WieldNode *m_wieldnodes[2];
|
||||||
|
|
||||||
scene::ISceneManager *m_wieldmgr = nullptr;
|
scene::ISceneManager *m_wieldmgr = nullptr;
|
||||||
WieldMeshSceneNode *m_wieldnode = nullptr;
|
|
||||||
|
|
||||||
// draw control
|
// draw control
|
||||||
MapDrawControl& m_draw_control;
|
MapDrawControl& m_draw_control;
|
||||||
@ -247,12 +291,6 @@ class Camera
|
|||||||
bool m_fov_transition_active = false;
|
bool m_fov_transition_active = false;
|
||||||
f32 m_fov_diff, m_transition_time;
|
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
|
// Field of view and aspect ratio stuff
|
||||||
f32 m_aspect = 1.0f;
|
f32 m_aspect = 1.0f;
|
||||||
f32 m_fov_x = 1.0f;
|
f32 m_fov_x = 1.0f;
|
||||||
@ -269,17 +307,6 @@ class Camera
|
|||||||
// Fall view bobbing
|
// Fall view bobbing
|
||||||
f32 m_view_bobbing_fall = 0.0f;
|
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;
|
CameraMode m_camera_mode = CAMERA_MODE_FIRST;
|
||||||
|
|
||||||
f32 m_cache_fall_bobbing_amount;
|
f32 m_cache_fall_bobbing_amount;
|
||||||
|
@ -1570,6 +1570,8 @@ bool Client::updateWieldedItem()
|
|||||||
list->setModified(false);
|
list->setModified(false);
|
||||||
if (auto *list = player->inventory.getList("hand"))
|
if (auto *list = player->inventory.getList("hand"))
|
||||||
list->setModified(false);
|
list->setModified(false);
|
||||||
|
if (auto *list = player->inventory.getList("offhand"))
|
||||||
|
list->setModified(false);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -777,7 +777,8 @@ class Game {
|
|||||||
bool look_for_object, const v3s16 &camera_offset);
|
bool look_for_object, const v3s16 &camera_offset);
|
||||||
void handlePointingAtNothing(const ItemStack &playerItem);
|
void handlePointingAtNothing(const ItemStack &playerItem);
|
||||||
void handlePointingAtNode(const PointedThing &pointed,
|
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,
|
void handlePointingAtObject(const PointedThing &pointed, const ItemStack &playeritem,
|
||||||
const v3f &player_position, bool show_debug);
|
const v3f &player_position, bool show_debug);
|
||||||
void handleDigging(const PointedThing &pointed, const v3s16 &nodepos,
|
void handleDigging(const PointedThing &pointed, const v3s16 &nodepos,
|
||||||
@ -1834,19 +1835,19 @@ inline bool Game::handleCallbacks()
|
|||||||
|
|
||||||
if (g_gamecallback->changepassword_requested) {
|
if (g_gamecallback->changepassword_requested) {
|
||||||
(new GUIPasswordChange(guienv, guiroot, -1,
|
(new GUIPasswordChange(guienv, guiroot, -1,
|
||||||
&g_menumgr, client, texture_src))->drop();
|
&g_menumgr, client, texture_src))->drop();
|
||||||
g_gamecallback->changepassword_requested = false;
|
g_gamecallback->changepassword_requested = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g_gamecallback->changevolume_requested) {
|
if (g_gamecallback->changevolume_requested) {
|
||||||
(new GUIVolumeChange(guienv, guiroot, -1,
|
(new GUIVolumeChange(guienv, guiroot, -1,
|
||||||
&g_menumgr, texture_src))->drop();
|
&g_menumgr, texture_src))->drop();
|
||||||
g_gamecallback->changevolume_requested = false;
|
g_gamecallback->changevolume_requested = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g_gamecallback->keyconfig_requested) {
|
if (g_gamecallback->keyconfig_requested) {
|
||||||
(new GUIKeyChangeMenu(guienv, guiroot, -1,
|
(new GUIKeyChangeMenu(guienv, guiroot, -1,
|
||||||
&g_menumgr, texture_src))->drop();
|
&g_menumgr, texture_src))->drop();
|
||||||
g_gamecallback->keyconfig_requested = false;
|
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
|
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 ItemStack &tool_item = player->getWieldedItem(&selected_item, &hand_item);
|
||||||
|
|
||||||
const ItemDefinition &selected_def = selected_item.getDefinition(itemdef_manager);
|
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,
|
!runData.btn_down_for_dig,
|
||||||
camera_offset);
|
camera_offset);
|
||||||
|
|
||||||
|
player->getOffhandWieldedItem(&offhand_item, &place_item, itemdef_manager, pointed);
|
||||||
|
|
||||||
if (pointed != runData.pointed_old)
|
if (pointed != runData.pointed_old)
|
||||||
infostream << "Pointing at " << pointed.dump() << std::endl;
|
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->getScript()->on_item_use(selected_item, pointed)))
|
||||||
client->interact(INTERACT_USE, pointed);
|
client->interact(INTERACT_USE, pointed);
|
||||||
} else if (pointed.type == POINTEDTHING_NODE) {
|
} 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) {
|
} else if (pointed.type == POINTEDTHING_OBJECT) {
|
||||||
v3f player_position = player->getPosition();
|
v3f player_position = player->getPosition();
|
||||||
bool basic_debug_allowed = client->checkPrivilege("debug") || (player->hud_flags & HUD_FLAG_BASIC_DEBUG);
|
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())
|
if (wasKeyPressed(KeyType::DIG) && client->modsLoaded())
|
||||||
client->getScript()->on_item_use(selected_item, pointed);
|
client->getScript()->on_item_use(selected_item, pointed);
|
||||||
} else if (wasKeyPressed(KeyType::PLACE)) {
|
} else if (wasKeyPressed(KeyType::PLACE)) {
|
||||||
handlePointingAtNothing(selected_item);
|
handlePointingAtNothing(place_item);
|
||||||
}
|
}
|
||||||
|
|
||||||
runData.pointed_old = pointed;
|
runData.pointed_old = pointed;
|
||||||
|
|
||||||
if (runData.punching || wasKeyPressed(KeyType::DIG))
|
if (runData.punching || wasKeyPressed(KeyType::DIG))
|
||||||
camera->setDigging(0); // dig animation
|
camera->setDigging(0, MAINHAND); // dig animation
|
||||||
|
|
||||||
input->clearWasKeyPressed();
|
input->clearWasKeyPressed();
|
||||||
input->clearWasKeyReleased();
|
input->clearWasKeyReleased();
|
||||||
@ -3524,7 +3527,8 @@ void Game::handlePointingAtNothing(const ItemStack &playerItem)
|
|||||||
|
|
||||||
|
|
||||||
void Game::handlePointingAtNode(const PointedThing &pointed,
|
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 nodepos = pointed.node_undersurface;
|
||||||
v3s16 neighborpos = pointed.node_abovesurface;
|
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;
|
infostream << "Place button pressed while looking at ground" << std::endl;
|
||||||
|
|
||||||
// Placing animation (always shown for feedback)
|
// 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();
|
soundmaker->m_player_rightpunch_sound = SoundSpec();
|
||||||
|
|
||||||
@ -3570,8 +3577,8 @@ void Game::handlePointingAtNode(const PointedThing &pointed,
|
|||||||
// make that happen
|
// make that happen
|
||||||
// And also set the sound and send the interact
|
// And also set the sound and send the interact
|
||||||
// But first check for meta formspec and rightclickable
|
// But first check for meta formspec and rightclickable
|
||||||
auto &def = selected_item.getDefinition(itemdef_manager);
|
auto &def = place_item.getDefinition(itemdef_manager);
|
||||||
bool placed = nodePlacement(def, selected_item, nodepos, neighborpos,
|
bool placed = nodePlacement(def, place_item, nodepos, neighborpos,
|
||||||
pointed, meta);
|
pointed, meta);
|
||||||
|
|
||||||
if (placed && client->modsLoaded())
|
if (placed && client->modsLoaded())
|
||||||
@ -3995,7 +4002,7 @@ void Game::handleDigging(const PointedThing &pointed, const v3s16 &nodepos,
|
|||||||
client->setCrack(-1, 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,
|
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()
|
direct_brightness = client->getEnv().getClientMap()
|
||||||
.getBackgroundBrightness(MYMIN(runData.fog_range * 1.2, 60 * BS),
|
.getBackgroundBrightness(MYMIN(runData.fog_range * 1.2, 60 * BS),
|
||||||
daynight_ratio, (int)(old_brightness * 255.5), &sunlight_seen)
|
daynight_ratio, (int)(old_brightness * 255.5), &sunlight_seen)
|
||||||
/ 255.0;
|
/ 255.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
float time_of_day_smooth = runData.time_of_day_smooth;
|
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()) {
|
if (client->updateWieldedItem()) {
|
||||||
// Update wielded tool
|
// Update wielded tool
|
||||||
ItemStack selected_item, hand_item;
|
ItemStack selected_item, hand_item, offhand_item;
|
||||||
ItemStack &tool_item = player->getWieldedItem(&selected_item, &hand_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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -142,6 +142,8 @@ ItemDefinition& ItemDefinition::operator=(const ItemDefinition &def)
|
|||||||
node_placement_prediction = def.node_placement_prediction;
|
node_placement_prediction = def.node_placement_prediction;
|
||||||
place_param2 = def.place_param2;
|
place_param2 = def.place_param2;
|
||||||
wallmounted_rotate_vertical = def.wallmounted_rotate_vertical;
|
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 = def.sound_place;
|
||||||
sound_place_failed = def.sound_place_failed;
|
sound_place_failed = def.sound_place_failed;
|
||||||
sound_use = def.sound_use;
|
sound_use = def.sound_use;
|
||||||
@ -195,6 +197,8 @@ void ItemDefinition::reset()
|
|||||||
node_placement_prediction.clear();
|
node_placement_prediction.clear();
|
||||||
place_param2.reset();
|
place_param2.reset();
|
||||||
wallmounted_rotate_vertical = false;
|
wallmounted_rotate_vertical = false;
|
||||||
|
has_on_place = false;
|
||||||
|
has_on_secondary_use = false;
|
||||||
touch_interaction = TouchInteraction();
|
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.serializeSimple(os, protocol_version);
|
||||||
sound_use_air.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
|
os << (u8)place_param2.has_value(); // protocol_version >= 43
|
||||||
if (place_param2)
|
if (place_param2)
|
||||||
os << *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.deSerializeSimple(is, protocol_version);
|
||||||
sound_use_air.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())
|
if (is.eof())
|
||||||
throw SerializationError("");
|
throw SerializationError("");
|
||||||
|
|
||||||
|
@ -121,6 +121,10 @@ struct ItemDefinition
|
|||||||
// "" = no prediction
|
// "" = no prediction
|
||||||
std::string node_placement_prediction;
|
std::string node_placement_prediction;
|
||||||
std::optional<u8> place_param2;
|
std::optional<u8> place_param2;
|
||||||
|
|
||||||
|
bool has_on_place;
|
||||||
|
bool has_on_secondary_use;
|
||||||
|
|
||||||
bool wallmounted_rotate_vertical;
|
bool wallmounted_rotate_vertical;
|
||||||
|
|
||||||
TouchInteraction touch_interaction;
|
TouchInteraction touch_interaction;
|
||||||
|
@ -893,9 +893,9 @@ void Server::handleCommand_Respawn(NetworkPacket* pkt)
|
|||||||
|
|
||||||
bool Server::checkInteractDistance(RemotePlayer *player, const f32 d, const std::string &what)
|
bool Server::checkInteractDistance(RemotePlayer *player, const f32 d, const std::string &what)
|
||||||
{
|
{
|
||||||
ItemStack selected_item, hand_item;
|
ItemStack selected_item, main_item;
|
||||||
player->getWieldedItem(&selected_item, &hand_item);
|
player->getWieldedItem(&selected_item, &main_item);
|
||||||
f32 max_d = BS * getToolRange(selected_item, hand_item, m_itemdef);
|
f32 max_d = BS * getToolRange(selected_item, main_item, m_itemdef);
|
||||||
|
|
||||||
// Cube diagonal * 1.5 for maximal supported node extents:
|
// Cube diagonal * 1.5 for maximal supported node extents:
|
||||||
// sqrt(3) * 1.5 ≅ 2.6
|
// sqrt(3) * 1.5 ≅ 2.6
|
||||||
@ -919,6 +919,14 @@ static inline void getWieldedItem(const PlayerSAO *playersao, std::optional<Item
|
|||||||
playersao->getWieldedItem(&(*ret));
|
playersao->getWieldedItem(&(*ret));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool getOffhandWieldedItem(const PlayerSAO *playersao, Optional<ItemStack> &offhand,
|
||||||
|
Optional<ItemStack> &place, IItemDefManager *idef, const PointedThing &pointed)
|
||||||
|
{
|
||||||
|
offhand = ItemStack();
|
||||||
|
place = ItemStack();
|
||||||
|
return playersao->getOffhandWieldedItem(&(*offhand), &(*place), idef, pointed);
|
||||||
|
}
|
||||||
|
|
||||||
void Server::handleCommand_Interact(NetworkPacket *pkt)
|
void Server::handleCommand_Interact(NetworkPacket *pkt)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@ -1159,8 +1167,8 @@ void Server::handleCommand_Interact(NetworkPacket *pkt)
|
|||||||
|
|
||||||
// Get player's wielded item
|
// Get player's wielded item
|
||||||
// See also: Game::handleDigging
|
// See also: Game::handleDigging
|
||||||
ItemStack selected_item, hand_item;
|
ItemStack selected_item, main_item;
|
||||||
player->getWieldedItem(&selected_item, &hand_item);
|
player->getWieldedItem(&selected_item, &main_item);
|
||||||
|
|
||||||
// Get diggability and expected digging time
|
// Get diggability and expected digging time
|
||||||
DigParams params = getDigParams(m_nodedef->get(n).groups,
|
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 can't dig, try hand
|
||||||
if (!params.diggable) {
|
if (!params.diggable) {
|
||||||
params = getDigParams(m_nodedef->get(n).groups,
|
params = getDigParams(m_nodedef->get(n).groups,
|
||||||
&hand_item.getToolCapabilities(m_itemdef));
|
&main_item.getToolCapabilities(m_itemdef));
|
||||||
}
|
}
|
||||||
// If can't dig, ignore dig
|
// If can't dig, ignore dig
|
||||||
if (!params.diggable) {
|
if (!params.diggable) {
|
||||||
@ -1227,15 +1235,16 @@ void Server::handleCommand_Interact(NetworkPacket *pkt)
|
|||||||
|
|
||||||
// Place block or right-click object
|
// Place block or right-click object
|
||||||
case INTERACT_PLACE: {
|
case INTERACT_PLACE: {
|
||||||
std::optional<ItemStack> selected_item;
|
std::optional<ItemStack> main_item, offhand_item, place_item;
|
||||||
getWieldedItem(playersao, selected_item);
|
getWieldedItem(playersao, main_item);
|
||||||
|
bool use_offhand = getOffhandWieldedItem(playersao, offhand_item, place_item, m_itemdef, pointed);
|
||||||
|
|
||||||
// Reset build time counter
|
// Reset build time counter
|
||||||
if (pointed.type == POINTEDTHING_NODE &&
|
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;
|
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();
|
node_placement_prediction.empty();
|
||||||
|
|
||||||
if (pointed.type == POINTEDTHING_OBJECT) {
|
if (pointed.type == POINTEDTHING_OBJECT) {
|
||||||
@ -1250,18 +1259,22 @@ void Server::handleCommand_Interact(NetworkPacket *pkt)
|
|||||||
<< pointed_object->getDescription() << std::endl;
|
<< pointed_object->getDescription() << std::endl;
|
||||||
|
|
||||||
// Do stuff
|
// Do stuff
|
||||||
if (m_script->item_OnSecondaryUse(selected_item, playersao, pointed)) {
|
if (m_script->item_OnSecondaryUse(use_offhand ? offhand_item : main_item, playersao, pointed)) {
|
||||||
if (selected_item.has_value() && playersao->setWieldedItem(*selected_item))
|
if (use_offhand
|
||||||
SendInventory(player, true);
|
? (offhand_item.has_value() && playersao->setOffhandWieldedItem(*offhand_item))
|
||||||
|
: (main_item.has_value() && playersao->setWieldedItem(*main_item)))
|
||||||
|
SendInventory(playersao, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
pointed_object->rightClick(playersao);
|
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
|
// Placement was handled in lua
|
||||||
|
|
||||||
// Apply returned ItemStack
|
// Apply returned ItemStack
|
||||||
if (selected_item.has_value() && playersao->setWieldedItem(*selected_item))
|
if (use_offhand
|
||||||
SendInventory(player, true);
|
? (offhand_item.has_value() && playersao->setOffhandWieldedItem(*offhand_item))
|
||||||
|
: (main_item.has_value() && playersao->setWieldedItem(*main_item)))
|
||||||
|
SendInventory(playersao, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pointed.type != POINTEDTHING_NODE)
|
if (pointed.type != POINTEDTHING_NODE)
|
||||||
@ -1303,17 +1316,20 @@ void Server::handleCommand_Interact(NetworkPacket *pkt)
|
|||||||
|
|
||||||
// Rightclick air
|
// Rightclick air
|
||||||
case INTERACT_ACTIVATE: {
|
case INTERACT_ACTIVATE: {
|
||||||
std::optional<ItemStack> selected_item;
|
std::optional<ItemStack> main_item, offhand_item, place_item;
|
||||||
getWieldedItem(playersao, selected_item);
|
getWieldedItem(playersao, main_item);
|
||||||
|
bool use_offhand = getOffhandWieldedItem(playersao, offhand_item, place_item, m_itemdef, pointed);
|
||||||
|
|
||||||
actionstream << player->getName() << " activates "
|
actionstream << player->getName() << " activates "
|
||||||
<< selected_item->name << std::endl;
|
<< place_item->name << std::endl;
|
||||||
|
|
||||||
pointed.type = POINTEDTHING_NOTHING; // can only ever be NOTHING
|
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
|
// 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);
|
SendInventory(player, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,6 +105,46 @@ ItemStack &Player::getWieldedItem(ItemStack *selected, ItemStack *hand) const
|
|||||||
return (hand && selected->name.empty()) ? *hand : *selected;
|
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)
|
u32 Player::addHud(HudElement *toadd)
|
||||||
{
|
{
|
||||||
MutexAutoLock lock(m_mutex);
|
MutexAutoLock lock(m_mutex);
|
||||||
|
@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include "constants.h"
|
#include "constants.h"
|
||||||
#include "network/networkprotocol.h"
|
#include "network/networkprotocol.h"
|
||||||
#include "util/basic_macros.h"
|
#include "util/basic_macros.h"
|
||||||
|
#include "util/pointedthing.h"
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@ -219,6 +220,13 @@ class Player
|
|||||||
|
|
||||||
// Returns non-empty `selected` ItemStack. `hand` is a fallback, if specified
|
// Returns non-empty `selected` ItemStack. `hand` is a fallback, if specified
|
||||||
ItemStack &getWieldedItem(ItemStack *selected, ItemStack *hand) const;
|
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);
|
void setWieldIndex(u16 index);
|
||||||
u16 getWieldIndex() const { return m_wield_index; }
|
u16 getWieldIndex() const { return m_wield_index; }
|
||||||
|
|
||||||
|
@ -83,6 +83,16 @@ void read_item_definition(lua_State* L, int index,
|
|||||||
def.usable = lua_isfunction(L, -1);
|
def.usable = lua_isfunction(L, -1);
|
||||||
lua_pop(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);
|
getboolfield(L, index, "liquids_pointable", def.liquids_pointable);
|
||||||
|
|
||||||
lua_getfield(L, index, "pointabilities");
|
lua_getfield(L, index, "pointabilities");
|
||||||
|
@ -570,6 +570,11 @@ ItemStack PlayerSAO::getWieldedItem(ItemStack *selected, ItemStack *hand) const
|
|||||||
return m_player->getWieldedItem(selected, hand);
|
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)
|
bool PlayerSAO::setWieldedItem(const ItemStack &item)
|
||||||
{
|
{
|
||||||
InventoryList *mlist = m_player->inventory.getList(getWieldList());
|
InventoryList *mlist = m_player->inventory.getList(getWieldList());
|
||||||
@ -580,6 +585,16 @@ bool PlayerSAO::setWieldedItem(const ItemStack &item)
|
|||||||
return false;
|
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()
|
void PlayerSAO::disconnected()
|
||||||
{
|
{
|
||||||
markForRemoval();
|
markForRemoval();
|
||||||
|
@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include "network/networkprotocol.h"
|
#include "network/networkprotocol.h"
|
||||||
#include "unit_sao.h"
|
#include "unit_sao.h"
|
||||||
#include "util/numeric.h"
|
#include "util/numeric.h"
|
||||||
|
#include "util/pointedthing.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
PlayerSAO needs some internals exposed.
|
PlayerSAO needs some internals exposed.
|
||||||
@ -134,7 +135,9 @@ class PlayerSAO : public UnitSAO
|
|||||||
std::string getWieldList() const override { return "main"; }
|
std::string getWieldList() const override { return "main"; }
|
||||||
u16 getWieldIndex() const override;
|
u16 getWieldIndex() const override;
|
||||||
ItemStack getWieldedItem(ItemStack *selected, ItemStack *hand = nullptr) 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 setWieldedItem(const ItemStack &item) override;
|
||||||
|
bool setOffhandWieldedItem(const ItemStack &item);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
PlayerSAO-specific
|
PlayerSAO-specific
|
||||||
|
Loading…
Reference in New Issue
Block a user