/*
-Minetest-c55
-Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
+Minetest
+Copyright (C) 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
#include "nodedef.h"
#include "gamedef.h"
#include "log.h"
+#include "environment.h"
+#include "serverobject.h"
#include <vector>
+#include <set>
#include "util/timetaker.h"
#include "main.h" // g_profiler
#include "profiler.h"
}
-collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef,
+collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
f32 pos_max_d, const aabb3f &box_0,
f32 stepheight, f32 dtime,
- v3f &pos_f, v3f &speed_f, v3f &accel_f)
+ v3f &pos_f, v3f &speed_f, v3f &accel_f,ActiveObject* self)
{
+ Map *map = &env->getMap();
//TimeTaker tt("collisionMoveSimple");
ScopeProfiler sp(g_profiler, "collisionMoveSimple avg", SPT_AVG);
/*
Calculate new velocity
*/
+ if( dtime > 0.5 ) {
+ infostream<<"collisionMoveSimple: WARNING: maximum step interval exceeded, lost movement details!"<<std::endl;
+ dtime = 0.5;
+ }
speed_f += accel_f * dtime;
- // If there is no speed, there are no collisions
+ // If there is no speed, there are no collisions
if(speed_f.getLength() == 0)
return result;
+ // Limit speed for avoiding hangs
+ speed_f.Y=rangelim(speed_f.Y,-5000,5000);
+ speed_f.X=rangelim(speed_f.X,-5000,5000);
+ speed_f.Z=rangelim(speed_f.Z,-5000,5000);
+
/*
Collect node boxes in movement range
*/
std::vector<aabb3f> cboxes;
std::vector<bool> is_unloaded;
std::vector<bool> is_step_up;
+ std::vector<bool> is_object;
+ std::vector<int> bouncy_values;
+ std::vector<v3s16> node_positions;
{
//TimeTaker tt2("collisionMoveSimple collect boxes");
ScopeProfiler sp(g_profiler, "collisionMoveSimple collect boxes avg", SPT_AVG);
for(s16 y = min_y; y <= max_y; y++)
for(s16 z = min_z; z <= max_z; z++)
{
+ v3s16 p(x,y,z);
try{
// Object collides into walkable nodes
- MapNode n = map->getNode(v3s16(x,y,z));
- if(gamedef->getNodeDefManager()->get(n).walkable == false)
+ MapNode n = map->getNode(p);
+ const ContentFeatures &f = gamedef->getNodeDefManager()->get(n);
+ if(f.walkable == false)
continue;
+ int n_bouncy_value = itemgroup_get(f.groups, "bouncy");
std::vector<aabb3f> nodeboxes = n.getNodeBoxes(gamedef->ndef());
for(std::vector<aabb3f>::iterator
cboxes.push_back(box);
is_unloaded.push_back(false);
is_step_up.push_back(false);
+ bouncy_values.push_back(n_bouncy_value);
+ node_positions.push_back(p);
+ is_object.push_back(false);
}
}
catch(InvalidPositionException &e)
{
// Collide with unloaded nodes
- aabb3f box = getNodeBox(v3s16(x,y,z), BS);
+ aabb3f box = getNodeBox(p, BS);
cboxes.push_back(box);
is_unloaded.push_back(true);
is_step_up.push_back(false);
+ bouncy_values.push_back(0);
+ node_positions.push_back(p);
+ is_object.push_back(false);
}
}
} // tt2
+ {
+ ScopeProfiler sp(g_profiler, "collisionMoveSimple objects avg", SPT_AVG);
+ //TimeTaker tt3("collisionMoveSimple collect object boxes");
+
+ /* add object boxes to cboxes */
+
+
+ std::list<ActiveObject*> objects;
+#ifndef SERVER
+ ClientEnvironment *c_env = dynamic_cast<ClientEnvironment*>(env);
+ if (c_env != 0)
+ {
+ f32 distance = speed_f.getLength();
+ std::vector<DistanceSortedActiveObject> clientobjects;
+ c_env->getActiveObjects(pos_f,distance * 1.5,clientobjects);
+ for (size_t i=0; i < clientobjects.size(); i++)
+ {
+ if ((self == 0) || (self != clientobjects[i].obj)) {
+ objects.push_back((ActiveObject*)clientobjects[i].obj);
+ }
+ }
+ }
+ else
+#endif
+ {
+ ServerEnvironment *s_env = dynamic_cast<ServerEnvironment*>(env);
+ if (s_env != 0)
+ {
+ f32 distance = speed_f.getLength();
+ std::set<u16> s_objects = s_env->getObjectsInsideRadius(pos_f,distance * 1.5);
+ for (std::set<u16>::iterator iter = s_objects.begin(); iter != s_objects.end(); iter++)
+ {
+ ServerActiveObject *current = s_env->getActiveObject(*iter);
+ if ((self == 0) || (self != current)) {
+ objects.push_back((ActiveObject*)current);
+ }
+ }
+ }
+ }
+
+ for (std::list<ActiveObject*>::const_iterator iter = objects.begin();iter != objects.end(); ++iter)
+ {
+ ActiveObject *object = *iter;
+
+ if (object != NULL)
+ {
+ aabb3f object_collisionbox;
+ if (object->getCollisionBox(&object_collisionbox))
+ {
+ cboxes.push_back(object_collisionbox);
+ is_unloaded.push_back(false);
+ is_step_up.push_back(false);
+ bouncy_values.push_back(0);
+ node_positions.push_back(v3s16(0,0,0));
+ is_object.push_back(true);
+ }
+ }
+ }
+ } //tt3
+
assert(cboxes.size() == is_unloaded.size());
assert(cboxes.size() == is_step_up.size());
+ assert(cboxes.size() == bouncy_values.size());
+ assert(cboxes.size() == node_positions.size());
+ assert(cboxes.size() == is_object.size());
/*
Collision detection
cbox.MaxEdge.Y - movingbox.MinEdge.Y,
d));
+ // Get bounce multiplier
+ bool bouncy = (bouncy_values[nearest_boxindex] >= 1);
+ float bounce = -(float)bouncy_values[nearest_boxindex] / 100.0;
+
// Move to the point of collision and reduce dtime by nearest_dtime
if(nearest_dtime < 0)
{
pos_f += speed_f * nearest_dtime;
dtime -= nearest_dtime;
}
+
+ bool is_collision = true;
+ if(is_unloaded[nearest_boxindex])
+ is_collision = false;
+
+ CollisionInfo info;
+ if (is_object[nearest_boxindex]) {
+ info.type = COLLISION_OBJECT;
+ }
+ else {
+ info.type = COLLISION_NODE;
+ }
+ info.node_p = node_positions[nearest_boxindex];
+ info.bouncy = bouncy;
+ info.old_speed = speed_f;
// Set the speed component that caused the collision to zero
if(step_up)
{
// Special case: Handle stairs
is_step_up[nearest_boxindex] = true;
+ is_collision = false;
}
else if(nearest_collided == 0) // X
{
- speed_f.X = 0;
+ if(fabs(speed_f.X) > BS*3)
+ speed_f.X *= bounce;
+ else
+ speed_f.X = 0;
result.collides = true;
result.collides_xz = true;
}
else if(nearest_collided == 1) // Y
{
- speed_f.Y = 0;
+ if(fabs(speed_f.Y) > BS*3)
+ speed_f.Y *= bounce;
+ else
+ speed_f.Y = 0;
result.collides = true;
}
else if(nearest_collided == 2) // Z
{
- speed_f.Z = 0;
+ if(fabs(speed_f.Z) > BS*3)
+ speed_f.Z *= bounce;
+ else
+ speed_f.Z = 0;
result.collides = true;
result.collides_xz = true;
}
+
+ info.new_speed = speed_f;
+ if(info.new_speed.getDistanceFrom(info.old_speed) < 0.1*BS)
+ is_collision = false;
+
+ if(is_collision){
+ result.collisions.push_back(info);
+ }
}
}