/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "localplayer.h"
#include <cmath>
#include "mtevent.h"
#include "collision.h"
#include "nodedef.h"
#include "settings.h"
#include "environment.h"
#include "map.h"
#include "client.h"
#include "content_cao.h"
/*
LocalPlayer
*/
LocalPlayer::LocalPlayer(Client *client, const char *name):
Player(name, client->idef()),
m_client(client)
{
}
static aabb3f getNodeBoundingBox(const std::vector<aabb3f> &nodeboxes)
{
if (nodeboxes.empty())
return aabb3f(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
aabb3f b_max;
std::vector<aabb3f>::const_iterator it = nodeboxes.begin();
b_max = aabb3f(it->MinEdge, it->MaxEdge);
++it;
for (; it != nodeboxes.end(); ++it)
b_max.addInternalBox(*it);
return b_max;
}
bool LocalPlayer::updateSneakNode(Map *map, const v3f &position,
const v3f &sneak_max)
{
static const v3s16 dir9_center[9] = {
v3s16( 0, 0, 0),
v3s16( 1, 0, 0),
v3s16(-1, 0, 0),
v3s16( 0, 0, 1),
v3s16( 0, 0, -1),
v3s16( 1, 0, 1),
v3s16(-1, 0, 1),
v3s16( 1, 0, -1),
v3s16(-1, 0, -1)
};
const NodeDefManager *nodemgr = m_client->ndef();
MapNode node;
bool is_valid_position;
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;
if (m_sneak_node_exists)
position_y_mod = m_sneak_node_bb_top.MaxEdge.Y - position_y_mod;
// Get position of current standing node
const v3s16 current_node = floatToInt(position - v3f(0.0f, position_y_mod, 0.0f), BS);
if (current_node != m_sneak_node) {
new_sneak_node_exists = false;
} else {
node = map->getNode(current_node, &is_valid_position);
if (!is_valid_position || !nodemgr->get(node).walkable)
new_sneak_node_exists = false;
}
// Keep old sneak node
if (new_sneak_node_exists)
return true;
// Get new sneak node
m_sneak_ladder_detected = false;
f32 min_distance_f = 100000.0f * BS;
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)
continue;
// 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)
continue;
// And the node(s) above have to be nonwalkable
bool ok = true;
if (!physics_override_sneak_glitch) {
u16 height =
ceilf((m_collisionbox.MaxEdge.Y - m_collisionbox.MinEdge.Y) / BS);
for (u16 y = 1; y <= height; y++) {
node = map->getNode(p + v3s16(0, y, 0), &is_valid_position);
if (!is_valid_position || nodemgr->get(node).walkable) {
ok = false;
break;
}
}
} else {
// legacy behaviour: check just one node
node = map->getNode(p + v3s16(0, 1, 0), &is_valid_position);
ok = is_valid_position && !nodemgr->get(node).walkable;
}
if (!ok)
continue;
min_distance_f = distance_f;
m_sneak_node = p;
new_sneak_node_exists = true;
}
if (!new_sneak_node_exists)
return false;
// Update saved top bounding box of sneak node
node = map->getNode(m_sneak_node);
std::vector<aabb3f> nodeboxes;
node.getCollisionBoxes(nodemgr, &nodeboxes);
m_sneak_node_bb_top = getNodeBoundingBox(nodeboxes);
if (physics_override_sneak_glitch) {
// Detect sneak ladder:
// Node two meters above sneak node must be solid
node = map->getNode(m_sneak_node + v3s16(0, 2, 0),
&is_valid_position);
if (is_valid_position && nodemgr->get(node).walkable) {
// Node three meters above: must be non-solid
node = map->getNode(m_sneak_node + v3s16(0, 3, 0),
&is_valid_position);
m_sneak_ladder_detected = is_valid_position &&
|