return b_max;
}
+
bool LocalPlayer::updateSneakNode(Map *map, const v3f &position,
const v3f &sneak_max)
{
+ // Acceptable distance to node center
+ constexpr f32 allowed_range = (0.5f + 0.1f) * BS;
static const v3s16 dir9_center[9] = {
v3s16( 0, 0, 0),
v3s16( 1, 0, 0),
bool new_sneak_node_exists = m_sneak_node_exists;
// We want the top of the sneak node to be below the players feet
- f32 position_y_mod = 0.05f * BS;
+ f32 position_y_mod = 0.02f * BS;
if (m_sneak_node_exists)
position_y_mod = m_sneak_node_bb_top.MaxEdge.Y - position_y_mod;
// Get new sneak node
m_sneak_ladder_detected = false;
- f32 min_distance_f = 100000.0f * BS;
+ f32 min_distance_sq = HUGE_VALF;
for (const auto &d : dir9_center) {
const v3s16 p = current_node + d;
- const v3f pf = intToFloat(p, BS);
- const v2f diff(position.X - pf.X, position.Z - pf.Z);
- f32 distance_f = diff.getLength();
- if (distance_f > min_distance_f ||
- fabs(diff.X) > (0.5f + 0.1f) * BS + sneak_max.X ||
- fabs(diff.Y) > (0.5f + 0.1f) * BS + sneak_max.Z)
+ node = map->getNode(p, &is_valid_position);
+ // The node to be sneaked on has to be walkable
+ if (!is_valid_position || !nodemgr->get(node).walkable)
continue;
+ v3f pf = intToFloat(p, BS);
+ {
+ std::vector<aabb3f> nodeboxes;
+ node.getCollisionBoxes(nodemgr, &nodeboxes);
+ pf += getNodeBoundingBox(nodeboxes).getCenter();
+ }
- // The node to be sneaked on has to be walkable
- node = map->getNode(p, &is_valid_position);
- if (!is_valid_position || !nodemgr->get(node).walkable)
+ const v2f diff(position.X - pf.X, position.Z - pf.Z);
+ const f32 distance_sq = diff.getLengthSQ();
+
+ if (distance_sq > min_distance_sq ||
+ std::fabs(diff.X) > allowed_range + sneak_max.X ||
+ std::fabs(diff.Y) > allowed_range + sneak_max.Z)
continue;
+
// And the node(s) above have to be nonwalkable
bool ok = true;
if (!physics_override.sneak_glitch) {
if (!ok)
continue;
- min_distance_f = distance_f;
+ min_distance_sq = distance_sq;
m_sneak_node = p;
new_sneak_node_exists = true;
}
// Add new collisions to the vector
if (collision_info && !free_move) {
v3f diff = intToFloat(m_standing_node, BS) - position;
- f32 distance = diff.getLength();
+ f32 distance_sq = diff.getLengthSQ();
// Force update each ClientEnvironment::step()
bool is_first = collision_info->empty();
diff = intToFloat(colinfo.node_p, BS) - position;
// Find nearest colliding node
- f32 len = diff.getLength();
- if (is_first || len < distance) {
+ f32 len_sq = diff.getLengthSQ();
+ if (is_first || len_sq < distance_sq) {
m_standing_node = colinfo.node_p;
- distance = len;
+ distance_sq = len_sq;
is_first = false;
}
}
}
/*
- If the player's feet touch the topside of any node, this is
- set to true.
+ If the player's feet touch the topside of any node
+ at the END of clientstep, then this is set to true.
Player is allowed to jump when this is true.
*/
// Max. distance (X, Z) over border for sneaking determined by collision box
// * 0.49 to keep the center just barely on the node
- v3f sneak_max = m_collisionbox.getExtent() * 0.49;
+ v3f sneak_max = m_collisionbox.getExtent() * 0.49f;
if (m_sneak_ladder_detected) {
// restore legacy behaviour (this makes the m_speed.Y hack necessary)
const ContentFeatures &f = nodemgr->get(map->getNode(m_standing_node));
const ContentFeatures &f1 = nodemgr->get(map->getNode(m_standing_node + v3s16(0, 1, 0)));
+ // We can jump from a bouncy node we collided with this clientstep,
+ // even if we are not "touching" it at the end of clientstep.
+ int standing_node_bouncy = 0;
+ if (result.collides && m_speed.Y > 0.0f) {
+ // must use result.collisions here because sometimes collision_info
+ // is passed in prepopulated with a problematic floor.
+ for (const auto &colinfo : result.collisions) {
+ if (colinfo.axis == COLLISION_AXIS_Y) {
+ // we cannot rely on m_standing_node because "sneak stuff"
+ standing_node_bouncy = itemgroup_get(nodemgr->get(map->getNode(colinfo.node_p)).groups, "bouncy");
+ if (standing_node_bouncy != 0)
+ break;
+ }
+ }
+ }
+
// Determine if jumping is possible
m_disable_jump = itemgroup_get(f.groups, "disable_jump") ||
itemgroup_get(f1.groups, "disable_jump");
- m_can_jump = ((touching_ground && !is_climbing) || sneak_can_jump) && !m_disable_jump;
+ m_can_jump = ((touching_ground && !is_climbing) || sneak_can_jump || standing_node_bouncy != 0)
+ && !m_disable_jump;
- // Jump key pressed while jumping off from a bouncy block
- if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
- m_speed.Y >= -0.5f * BS) {
- float jumpspeed = movement_speed_jump * physics_override.jump;
- if (m_speed.Y > 1.0f) {
- // Reduce boost when speed already is high
- m_speed.Y += jumpspeed / (1.0f + (m_speed.Y / 16.0f));
+ // Jump/Sneak key pressed while bouncing from a bouncy block
+ float jumpspeed = movement_speed_jump * physics_override.jump;
+ if (m_can_jump && (control.jump || control.sneak) && standing_node_bouncy > 0) {
+ // controllable (>0) bouncy block
+ if (!control.jump) {
+ // sneak pressed, but not jump
+ // Subjective testing indicates 1/3 bounce decrease works well.
+ jumpspeed = -m_speed.Y / 3.0f;
} else {
- m_speed.Y += jumpspeed;
+ // jump pressed
+ // Reduce boost when speed already is high
+ jumpspeed = jumpspeed / (1.0f + (m_speed.Y * 2.8f / jumpspeed));
}
+ m_speed.Y += jumpspeed;
setSpeed(m_speed);
m_can_jump = false;
+ } else if(m_speed.Y > jumpspeed && standing_node_bouncy < 0) {
+ // uncontrollable bouncy is limited to normal jump height.
+ m_can_jump = false;
}
// Autojump
m_standing_node = floatToInt(m_position, BS);
/*
- If the player's feet touch the topside of any node, this is
- set to true.
+ If the player's feet touch the topside of any node
+ at the END of clientstep, then this is set to true.
Player is allowed to jump when this is true.
*/
*/
const ContentFeatures &f = nodemgr->get(map->getNode(getStandingNodePos()));
+ // We can jump from a bouncy node we collided with this clientstep,
+ // even if we are not "touching" it at the end of clientstep.
+ int standing_node_bouncy = 0;
+ if (result.collides && m_speed.Y > 0.0f) {
+ // must use result.collisions here because sometimes collision_info
+ // is passed in prepopulated with a problematic floor.
+ for (const auto &colinfo : result.collisions) {
+ if (colinfo.axis == COLLISION_AXIS_Y) {
+ // we cannot rely on m_standing_node because "sneak stuff"
+ standing_node_bouncy = itemgroup_get(nodemgr->get(map->getNode(colinfo.node_p)).groups, "bouncy");
+ if (standing_node_bouncy != 0)
+ break;
+ }
+ }
+ }
+
// Determine if jumping is possible
m_disable_jump = itemgroup_get(f.groups, "disable_jump");
- m_can_jump = touching_ground && !m_disable_jump;
+ m_can_jump = (touching_ground || standing_node_bouncy != 0) && !m_disable_jump;
- // Jump key pressed while jumping off from a bouncy block
- if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
- m_speed.Y >= -0.5f * BS) {
- float jumpspeed = movement_speed_jump * physics_override.jump;
- if (m_speed.Y > 1.0f) {
- // Reduce boost when speed already is high
- m_speed.Y += jumpspeed / (1.0f + (m_speed.Y / 16.0f));
+ // Jump/Sneak key pressed while bouncing from a bouncy block
+ float jumpspeed = movement_speed_jump * physics_override.jump;
+ if (m_can_jump && (control.jump || control.sneak) && standing_node_bouncy > 0) {
+ // controllable (>0) bouncy block
+ if (!control.jump) {
+ // sneak pressed, but not jump
+ // Subjective testing indicates 1/3 bounce decrease works well.
+ jumpspeed = -m_speed.Y / 3.0f;
} else {
- m_speed.Y += jumpspeed;
+ // jump pressed
+ // Reduce boost when speed already is high
+ jumpspeed = jumpspeed / (1.0f + (m_speed.Y * 2.8f / jumpspeed));
}
+ m_speed.Y += jumpspeed;
setSpeed(m_speed);
m_can_jump = false;
+ } else if(m_speed.Y > jumpspeed && standing_node_bouncy < 0) {
+ // uncontrollable bouncy is limited to normal jump height.
+ m_can_jump = false;
}
// Autojump