Fix player coordinate rounding in collisionMoveSimple() (#6197)

To determine the area (nodes) where a player movement took place
collisionMoveSimple() first took the old/new player coordinates and rounded
them to integers, then added the player character's collision box and
implicitely rounded the result. This has 2 problems:

Rounding the position and the box seperately, then adding the resulting
integers means you get twice the rounding error. And implicit rounding
always rounds towards 0.0, unlike floatToInt(), which rounds towards the
closest integer.

Previous (simplified) behavior: round(pos)+(int)box, for example player at
Y=0.9, body is 1.75m high: round(0.9)+(int)1.75 = 1+1 = 2.
==> A character's height of 1.75m always got rounded down to 1m, its width
of +/-0.3 even became 0.

Fixed by adding the floats first, then rounding properly: round(pos+box) =
round(0.9+1.75) = round(2.65) = 3.
This commit is contained in:
Jens Rottmann 2017-08-04 21:48:32 +02:00 committed by SmallJoker
parent e5311a4d56
commit 90a9e4e69f

@ -258,20 +258,25 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
//TimeTaker tt2("collisionMoveSimple collect boxes"); //TimeTaker tt2("collisionMoveSimple collect boxes");
ScopeProfiler sp(g_profiler, "collisionMoveSimple collect boxes avg", SPT_AVG); ScopeProfiler sp(g_profiler, "collisionMoveSimple collect boxes avg", SPT_AVG);
v3s16 oldpos_i = floatToInt(*pos_f, BS); v3f newpos_f = *pos_f + *speed_f * dtime;
v3s16 newpos_i = floatToInt(*pos_f + *speed_f * dtime, BS); v3f minpos_f(
s16 min_x = MYMIN(oldpos_i.X, newpos_i.X) + (box_0.MinEdge.X / BS) - 1; MYMIN(pos_f->X, newpos_f.X),
s16 min_y = MYMIN(oldpos_i.Y, newpos_i.Y) + (box_0.MinEdge.Y / BS) - 1; MYMIN(pos_f->Y, newpos_f.Y),
s16 min_z = MYMIN(oldpos_i.Z, newpos_i.Z) + (box_0.MinEdge.Z / BS) - 1; MYMIN(pos_f->Z, newpos_f.Z)
s16 max_x = MYMAX(oldpos_i.X, newpos_i.X) + (box_0.MaxEdge.X / BS) + 1; );
s16 max_y = MYMAX(oldpos_i.Y, newpos_i.Y) + (box_0.MaxEdge.Y / BS) + 1; v3f maxpos_f(
s16 max_z = MYMAX(oldpos_i.Z, newpos_i.Z) + (box_0.MaxEdge.Z / BS) + 1; MYMAX(pos_f->X, newpos_f.X),
MYMAX(pos_f->Y, newpos_f.Y),
MYMAX(pos_f->Z, newpos_f.Z)
);
v3s16 min = floatToInt(minpos_f + box_0.MinEdge, BS) - v3s16(1, 1, 1);
v3s16 max = floatToInt(maxpos_f + box_0.MaxEdge, BS) + v3s16(1, 1, 1);
bool any_position_valid = false; bool any_position_valid = false;
for(s16 x = min_x; x <= max_x; x++) for(s16 x = min.X; x <= max.X; x++)
for(s16 y = min_y; y <= max_y; y++) for(s16 y = min.Y; y <= max.Y; y++)
for(s16 z = min_z; z <= max_z; z++) for(s16 z = min.Z; z <= max.Z; z++)
{ {
v3s16 p(x,y,z); v3s16 p(x,y,z);