From 6bf4931fc28c85053acdbb2e08261d2290583ea4 Mon Sep 17 00:00:00 2001 From: Kahrl Date: Thu, 2 Feb 2012 19:20:46 +0100 Subject: [PATCH 1/6] NDT_NODEBOX, collision code rewrite with step height, stairs mod -- rebased after itemdef --- data/mods/stairs/depends.txt | 1 + data/mods/stairs/init.lua | 93 +++++++ src/collision.cpp | 517 +++++++++++++++++++++++++---------- src/collision.h | 33 ++- src/content_cao.cpp | 10 +- src/content_mapblock.cpp | 66 +++++ src/content_sao.cpp | 32 ++- src/game.cpp | 246 +++++------------ src/mapnode.cpp | 91 ++++++ src/mapnode.h | 12 + src/nodedef.cpp | 84 ++++-- src/nodedef.h | 24 +- src/player.cpp | 224 ++------------- src/scriptapi.cpp | 115 +++++--- src/test.cpp | 149 ++++++++++ src/tile.h | 8 + 16 files changed, 1090 insertions(+), 615 deletions(-) create mode 100644 data/mods/stairs/depends.txt create mode 100644 data/mods/stairs/init.lua diff --git a/data/mods/stairs/depends.txt b/data/mods/stairs/depends.txt new file mode 100644 index 000000000..4ad96d515 --- /dev/null +++ b/data/mods/stairs/depends.txt @@ -0,0 +1 @@ +default diff --git a/data/mods/stairs/init.lua b/data/mods/stairs/init.lua new file mode 100644 index 000000000..cb3de3c27 --- /dev/null +++ b/data/mods/stairs/init.lua @@ -0,0 +1,93 @@ +stairs = {} + +-- Node will be called stairs:stair_ +function stairs.register_stair(subname, recipeitem, material, images, description) + minetest.register_node("stairs:stair_" .. subname, { + description = description, + drawtype = "nodebox", + tile_images = images, + paramtype = "light", + paramtype2 = "facedir", + is_ground_content = true, + node_box = { + type = "fixed", + fixed = { + {-0.5, -0.5, -0.5, 0.5, 0, 0.5}, + {-0.5, 0, 0, 0.5, 0.5, 0.5}, + }, + }, + material = material, + }) + + minetest.register_craft({ + output = 'stairs:stair_' .. subname .. ' 4', + recipe = { + {recipeitem, "", ""}, + {recipeitem, recipeitem, ""}, + {recipeitem, recipeitem, recipeitem}, + }, + }) +end + +-- Node will be called stairs:slab_ +function stairs.register_slab(subname, recipeitem, material, images, description) + minetest.register_node("stairs:slab_" .. subname, { + description = description, + drawtype = "nodebox", + tile_images = images, + paramtype = "light", + is_ground_content = true, + node_box = { + type = "fixed", + fixed = {-0.5, -0.5, -0.5, 0.5, 0, 0.5}, + }, + selection_box = { + type = "fixed", + fixed = {-0.5, -0.5, -0.5, 0.5, 0, 0.5}, + }, + material = material, + }) + + minetest.register_craft({ + output = 'stairs:slab_' .. subname .. ' 3', + recipe = { + {recipeitem, recipeitem, recipeitem}, + }, + }) +end + +-- Nodes will be called stairs:{stair,slab}_ +function stairs.register_stair_and_slab(subname, recipeitem, material, images, desc_stair, desc_slab) + stairs.register_stair(subname, recipeitem, material, images, desc_stair) + stairs.register_slab(subname, recipeitem, material, images, desc_slab) +end + +stairs.register_stair_and_slab("wood", "default:wood", + minetest.digprop_woodlike(0.75), + {"default_wood.png"}, + "Wooden stair", + "Wooden slab") + +stairs.register_stair_and_slab("stone", "default:stone", + minetest.digprop_stonelike(0.75), + {"default_stone.png"}, + "Stone stair", + "Stone slab") + +stairs.register_stair_and_slab("cobble", "default:cobble", + minetest.digprop_stonelike(0.75), + {"default_cobble.png"}, + "Cobble stair", + "Cobble slab") + +stairs.register_stair_and_slab("brick", "default:brick", + minetest.digprop_stonelike(0.75), + {"default_brick.png"}, + "Brick stair", + "Brick slab") + +stairs.register_stair_and_slab("sandstone", "default:sandstone", + minetest.digprop_stonelike(0.75), + {"default_sandstone.png"}, + "Sandstone stair", + "Sandstone slab") diff --git a/src/collision.cpp b/src/collision.cpp index 3460b04fd..d131c7a20 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -22,28 +22,240 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "map.h" #include "nodedef.h" #include "gamedef.h" +#include "log.h" +#include + +// Helper function: +// Checks for collision of a moving aabbox with a static aabbox +// Returns -1 if no collision, 0 if X collision, 1 if Y collision, 2 if Z collision +// The time after which the collision occurs is stored in dtime. +int axisAlignedCollision( + const aabb3f &staticbox, const aabb3f &movingbox, + const v3f &speed, f32 d, f32 &dtime) +{ + //TimeTaker tt("axisAlignedCollision"); + + f32 xsize = (staticbox.MaxEdge.X - staticbox.MinEdge.X); + f32 ysize = (staticbox.MaxEdge.Y - staticbox.MinEdge.Y); + f32 zsize = (staticbox.MaxEdge.Z - staticbox.MinEdge.Z); + + aabb3f relbox( + movingbox.MinEdge.X - staticbox.MinEdge.X, + movingbox.MinEdge.Y - staticbox.MinEdge.Y, + movingbox.MinEdge.Z - staticbox.MinEdge.Z, + movingbox.MaxEdge.X - staticbox.MinEdge.X, + movingbox.MaxEdge.Y - staticbox.MinEdge.Y, + movingbox.MaxEdge.Z - staticbox.MinEdge.Z + ); + + if(speed.X > 0) // Check for collision with X- plane + { + if(relbox.MaxEdge.X <= d) + { + dtime = - relbox.MaxEdge.X / speed.X; + if((relbox.MinEdge.Y + speed.Y * dtime < ysize) && + (relbox.MaxEdge.Y + speed.Y * dtime > 0) && + (relbox.MinEdge.Z + speed.Z * dtime < zsize) && + (relbox.MaxEdge.Z + speed.Z * dtime > 0)) + return 0; + } + else if(relbox.MinEdge.X > xsize) + { + return -1; + } + } + else if(speed.X < 0) // Check for collision with X+ plane + { + if(relbox.MinEdge.X >= xsize - d) + { + dtime = (xsize - relbox.MinEdge.X) / speed.X; + if((relbox.MinEdge.Y + speed.Y * dtime < ysize) && + (relbox.MaxEdge.Y + speed.Y * dtime > 0) && + (relbox.MinEdge.Z + speed.Z * dtime < zsize) && + (relbox.MaxEdge.Z + speed.Z * dtime > 0)) + return 0; + } + else if(relbox.MaxEdge.X < 0) + { + return -1; + } + } + + // NO else if here + + if(speed.Y > 0) // Check for collision with Y- plane + { + if(relbox.MaxEdge.Y <= d) + { + dtime = - relbox.MaxEdge.Y / speed.Y; + if((relbox.MinEdge.X + speed.X * dtime < xsize) && + (relbox.MaxEdge.X + speed.X * dtime > 0) && + (relbox.MinEdge.Z + speed.Z * dtime < zsize) && + (relbox.MaxEdge.Z + speed.Z * dtime > 0)) + return 1; + } + else if(relbox.MinEdge.Y > ysize) + { + return -1; + } + } + else if(speed.Y < 0) // Check for collision with Y+ plane + { + if(relbox.MinEdge.Y >= ysize - d) + { + dtime = (ysize - relbox.MinEdge.Y) / speed.Y; + if((relbox.MinEdge.X + speed.X * dtime < xsize) && + (relbox.MaxEdge.X + speed.X * dtime > 0) && + (relbox.MinEdge.Z + speed.Z * dtime < zsize) && + (relbox.MaxEdge.Z + speed.Z * dtime > 0)) + return 1; + } + else if(relbox.MaxEdge.Y < 0) + { + return -1; + } + } + + // NO else if here + + if(speed.Z > 0) // Check for collision with Z- plane + { + if(relbox.MaxEdge.Z <= d) + { + dtime = - relbox.MaxEdge.Z / speed.Z; + if((relbox.MinEdge.X + speed.X * dtime < xsize) && + (relbox.MaxEdge.X + speed.X * dtime > 0) && + (relbox.MinEdge.Y + speed.Y * dtime < ysize) && + (relbox.MaxEdge.Y + speed.Y * dtime > 0)) + return 2; + } + //else if(relbox.MinEdge.Z > zsize) + //{ + // return -1; + //} + } + else if(speed.Z < 0) // Check for collision with Z+ plane + { + if(relbox.MinEdge.Z >= zsize - d) + { + dtime = (zsize - relbox.MinEdge.Z) / speed.Z; + if((relbox.MinEdge.X + speed.X * dtime < xsize) && + (relbox.MaxEdge.X + speed.X * dtime > 0) && + (relbox.MinEdge.Y + speed.Y * dtime < ysize) && + (relbox.MaxEdge.Y + speed.Y * dtime > 0)) + return 2; + } + //else if(relbox.MaxEdge.Z < 0) + //{ + // return -1; + //} + } + + return -1; +} + +// Helper function: +// Checks if moving the movingbox up by the given distance would hit a ceiling. +bool wouldCollideWithCeiling( + const std::vector &staticboxes, + const aabb3f &movingbox, + f32 y_increase, f32 d) +{ + //TimeTaker tt("wouldCollideWithCeiling"); + + assert(y_increase >= 0); + + for(std::vector::const_iterator + i = staticboxes.begin(); + i != staticboxes.end(); i++) + { + const aabb3f& staticbox = *i; + if((movingbox.MaxEdge.Y - d <= staticbox.MinEdge.Y) && + (movingbox.MaxEdge.Y + y_increase > staticbox.MinEdge.Y) && + (movingbox.MinEdge.X < staticbox.MaxEdge.X) && + (movingbox.MaxEdge.X > staticbox.MinEdge.X) && + (movingbox.MinEdge.Z < staticbox.MaxEdge.Z) && + (movingbox.MaxEdge.Z > staticbox.MinEdge.Z)) + return true; + } + + return false; +} + collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef, - f32 pos_max_d, const core::aabbox3d &box_0, - f32 dtime, v3f &pos_f, v3f &speed_f) + f32 pos_max_d, const aabb3f &box_0, + f32 stepheight, f32 dtime, + v3f &pos_f, v3f &speed_f, v3f &accel_f) { + TimeTaker tt("collisionMoveSimple"); + collisionMoveResult result; - v3f oldpos_f = pos_f; - v3s16 oldpos_i = floatToInt(oldpos_f, BS); + /* + Calculate new velocity + */ + speed_f += accel_f * dtime; /* - Calculate new position + Collect node boxes in movement range */ - pos_f += speed_f * dtime; + std::vector cboxes; + std::vector is_unloaded; + std::vector is_step_up; + { + TimeTaker tt2("collisionMoveSimple collect boxes"); + + v3s16 oldpos_i = floatToInt(pos_f, BS); + v3s16 newpos_i = floatToInt(pos_f + speed_f * dtime, BS); + s16 min_x = MYMIN(oldpos_i.X, newpos_i.X) + (box_0.MinEdge.X / BS) - 1; + s16 min_y = MYMIN(oldpos_i.Y, newpos_i.Y) + (box_0.MinEdge.Y / BS) - 1; + s16 min_z = MYMIN(oldpos_i.Z, newpos_i.Z) + (box_0.MinEdge.Z / BS) - 1; + 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; + s16 max_z = MYMAX(oldpos_i.Z, newpos_i.Z) + (box_0.MaxEdge.Z / BS) + 1; + + for(s16 x = min_x; x <= max_x; x++) + for(s16 y = min_y; y <= max_y; y++) + for(s16 z = min_z; z <= max_z; z++) + { + try{ + // Object collides into walkable nodes + MapNode n = map->getNode(v3s16(x,y,z)); + if(gamedef->getNodeDefManager()->get(n).walkable == false) + continue; + + std::vector nodeboxes = n.getNodeBoxes(gamedef->ndef()); + for(std::vector::iterator + i = nodeboxes.begin(); + i != nodeboxes.end(); i++) + { + aabb3f box = *i; + box.MinEdge += v3f(x, y, z)*BS; + box.MaxEdge += v3f(x, y, z)*BS; + cboxes.push_back(box); + is_unloaded.push_back(false); + is_step_up.push_back(false); + } + } + catch(InvalidPositionException &e) + { + // Collide with unloaded nodes + aabb3f box = getNodeBox(v3s16(x,y,z), BS); + cboxes.push_back(box); + is_unloaded.push_back(true); + is_step_up.push_back(false); + } + } + } // tt2 + + assert(cboxes.size() == is_unloaded.size()); + assert(cboxes.size() == is_step_up.size()); /* Collision detection */ - - // position in nodes - v3s16 pos_i = floatToInt(pos_f, BS); - + /* Collision uncertainty radius Make it a bit larger than the maximum distance of movement @@ -54,49 +266,128 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef, // This should always apply, otherwise there are glitches assert(d > pos_max_d); - - /* - Calculate collision box - */ - core::aabbox3d box = box_0; - box.MaxEdge += pos_f; - box.MinEdge += pos_f; - core::aabbox3d oldbox = box_0; - oldbox.MaxEdge += oldpos_f; - oldbox.MinEdge += oldpos_f; - /* - If the object lies on a walkable node, this is set to true. - */ - result.touching_ground = false; - - /* - Go through every node around the object - */ - s16 min_x = (box_0.MinEdge.X / BS) - 2; - s16 min_y = (box_0.MinEdge.Y / BS) - 2; - s16 min_z = (box_0.MinEdge.Z / BS) - 2; - s16 max_x = (box_0.MaxEdge.X / BS) + 1; - s16 max_y = (box_0.MaxEdge.Y / BS) + 1; - s16 max_z = (box_0.MaxEdge.Z / BS) + 1; - for(s16 y = oldpos_i.Y + min_y; y <= oldpos_i.Y + max_y; y++) - for(s16 z = oldpos_i.Z + min_z; z <= oldpos_i.Z + max_z; z++) - for(s16 x = oldpos_i.X + min_x; x <= oldpos_i.X + max_x; x++) + int loopcount = 0; + + while(dtime > BS*1e-10) { - try{ - // Object collides into walkable nodes - MapNode n = map->getNode(v3s16(x,y,z)); - if(gamedef->getNodeDefManager()->get(n).walkable == false) - continue; - } - catch(InvalidPositionException &e) + TimeTaker tt3("collisionMoveSimple dtime loop"); + + // Avoid infinite loop + loopcount++; + if(loopcount >= 100) { - // Doing nothing here will block the object from - // walking over map borders + infostream<<"collisionMoveSimple: WARNING: Loop count exceeded, aborting to avoid infiniite loop"< nodebox = getNodeBox(v3s16(x,y,z), BS); - + aabb3f movingbox = box_0; + movingbox.MinEdge += pos_f; + movingbox.MaxEdge += pos_f; + + int nearest_collided = -1; + f32 nearest_dtime = dtime; + u32 nearest_boxindex = -1; + + /* + Go through every nodebox, find nearest collision + */ + for(u32 boxindex = 0; boxindex < cboxes.size(); boxindex++) + { + // Ignore if already stepped up this nodebox. + if(is_step_up[boxindex]) + continue; + + // Find nearest collision of the two boxes (raytracing-like) + f32 dtime_tmp; + int collided = axisAlignedCollision( + cboxes[boxindex], movingbox, speed_f, d, dtime_tmp); + + if(collided == -1 || dtime_tmp >= nearest_dtime) + continue; + + nearest_dtime = dtime_tmp; + nearest_collided = collided; + nearest_boxindex = boxindex; + } + + if(nearest_collided == -1) + { + // No collision with any collision box. + pos_f += speed_f * dtime; + dtime = 0; // Set to 0 to avoid "infinite" loop due to small FP numbers + } + else + { + // Otherwise, a collision occurred. + + const aabb3f& cbox = cboxes[nearest_boxindex]; + + // Check for stairs. + bool step_up = (nearest_collided != 1) && // must not be Y direction + (movingbox.MinEdge.Y < cbox.MaxEdge.Y) && + (movingbox.MinEdge.Y + stepheight > cbox.MaxEdge.Y) && + (!wouldCollideWithCeiling(cboxes, movingbox, + cbox.MaxEdge.Y - movingbox.MinEdge.Y, + d)); + + // Move to the point of collision and reduce dtime by nearest_dtime + if(nearest_dtime < 0) + { + // Handle negative nearest_dtime (can be caused by the d allowance) + if(!step_up) + { + if(nearest_collided == 0) + pos_f.X += speed_f.X * nearest_dtime; + if(nearest_collided == 1) + pos_f.Y += speed_f.Y * nearest_dtime; + if(nearest_collided == 2) + pos_f.Z += speed_f.Z * nearest_dtime; + } + } + else + { + pos_f += speed_f * nearest_dtime; + dtime -= nearest_dtime; + } + + // Set the speed component that caused the collision to zero + if(step_up) + { + // Special case: Handle stairs + is_step_up[nearest_boxindex] = true; + } + else if(nearest_collided == 0) // X + { + speed_f.X = 0; + result.collides = true; + result.collides_xz = true; + } + else if(nearest_collided == 1) // Y + { + speed_f.Y = 0; + result.collides = true; + } + else if(nearest_collided == 2) // Z + { + speed_f.Z = 0; + result.collides = true; + result.collides_xz = true; + } + } + } + + /* + Final touches: Check if standing on ground, step up stairs. + */ + aabb3f box = box_0; + box.MinEdge += pos_f; + box.MaxEdge += pos_f; + for(u32 boxindex = 0; boxindex < cboxes.size(); boxindex++) + { + const aabb3f& cbox = cboxes[boxindex]; + /* See if the object is touching ground. @@ -107,108 +398,44 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef, Use 0.15*BS so that it is easier to get on a node. */ if( - //fabs(nodebox.MaxEdge.Y-box.MinEdge.Y) < d - fabs(nodebox.MaxEdge.Y-box.MinEdge.Y) < 0.15*BS - && nodebox.MaxEdge.X-d > box.MinEdge.X - && nodebox.MinEdge.X+d < box.MaxEdge.X - && nodebox.MaxEdge.Z-d > box.MinEdge.Z - && nodebox.MinEdge.Z+d < box.MaxEdge.Z + cbox.MaxEdge.X-d > box.MinEdge.X && + cbox.MinEdge.X+d < box.MaxEdge.X && + cbox.MaxEdge.Z-d > box.MinEdge.Z && + cbox.MinEdge.Z+d < box.MaxEdge.Z ){ - result.touching_ground = true; - } - - // If object doesn't intersect with node, ignore node. - if(box.intersectsWithBox(nodebox) == false) - continue; - - /* - Go through every axis - */ - v3f dirs[3] = { - v3f(0,0,1), // back-front - v3f(0,1,0), // top-bottom - v3f(1,0,0), // right-left - }; - for(u16 i=0; i<3; i++) - { - /* - Calculate values along the axis - */ - f32 nodemax = nodebox.MaxEdge.dotProduct(dirs[i]); - f32 nodemin = nodebox.MinEdge.dotProduct(dirs[i]); - f32 objectmax = box.MaxEdge.dotProduct(dirs[i]); - f32 objectmin = box.MinEdge.dotProduct(dirs[i]); - f32 objectmax_old = oldbox.MaxEdge.dotProduct(dirs[i]); - f32 objectmin_old = oldbox.MinEdge.dotProduct(dirs[i]); - - /* - Check collision for the axis. - Collision happens when object is going through a surface. - */ - bool negative_axis_collides = - (nodemax > objectmin && nodemax <= objectmin_old + d - && speed_f.dotProduct(dirs[i]) < 0); - bool positive_axis_collides = - (nodemin < objectmax && nodemin >= objectmax_old - d - && speed_f.dotProduct(dirs[i]) > 0); - bool main_axis_collides = - negative_axis_collides || positive_axis_collides; - - /* - Check overlap of object and node in other axes - */ - bool other_axes_overlap = true; - for(u16 j=0; j<3; j++) + if(is_step_up[boxindex]) { - if(j == i) - continue; - f32 nodemax = nodebox.MaxEdge.dotProduct(dirs[j]); - f32 nodemin = nodebox.MinEdge.dotProduct(dirs[j]); - f32 objectmax = box.MaxEdge.dotProduct(dirs[j]); - f32 objectmin = box.MinEdge.dotProduct(dirs[j]); - if(!(nodemax - d > objectmin && nodemin + d < objectmax)) - { - other_axes_overlap = false; - break; - } + pos_f.Y += (cbox.MaxEdge.Y - box.MinEdge.Y); + box = box_0; + box.MinEdge += pos_f; + box.MaxEdge += pos_f; } - - /* - If this is a collision, revert the pos_f in the main - direction. - */ - if(other_axes_overlap && main_axis_collides) + if(fabs(cbox.MaxEdge.Y-box.MinEdge.Y) < 0.15*BS) { - speed_f -= speed_f.dotProduct(dirs[i]) * dirs[i]; - pos_f -= pos_f.dotProduct(dirs[i]) * dirs[i]; - pos_f += oldpos_f.dotProduct(dirs[i]) * dirs[i]; - result.collides = true; + result.touching_ground = true; + if(is_unloaded[boxindex]) + result.standing_on_unloaded = true; } - } - } // xyz - + } + return result; } collisionMoveResult collisionMovePrecise(Map *map, IGameDef *gamedef, - f32 pos_max_d, const core::aabbox3d &box_0, - f32 dtime, v3f &pos_f, v3f &speed_f) + f32 pos_max_d, const aabb3f &box_0, + f32 stepheight, f32 dtime, + v3f &pos_f, v3f &speed_f, v3f &accel_f) { + TimeTaker tt("collisionMovePrecise"); + infostream<<"start collisionMovePrecise\n"; + collisionMoveResult final_result; - // Maximum time increment (for collision detection etc) - // time = distance / speed - f32 dtime_max_increment = pos_max_d / speed_f.getLength(); - - // Maximum time increment is 10ms or lower - if(dtime_max_increment > 0.01) - dtime_max_increment = 0.01; - // Don't allow overly huge dtime if(dtime > 2.0) dtime = 2.0; - + f32 dtime_downcount = dtime; u32 loopcount = 0; @@ -216,6 +443,16 @@ collisionMoveResult collisionMovePrecise(Map *map, IGameDef *gamedef, { loopcount++; + // Maximum time increment (for collision detection etc) + // time = distance / speed + f32 dtime_max_increment = 1.0; + if(speed_f.getLength() != 0) + dtime_max_increment = pos_max_d / speed_f.getLength(); + + // Maximum time increment is 10ms or lower + if(dtime_max_increment > 0.01) + dtime_max_increment = 0.01; + f32 dtime_part; if(dtime_downcount > dtime_max_increment) { @@ -234,17 +471,21 @@ collisionMoveResult collisionMovePrecise(Map *map, IGameDef *gamedef, } collisionMoveResult result = collisionMoveSimple(map, gamedef, - pos_max_d, box_0, dtime_part, pos_f, speed_f); + pos_max_d, box_0, stepheight, dtime_part, + pos_f, speed_f, accel_f); if(result.touching_ground) final_result.touching_ground = true; if(result.collides) final_result.collides = true; + if(result.collides_xz) + final_result.collides_xz = true; + if(result.standing_on_unloaded) + final_result.standing_on_unloaded = true; } while(dtime_downcount > 0.001); - + + infostream<<"end collisionMovePrecise\n"; return final_result; } - - diff --git a/src/collision.h b/src/collision.h index e823a08fe..d4a3f749c 100644 --- a/src/collision.h +++ b/src/collision.h @@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #define COLLISION_HEADER #include "common_irrlicht.h" +#include class Map; class IGameDef; @@ -29,22 +30,44 @@ struct collisionMoveResult { bool touching_ground; bool collides; + bool collides_xz; + bool standing_on_unloaded; collisionMoveResult(): touching_ground(false), - collides(false) + collides(false), + collides_xz(false), + standing_on_unloaded(false) {} }; // Moves using a single iteration; speed should not exceed pos_max_d/dtime collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef, - f32 pos_max_d, const core::aabbox3d &box_0, - f32 dtime, v3f &pos_f, v3f &speed_f); + f32 pos_max_d, const aabb3f &box_0, + f32 stepheight, f32 dtime, + v3f &pos_f, v3f &speed_f, v3f &accel_f); // Moves using as many iterations as needed collisionMoveResult collisionMovePrecise(Map *map, IGameDef *gamedef, - f32 pos_max_d, const core::aabbox3d &box_0, - f32 dtime, v3f &pos_f, v3f &speed_f); + f32 pos_max_d, const aabb3f &box_0, + f32 stepheight, f32 dtime, + v3f &pos_f, v3f &speed_f, v3f &accel_f); + +// Helper function: +// Checks for collision of a moving aabbox with a static aabbox +// Returns -1 if no collision, 0 if X collision, 1 if Y collision, 2 if Z collision +// dtime receives time until first collision, invalid if -1 is returned +int axisAlignedCollision( + const aabb3f &staticbox, const aabb3f &movingbox, + const v3f &speed, f32 d, f32 &dtime); + +// Helper function: +// Checks if moving the movingbox up by the given distance would hit a ceiling. +bool wouldCollideWithCeiling( + const std::vector &staticboxes, + const aabb3f &movingbox, + f32 y_increase, f32 d); + enum CollisionType { diff --git a/src/content_cao.cpp b/src/content_cao.cpp index a2708674b..0b7cbede7 100644 --- a/src/content_cao.cpp +++ b/src/content_cao.cpp @@ -1874,22 +1874,24 @@ public: box.MinEdge *= BS; box.MaxEdge *= BS; collisionMoveResult moveresult; - f32 pos_max_d = BS*0.25; // Distance per iteration + f32 pos_max_d = BS*0.125; // Distance per iteration + f32 stepheight = 0; v3f p_pos = m_position; v3f p_velocity = m_velocity; + v3f p_acceleration = m_acceleration; IGameDef *gamedef = env->getGameDef(); moveresult = collisionMovePrecise(&env->getMap(), gamedef, - pos_max_d, box, dtime, p_pos, p_velocity); + pos_max_d, box, stepheight, dtime, + p_pos, p_velocity, p_acceleration); // Apply results m_position = p_pos; m_velocity = p_velocity; + m_acceleration = p_acceleration; bool is_end_position = moveresult.collides; pos_translator.update(m_position, is_end_position, dtime); pos_translator.translate(dtime); updateNodePos(); - - m_velocity += dtime * m_acceleration; } else { m_position += dtime * m_velocity + 0.5 * dtime * dtime * m_acceleration; m_velocity += dtime * m_acceleration; diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp index dc1e1daed..b83691862 100644 --- a/src/content_mapblock.cpp +++ b/src/content_mapblock.cpp @@ -985,6 +985,72 @@ void mapblock_mesh_generate_special(MeshMakeData *data, u16 indices[] = {0,1,2,2,3,0}; collector.append(material_rail, vertices, 4, indices, 6); break;} + case NDT_NODEBOX: + { + v3s16 facedirs[6] = { + v3s16(0,1,0), + v3s16(0,-1,0), + v3s16(1,0,0), + v3s16(-1,0,0), + v3s16(0,0,1), + v3s16(0,0,-1), + }; + video::SMaterial material_nodebox[6]; + AtlasPointer pa_nodebox[6]; + + for(int i = 0; i < 6; i++) + { + material_nodebox[i].setFlag(video::EMF_LIGHTING, false); + material_nodebox[i].setFlag(video::EMF_BILINEAR_FILTER, false); + material_nodebox[i].setFlag(video::EMF_FOG_ENABLE, true); + material_nodebox[i].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; + TileSpec tile = getNodeTile(n, p, facedirs[i], + &data->m_temp_mods, tsrc, nodedef); + pa_nodebox[i] = tile.texture; + material_nodebox[i].setTexture(0, pa_nodebox[i].atlas); + } + + u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio, nodedef))); + video::SColor c = MapBlock_LightColor(255, l); + + v3f pos = intToFloat(p+blockpos_nodes, BS); + + std::vector boxes = n.getNodeBoxes(nodedef); + for(std::vector::iterator + i = boxes.begin(); + i != boxes.end(); i++) + { + aabb3f box = *i; + box.MinEdge += pos; + box.MaxEdge += pos; + + // Compute texture coords + f32 tx1 = (i->MinEdge.X/BS)+0.5; + f32 ty1 = (i->MinEdge.Y/BS)+0.5; + f32 tz1 = (i->MinEdge.Z/BS)+0.5; + f32 tx2 = (i->MaxEdge.X/BS)+0.5; + f32 ty2 = (i->MaxEdge.Y/BS)+0.5; + f32 tz2 = (i->MaxEdge.Z/BS)+0.5; + f32 txc[24] = { + // up + tx1, 1-tz2, tx2, 1-tz1, + // down + tx1, tz1, tx2, tz2, + // right + tz1, 1-ty2, tz2, 1-ty1, + // left + 1-tz2, 1-ty2, 1-tz1, 1-ty1, + // back + 1-tx2, 1-ty2, 1-tx1, 1-ty1, + // front + tx1, 1-ty2, tx2, 1-ty1, + }; + + makeCuboid(&collector, box, + material_nodebox, pa_nodebox, 6, + c, txc); + } + break;} } } } diff --git a/src/content_sao.cpp b/src/content_sao.cpp index 02be64c64..cb6be2f2c 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -163,9 +163,12 @@ void ItemSAO::step(float dtime, bool send_recommended) m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime); v3f pos_f = getBasePosition(); v3f pos_f_old = pos_f; + v3f accel_f = v3f(0,0,0); + f32 stepheight = 0; IGameDef *gamedef = m_env->getGameDef(); moveresult = collisionMoveSimple(&m_env->getMap(), gamedef, - pos_max_d, box, dtime, pos_f, m_speed_f); + pos_max_d, box, stepheight, dtime, + pos_f, m_speed_f, accel_f); if(send_recommended == false) return; @@ -404,9 +407,12 @@ void RatSAO::step(float dtime, bool send_recommended) m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime); v3f pos_f = getBasePosition(); v3f pos_f_old = pos_f; + v3f accel_f = v3f(0,0,0); + f32 stepheight = 0; IGameDef *gamedef = m_env->getGameDef(); moveresult = collisionMoveSimple(&m_env->getMap(), gamedef, - pos_max_d, box, dtime, pos_f, m_speed_f); + pos_max_d, box, stepheight, dtime, + pos_f, m_speed_f, accel_f); m_touching_ground = moveresult.touching_ground; setBasePosition(pos_f); @@ -652,9 +658,12 @@ void Oerkki1SAO::step(float dtime, bool send_recommended) m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);*/ v3f pos_f = getBasePosition(); v3f pos_f_old = pos_f; + v3f accel_f = v3f(0,0,0); + f32 stepheight = 0; IGameDef *gamedef = m_env->getGameDef(); moveresult = collisionMovePrecise(&m_env->getMap(), gamedef, - pos_max_d, box, dtime, pos_f, m_speed_f); + pos_max_d, box, stepheight, dtime, + pos_f, m_speed_f, accel_f); m_touching_ground = moveresult.touching_ground; // Do collision damage @@ -899,9 +908,12 @@ void FireflySAO::step(float dtime, bool send_recommended) m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime); v3f pos_f = getBasePosition(); v3f pos_f_old = pos_f; + v3f accel_f = v3f(0,0,0); + f32 stepheight = 0; IGameDef *gamedef = m_env->getGameDef(); moveresult = collisionMoveSimple(&m_env->getMap(), gamedef, - pos_max_d, box, dtime, pos_f, m_speed_f); + pos_max_d, box, stepheight, dtime, + pos_f, m_speed_f, accel_f); m_touching_ground = moveresult.touching_ground; setBasePosition(pos_f); @@ -1618,16 +1630,18 @@ void LuaEntitySAO::step(float dtime, bool send_recommended) box.MaxEdge *= BS; collisionMoveResult moveresult; f32 pos_max_d = BS*0.25; // Distance per iteration - v3f p_pos = getBasePosition(); + f32 stepheight = 0; // Maximum climbable step height + v3f p_pos = m_base_position; v3f p_velocity = m_velocity; + v3f p_acceleration = m_acceleration; IGameDef *gamedef = m_env->getGameDef(); moveresult = collisionMovePrecise(&m_env->getMap(), gamedef, - pos_max_d, box, dtime, p_pos, p_velocity); + pos_max_d, box, stepheight, dtime, + p_pos, p_velocity, p_acceleration); // Apply results - setBasePosition(p_pos); + m_base_position = p_pos; m_velocity = p_velocity; - - m_velocity += dtime * m_acceleration; + m_acceleration = p_acceleration; } else { m_base_position += dtime * m_velocity + 0.5 * dtime * dtime * m_acceleration; diff --git a/src/game.cpp b/src/game.cpp index a1f0fe07f..df41e7da5 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -293,44 +293,40 @@ PointedThing getPointedThing(Client *client, v3f player_position, core::line3d shootline, f32 d, bool liquids_pointable, bool look_for_object, - core::aabbox3d &hilightbox, - bool &should_show_hilightbox, + std::vector &hilightboxes, ClientActiveObject *&selected_object) { PointedThing result; - hilightbox = core::aabbox3d(0,0,0,0,0,0); - should_show_hilightbox = false; + hilightboxes.clear(); selected_object = NULL; - INodeDefManager *nodedef = client->getNodeDefManager(); - // First try to find a pointed at active object if(look_for_object) { selected_object = client->getSelectedActiveObject(d*BS, camera_position, shootline); - } - if(selected_object != NULL) - { - core::aabbox3d *selection_box - = selected_object->getSelectionBox(); - // Box should exist because object was returned in the - // first place - assert(selection_box); - v3f pos = selected_object->getPosition(); + if(selected_object != NULL) + { + if(selected_object->doShowSelectionBox()) + { + aabb3f *selection_box = selected_object->getSelectionBox(); + // Box should exist because object was + // returned in the first place + assert(selection_box); - hilightbox = core::aabbox3d( - selection_box->MinEdge + pos, - selection_box->MaxEdge + pos - ); + v3f pos = selected_object->getPosition(); + hilightboxes.push_back(aabb3f( + selection_box->MinEdge + pos, + selection_box->MaxEdge + pos)); + } - should_show_hilightbox = selected_object->doShowSelectionBox(); - result.type = POINTEDTHING_OBJECT; - result.object_id = selected_object->getId(); - return result; + result.type = POINTEDTHING_OBJECT; + result.object_id = selected_object->getId(); + return result; + } } // That didn't work, try to find a pointed at node @@ -366,196 +362,88 @@ PointedThing getPointedThing(Client *client, v3f player_position, if(!isPointableNode(n, client, liquids_pointable)) continue; + v3s16 facedirs[6] = { + v3s16(-1,0,0), + v3s16(1,0,0), + v3s16(0,-1,0), + v3s16(0,1,0), + v3s16(0,0,-1), + v3s16(0,0,1), + }; + + std::vector boxes = n.getSelectionBoxes(client->ndef()); + v3s16 np(x,y,z); v3f npf = intToFloat(np, BS); - - f32 d = 0.01; - - v3s16 dirs[6] = { - v3s16(0,0,1), // back - v3s16(0,1,0), // top - v3s16(1,0,0), // right - v3s16(0,0,-1), // front - v3s16(0,-1,0), // bottom - v3s16(-1,0,0), // left - }; - - const ContentFeatures &f = nodedef->get(n); - - if(f.selection_box.type == NODEBOX_FIXED) + + for(std::vector::const_iterator + i = boxes.begin(); + i != boxes.end(); i++) { - core::aabbox3d box = f.selection_box.fixed; + aabb3f box = *i; box.MinEdge += npf; box.MaxEdge += npf; - v3s16 facedirs[6] = { - v3s16(-1,0,0), - v3s16(1,0,0), - v3s16(0,-1,0), - v3s16(0,1,0), - v3s16(0,0,-1), - v3s16(0,0,1), - }; - - core::aabbox3d faceboxes[6] = { + f32 d = 0.001*BS; + aabb3f faceboxes[6] = { // X- - core::aabbox3d( + aabb3f( box.MinEdge.X, box.MinEdge.Y, box.MinEdge.Z, box.MinEdge.X+d, box.MaxEdge.Y, box.MaxEdge.Z ), // X+ - core::aabbox3d( + aabb3f( box.MaxEdge.X-d, box.MinEdge.Y, box.MinEdge.Z, box.MaxEdge.X, box.MaxEdge.Y, box.MaxEdge.Z ), // Y- - core::aabbox3d( + aabb3f( box.MinEdge.X, box.MinEdge.Y, box.MinEdge.Z, box.MaxEdge.X, box.MinEdge.Y+d, box.MaxEdge.Z ), // Y+ - core::aabbox3d( + aabb3f( box.MinEdge.X, box.MaxEdge.Y-d, box.MinEdge.Z, box.MaxEdge.X, box.MaxEdge.Y, box.MaxEdge.Z ), // Z- - core::aabbox3d( + aabb3f( box.MinEdge.X, box.MinEdge.Y, box.MinEdge.Z, box.MaxEdge.X, box.MaxEdge.Y, box.MinEdge.Z+d ), // Z+ - core::aabbox3d( + aabb3f( box.MinEdge.X, box.MinEdge.Y, box.MaxEdge.Z-d, box.MaxEdge.X, box.MaxEdge.Y, box.MaxEdge.Z ), }; - for(u16 i=0; i<6; i++) + for(u16 j=0; j<6; j++) { - v3f facedir_f(facedirs[i].X, facedirs[i].Y, facedirs[i].Z); - v3f centerpoint = npf + facedir_f * BS/2; + v3f centerpoint = faceboxes[j].getCenter(); f32 distance = (centerpoint - camera_position).getLength(); if(distance >= mindistance) continue; - if(!faceboxes[i].intersectsWithLine(shootline)) + if(!faceboxes[j].intersectsWithLine(shootline)) continue; + result.type = POINTEDTHING_NODE; result.node_undersurface = np; - result.node_abovesurface = np+facedirs[i]; + result.node_abovesurface = np+facedirs[j]; mindistance = distance; - hilightbox = box; - should_show_hilightbox = true; - } - } - else if(f.selection_box.type == NODEBOX_WALLMOUNTED) - { - v3s16 dir = n.getWallMountedDir(nodedef); - v3f dir_f = v3f(dir.X, dir.Y, dir.Z); - dir_f *= BS/2 - BS/6 - BS/20; - v3f cpf = npf + dir_f; - f32 distance = (cpf - camera_position).getLength(); - core::aabbox3d box; - - // top - if(dir == v3s16(0,1,0)){ - box = f.selection_box.wall_top; - } - // bottom - else if(dir == v3s16(0,-1,0)){ - box = f.selection_box.wall_bottom; - } - // side - else{ - v3f vertices[2] = + hilightboxes.clear(); + for(std::vector::const_iterator + i2 = boxes.begin(); + i2 != boxes.end(); i2++) { - f.selection_box.wall_side.MinEdge, - f.selection_box.wall_side.MaxEdge - }; - - for(s32 i=0; i<2; i++) - { - if(dir == v3s16(-1,0,0)) - vertices[i].rotateXZBy(0); - if(dir == v3s16(1,0,0)) - vertices[i].rotateXZBy(180); - if(dir == v3s16(0,0,-1)) - vertices[i].rotateXZBy(90); - if(dir == v3s16(0,0,1)) - vertices[i].rotateXZBy(-90); - } - - box = core::aabbox3d(vertices[0]); - box.addInternalPoint(vertices[1]); - } - - box.MinEdge += npf; - box.MaxEdge += npf; - - if(distance < mindistance) - { - if(box.intersectsWithLine(shootline)) - { - result.type = POINTEDTHING_NODE; - result.node_undersurface = np; - result.node_abovesurface = np; - mindistance = distance; - hilightbox = box; - should_show_hilightbox = true; + aabb3f box = *i2; + box.MinEdge += npf + v3f(-d,-d,-d); + box.MaxEdge += npf + v3f(d,d,d); + hilightboxes.push_back(box); } } } - else // NODEBOX_REGULAR - { - for(u16 i=0; i<6; i++) - { - v3f dir_f = v3f(dirs[i].X, - dirs[i].Y, dirs[i].Z); - v3f centerpoint = npf + dir_f * BS/2; - f32 distance = - (centerpoint - camera_position).getLength(); - - if(distance < mindistance) - { - core::CMatrix4 m; - m.buildRotateFromTo(v3f(0,0,1), dir_f); - - // This is the back face - v3f corners[2] = { - v3f(BS/2, BS/2, BS/2), - v3f(-BS/2, -BS/2, BS/2+d) - }; - - for(u16 j=0; j<2; j++) - { - m.rotateVect(corners[j]); - corners[j] += npf; - } - - core::aabbox3d facebox(corners[0]); - facebox.addInternalPoint(corners[1]); - - if(facebox.intersectsWithLine(shootline)) - { - result.type = POINTEDTHING_NODE; - result.node_undersurface = np; - result.node_abovesurface = np + dirs[i]; - mindistance = distance; - - //hilightbox = facebox; - - const float d = 0.502; - core::aabbox3d nodebox - (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d); - v3f nodepos_f = intToFloat(np, BS); - nodebox.MinEdge += nodepos_f; - nodebox.MaxEdge += nodepos_f; - hilightbox = nodebox; - should_show_hilightbox = true; - } - } // if distance < mindistance - } // for dirs - } // regular block } // for coords return result; @@ -1112,9 +1000,6 @@ void the_game( else hotbar_imagesize = 64; - // Hilight boxes collected during the loop and displayed - core::list< core::aabbox3d > hilightboxes; - // Info text std::wstring infotext; @@ -1821,8 +1706,8 @@ void the_game( core::line3d shootline(camera_position, camera_position + camera_direction * BS * (d+1)); - core::aabbox3d hilightbox; - bool should_show_hilightbox = false; + // Hilight boxes collected during the loop and displayed + std::vector hilightboxes; ClientActiveObject *selected_object = NULL; PointedThing pointed = getPointedThing( @@ -1831,7 +1716,7 @@ void the_game( camera_position, shootline, d, playeritem_liquids_pointable, !ldown_for_dig, // output - hilightbox, should_show_hilightbox, + hilightboxes, selected_object); if(pointed != pointed_old) @@ -1840,12 +1725,6 @@ void the_game( //dstream<<"Pointing at "<::Iterator i=hilightboxes.begin(); - i != hilightboxes.end(); i++) - { + for(std::vector::const_iterator + i = hilightboxes.begin(); + i != hilightboxes.end(); i++) + { /*infostream<<"hilightbox min=" <<"("<MinEdge.X<<","<MinEdge.Y<<","<MinEdge.Z<<")" <<" max=" diff --git a/src/mapnode.cpp b/src/mapnode.cpp index 6cb9671b5..396009173 100644 --- a/src/mapnode.cpp +++ b/src/mapnode.cpp @@ -132,7 +132,98 @@ v3s16 MapNode::getWallMountedDir(INodeDefManager *nodemgr) const } } +static std::vector transformNodeBox(const MapNode &n, + const NodeBox &nodebox, INodeDefManager *nodemgr) +{ + std::vector boxes; + if(nodebox.type == NODEBOX_FIXED) + { + const std::vector &fixed = nodebox.fixed; + int facedir = n.getFaceDir(nodemgr); + for(std::vector::const_iterator + i = fixed.begin(); + i != fixed.end(); i++) + { + aabb3f box = *i; + if(facedir == 1) + { + box.MinEdge.rotateXZBy(-90); + box.MaxEdge.rotateXZBy(-90); + box.repair(); + } + else if(facedir == 2) + { + box.MinEdge.rotateXZBy(180); + box.MaxEdge.rotateXZBy(180); + box.repair(); + } + else if(facedir == 3) + { + box.MinEdge.rotateXZBy(90); + box.MaxEdge.rotateXZBy(90); + box.repair(); + } + boxes.push_back(box); + } + } + else if(nodebox.type == NODEBOX_WALLMOUNTED) + { + v3s16 dir = n.getWallMountedDir(nodemgr); + // top + if(dir == v3s16(0,1,0)) + { + boxes.push_back(nodebox.wall_top); + } + // bottom + else if(dir == v3s16(0,-1,0)) + { + boxes.push_back(nodebox.wall_bottom); + } + // side + else + { + v3f vertices[2] = + { + nodebox.wall_side.MinEdge, + nodebox.wall_side.MaxEdge + }; + + for(s32 i=0; i<2; i++) + { + if(dir == v3s16(-1,0,0)) + vertices[i].rotateXZBy(0); + if(dir == v3s16(1,0,0)) + vertices[i].rotateXZBy(180); + if(dir == v3s16(0,0,-1)) + vertices[i].rotateXZBy(90); + if(dir == v3s16(0,0,1)) + vertices[i].rotateXZBy(-90); + } + + aabb3f box = aabb3f(vertices[0]); + box.addInternalPoint(vertices[1]); + boxes.push_back(box); + } + } + else // NODEBOX_REGULAR + { + boxes.push_back(aabb3f(-BS/2,-BS/2,-BS/2,BS/2,BS/2,BS/2)); + } + return boxes; +} + +std::vector MapNode::getNodeBoxes(INodeDefManager *nodemgr) const +{ + const ContentFeatures &f = nodemgr->get(*this); + return transformNodeBox(*this, f.node_box, nodemgr); +} + +std::vector MapNode::getSelectionBoxes(INodeDefManager *nodemgr) const +{ + const ContentFeatures &f = nodemgr->get(*this); + return transformNodeBox(*this, f.selection_box, nodemgr); +} u32 MapNode::serializedLength(u8 version) { diff --git a/src/mapnode.h b/src/mapnode.h index 5e066604b..d14db0dc5 100644 --- a/src/mapnode.h +++ b/src/mapnode.h @@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irrlichttypes.h" #include "light.h" +#include class INodeDefManager; @@ -195,6 +196,17 @@ struct MapNode u8 getWallMounted(INodeDefManager *nodemgr) const; v3s16 getWallMountedDir(INodeDefManager *nodemgr) const; + /* + Gets list of node boxes (used for rendering (NDT_NODEBOX) + and collision) + */ + std::vector getNodeBoxes(INodeDefManager *nodemgr) const; + + /* + Gets list of selection boxes + */ + std::vector getSelectionBoxes(INodeDefManager *nodemgr) const; + /* Serialization functions */ diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 0c2793a0e..86be7c911 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -32,34 +32,74 @@ with this program; if not, write to the Free Software Foundation, Inc., NodeBox */ +void NodeBox::reset() +{ + type = NODEBOX_REGULAR; + // default is empty + fixed.clear(); + // default is sign/ladder-like + wall_top = aabb3f(-BS/2, BS/2-BS/16., -BS/2, BS/2, BS/2, BS/2); + wall_bottom = aabb3f(-BS/2, -BS/2, -BS/2, BS/2, -BS/2+BS/16., BS/2); + wall_side = aabb3f(-BS/2, -BS/2, -BS/2, -BS/2+BS/16., BS/2, BS/2); +} + void NodeBox::serialize(std::ostream &os) const { - writeU8(os, 0); // version + writeU8(os, 1); // version writeU8(os, type); - writeV3F1000(os, fixed.MinEdge); - writeV3F1000(os, fixed.MaxEdge); - writeV3F1000(os, wall_top.MinEdge); - writeV3F1000(os, wall_top.MaxEdge); - writeV3F1000(os, wall_bottom.MinEdge); - writeV3F1000(os, wall_bottom.MaxEdge); - writeV3F1000(os, wall_side.MinEdge); - writeV3F1000(os, wall_side.MaxEdge); + + if(type == NODEBOX_FIXED) + { + writeU16(os, fixed.size()); + for(std::vector::const_iterator + i = fixed.begin(); + i != fixed.end(); i++) + { + writeV3F1000(os, i->MinEdge); + writeV3F1000(os, i->MaxEdge); + } + } + else if(type == NODEBOX_WALLMOUNTED) + { + writeV3F1000(os, wall_top.MinEdge); + writeV3F1000(os, wall_top.MaxEdge); + writeV3F1000(os, wall_bottom.MinEdge); + writeV3F1000(os, wall_bottom.MaxEdge); + writeV3F1000(os, wall_side.MinEdge); + writeV3F1000(os, wall_side.MaxEdge); + } } void NodeBox::deSerialize(std::istream &is) { int version = readU8(is); - if(version != 0) + if(version != 1) throw SerializationError("unsupported NodeBox version"); + + reset(); + type = (enum NodeBoxType)readU8(is); - fixed.MinEdge = readV3F1000(is); - fixed.MaxEdge = readV3F1000(is); - wall_top.MinEdge = readV3F1000(is); - wall_top.MaxEdge = readV3F1000(is); - wall_bottom.MinEdge = readV3F1000(is); - wall_bottom.MaxEdge = readV3F1000(is); - wall_side.MinEdge = readV3F1000(is); - wall_side.MaxEdge = readV3F1000(is); + + if(type == NODEBOX_FIXED) + { + u16 fixed_count = readU16(is); + while(fixed_count--) + { + aabb3f box; + box.MinEdge = readV3F1000(is); + box.MaxEdge = readV3F1000(is); + fixed.push_back(box); + } + } + else if(type == NODEBOX_WALLMOUNTED) + { + wall_top.MinEdge = readV3F1000(is); + wall_top.MaxEdge = readV3F1000(is); + wall_bottom.MinEdge = readV3F1000(is); + wall_bottom.MaxEdge = readV3F1000(is); + wall_side.MinEdge = readV3F1000(is); + wall_side.MaxEdge = readV3F1000(is); + } } /* @@ -143,6 +183,7 @@ void ContentFeatures::reset() liquid_viscosity = 0; light_source = 0; damage_per_second = 0; + node_box = NodeBox(); selection_box = NodeBox(); material = MaterialProperties(); // Make unknown blocks diggable @@ -154,7 +195,7 @@ void ContentFeatures::reset() void ContentFeatures::serialize(std::ostream &os) { - writeU8(os, 1); // version + writeU8(os, 2); // version os<solidness = 0; break; } diff --git a/src/nodedef.h b/src/nodedef.h index 9524385cf..1645b3b06 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -62,7 +62,7 @@ enum LiquidType enum NodeBoxType { NODEBOX_REGULAR, // Regular block; allows buildable_to - NODEBOX_FIXED, // Static separately defined box + NODEBOX_FIXED, // Static separately defined box(es) NODEBOX_WALLMOUNTED, // Box for wall mounted nodes; (top, bottom, side) }; @@ -71,22 +71,16 @@ struct NodeBox enum NodeBoxType type; // NODEBOX_REGULAR (no parameters) // NODEBOX_FIXED - core::aabbox3d fixed; + std::vector fixed; // NODEBOX_WALLMOUNTED - core::aabbox3d wall_top; - core::aabbox3d wall_bottom; - core::aabbox3d wall_side; // being at the -X side + aabb3f wall_top; + aabb3f wall_bottom; + aabb3f wall_side; // being at the -X side - NodeBox(): - type(NODEBOX_REGULAR), - // default is rail-like - fixed(-BS/2, -BS/2, -BS/2, BS/2, -BS/2+BS/16., BS/2), - // default is sign/ladder-like - wall_top(-BS/2, BS/2-BS/16., -BS/2, BS/2, BS/2, BS/2), - wall_bottom(-BS/2, -BS/2, -BS/2, BS/2, -BS/2+BS/16., BS/2), - wall_side(-BS/2, -BS/2, -BS/2, -BS/2+BS/16., BS/2, BS/2) - {} + NodeBox() + { reset(); } + void reset(); void serialize(std::ostream &os) const; void deSerialize(std::istream &is); }; @@ -122,6 +116,7 @@ enum NodeDrawType NDT_PLANTLIKE, NDT_FENCELIKE, NDT_RAILLIKE, + NDT_NODEBOX, }; #define CF_SPECIAL_COUNT 2 @@ -193,6 +188,7 @@ struct ContentFeatures // Amount of light the node emits u8 light_source; u32 damage_per_second; + NodeBox node_box; NodeBox selection_box; MaterialProperties material; // Compatibility with old maps diff --git a/src/player.cpp b/src/player.cpp index 6506c43c3..47f4f015b 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -205,23 +205,14 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, INodeDefManager *nodemgr = m_gamedef->ndef(); v3f position = getPosition(); - v3f oldpos = position; - v3s16 oldpos_i = floatToInt(oldpos, BS); v3f old_speed = m_speed; - /*std::cout<<"oldpos_i=("<getBool("free_move"); if(free_move) { + position += m_speed * dtime; setPosition(position); return; } @@ -230,9 +221,6 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, Collision detection */ - // Player position in nodes - v3s16 pos_i = floatToInt(position, BS); - /* Check if player is in water (the oscillating value) */ @@ -282,30 +270,12 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, is_climbing = false; } - /* - Collision uncertainty radius - Make it a bit larger than the maximum distance of movement - */ - //f32 d = pos_max_d * 1.1; - // A fairly large value in here makes moving smoother - f32 d = 0.15*BS; - - // This should always apply, otherwise there are glitches - assert(d > pos_max_d); - float player_radius = BS*0.35; float player_height = BS*1.7; // Maximum distance over border for sneaking f32 sneak_max = BS*0.4; - /* - If sneaking, player has larger collision radius to keep from - falling - */ - /*if(control.sneak) - player_radius = sneak_max + d*1.1;*/ - /* If sneaking, keep in range from the last walked node and don't fall off from it @@ -321,23 +291,8 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, if(position.Y < min_y) { position.Y = min_y; - - //v3f old_speed = m_speed; - if(m_speed.Y < 0) m_speed.Y = 0; - - /*if(collision_info) - { - // Report fall collision - if(old_speed.Y < m_speed.Y - 0.1) - { - CollisionInfo info; - info.t = COLLISION_FALL; - info.speed = m_speed.Y - old_speed.Y; - collision_info->push_back(info); - } - }*/ } } @@ -345,176 +300,31 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, Calculate player collision box (new and old) */ core::aabbox3d playerbox( - position.X - player_radius, - position.Y - 0.0, - position.Z - player_radius, - position.X + player_radius, - position.Y + player_height, - position.Z + player_radius - ); - core::aabbox3d playerbox_old( - oldpos.X - player_radius, - oldpos.Y - 0.0, - oldpos.Z - player_radius, - oldpos.X + player_radius, - oldpos.Y + player_height, - oldpos.Z + player_radius + -player_radius, + 0.0, + -player_radius, + player_radius, + player_height, + player_radius ); + float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2); + + v3f accel_f = v3f(0,0,0); + + collisionMoveResult result = collisionMoveSimple(&map, m_gamedef, + pos_max_d, playerbox, player_stepheight, dtime, + position, m_speed, accel_f); + /* If the player's feet touch the topside of any node, this is set to true. Player is allowed to jump when this is true. */ - touching_ground = false; + touching_ground = result.touching_ground; - /*std::cout<<"Checking collisions for (" - < (" - <get(map.getNode(v3s16(x,y,z))).walkable == false) - continue; - } - catch(InvalidPositionException &e) - { - is_unloaded = true; - // Doing nothing here will block the player from - // walking over map borders - } - - core::aabbox3d nodebox = getNodeBox(v3s16(x,y,z), BS); - - /* - See if the player is touching ground. - - Player touches ground if player's minimum Y is near node's - maximum Y and player's X-Z-area overlaps with the node's - X-Z-area. - - Use 0.15*BS so that it is easier to get on a node. - */ - if( - //fabs(nodebox.MaxEdge.Y-playerbox.MinEdge.Y) < d - fabs(nodebox.MaxEdge.Y-playerbox.MinEdge.Y) < 0.15*BS - && nodebox.MaxEdge.X-d > playerbox.MinEdge.X - && nodebox.MinEdge.X+d < playerbox.MaxEdge.X - && nodebox.MaxEdge.Z-d > playerbox.MinEdge.Z - && nodebox.MinEdge.Z+d < playerbox.MaxEdge.Z - ){ - touching_ground = true; - if(is_unloaded) - standing_on_unloaded = true; - } - - // If player doesn't intersect with node, ignore node. - if(playerbox.intersectsWithBox(nodebox) == false) - continue; - - /* - Go through every axis - */ - v3f dirs[3] = { - v3f(0,0,1), // back-front - v3f(0,1,0), // top-bottom - v3f(1,0,0), // right-left - }; - for(u16 i=0; i<3; i++) - { - /* - Calculate values along the axis - */ - f32 nodemax = nodebox.MaxEdge.dotProduct(dirs[i]); - f32 nodemin = nodebox.MinEdge.dotProduct(dirs[i]); - f32 playermax = playerbox.MaxEdge.dotProduct(dirs[i]); - f32 playermin = playerbox.MinEdge.dotProduct(dirs[i]); - f32 playermax_old = playerbox_old.MaxEdge.dotProduct(dirs[i]); - f32 playermin_old = playerbox_old.MinEdge.dotProduct(dirs[i]); - - /* - Check collision for the axis. - Collision happens when player is going through a surface. - */ - /*f32 neg_d = d; - f32 pos_d = d; - // Make it easier to get on top of a node - if(i == 1) - neg_d = 0.15*BS; - bool negative_axis_collides = - (nodemax > playermin && nodemax <= playermin_old + neg_d - && m_speed.dotProduct(dirs[i]) < 0); - bool positive_axis_collides = - (nodemin < playermax && nodemin >= playermax_old - pos_d - && m_speed.dotProduct(dirs[i]) > 0);*/ - bool negative_axis_collides = - (nodemax > playermin && nodemax <= playermin_old + d - && m_speed.dotProduct(dirs[i]) < 0); - bool positive_axis_collides = - (nodemin < playermax && nodemin >= playermax_old - d - && m_speed.dotProduct(dirs[i]) > 0); - bool main_axis_collides = - negative_axis_collides || positive_axis_collides; - - /* - Check overlap of player and node in other axes - */ - bool other_axes_overlap = true; - for(u16 j=0; j<3; j++) - { - if(j == i) - continue; - f32 nodemax = nodebox.MaxEdge.dotProduct(dirs[j]); - f32 nodemin = nodebox.MinEdge.dotProduct(dirs[j]); - f32 playermax = playerbox.MaxEdge.dotProduct(dirs[j]); - f32 playermin = playerbox.MinEdge.dotProduct(dirs[j]); - if(!(nodemax - d > playermin && nodemin + d < playermax)) - { - other_axes_overlap = false; - break; - } - } - - /* - If this is a collision, revert the position in the main - direction. - */ - if(other_axes_overlap && main_axis_collides) - { - //v3f old_speed = m_speed; - - m_speed -= m_speed.dotProduct(dirs[i]) * dirs[i]; - position -= position.dotProduct(dirs[i]) * dirs[i]; - position += oldpos.dotProduct(dirs[i]) * dirs[i]; - - /*if(collision_info) - { - // Report fall collision - if(old_speed.Y < m_speed.Y - 0.1) - { - CollisionInfo info; - info.t = COLLISION_FALL; - info.speed = m_speed.Y - old_speed.Y; - collision_info->push_back(info); - } - }*/ - } - - } - } // xyz + bool standing_on_unloaded = result.standing_on_unloaded; /* Check the nodes under the player to see from which node the diff --git a/src/scriptapi.cpp b/src/scriptapi.cpp index a064cd688..eea93001f 100644 --- a/src/scriptapi.cpp +++ b/src/scriptapi.cpp @@ -380,6 +380,7 @@ struct EnumString es_DrawType[] = {NDT_PLANTLIKE, "plantlike"}, {NDT_FENCELIKE, "fencelike"}, {NDT_RAILLIKE, "raillike"}, + {NDT_NODEBOX, "nodebox"}, {0, NULL}, }; @@ -586,32 +587,93 @@ static video::SColor readARGB8(lua_State *L, int index) return color; } -static core::aabbox3d read_aabbox3df32(lua_State *L, int index, f32 scale) +static aabb3f read_aabb3f(lua_State *L, int index, f32 scale) { - core::aabbox3d box; - if(lua_istable(L, -1)){ - lua_rawgeti(L, -1, 1); + aabb3f box; + if(lua_istable(L, index)){ + lua_rawgeti(L, index, 1); box.MinEdge.X = lua_tonumber(L, -1) * scale; lua_pop(L, 1); - lua_rawgeti(L, -1, 2); + lua_rawgeti(L, index, 2); box.MinEdge.Y = lua_tonumber(L, -1) * scale; lua_pop(L, 1); - lua_rawgeti(L, -1, 3); + lua_rawgeti(L, index, 3); box.MinEdge.Z = lua_tonumber(L, -1) * scale; lua_pop(L, 1); - lua_rawgeti(L, -1, 4); + lua_rawgeti(L, index, 4); box.MaxEdge.X = lua_tonumber(L, -1) * scale; lua_pop(L, 1); - lua_rawgeti(L, -1, 5); + lua_rawgeti(L, index, 5); box.MaxEdge.Y = lua_tonumber(L, -1) * scale; lua_pop(L, 1); - lua_rawgeti(L, -1, 6); + lua_rawgeti(L, index, 6); box.MaxEdge.Z = lua_tonumber(L, -1) * scale; lua_pop(L, 1); } return box; } +static std::vector read_aabb3f_vector(lua_State *L, int index, f32 scale) +{ + std::vector boxes; + if(lua_istable(L, index)){ + int n = lua_objlen(L, index); + + // Check if it's a single box or a list of boxes + bool possibly_single_box = (n == 6); + for(int i = 1; i <= n && possibly_single_box; i++){ + lua_rawgeti(L, index, i); + if(!lua_isnumber(L, -1)) + possibly_single_box = false; + lua_pop(L, 1); + } + + if(possibly_single_box){ + // Read a single box + boxes.push_back(read_aabb3f(L, index, scale)); + } else { + // Read a list of boxes + for(int i = 1; i <= n; i++) + { + lua_rawgeti(L, index, i); + boxes.push_back(read_aabb3f(L, -1, scale)); + lua_pop(L, 1); + } + } + } + return boxes; +} + +static NodeBox read_nodebox(lua_State *L, int index) +{ + NodeBox nodebox; + if(lua_istable(L, -1)){ + nodebox.type = (NodeBoxType)getenumfield(L, index, "type", + es_NodeBoxType, NODEBOX_REGULAR); + + lua_getfield(L, index, "fixed"); + if(lua_istable(L, -1)) + nodebox.fixed = read_aabb3f_vector(L, -1, BS); + lua_pop(L, 1); + + lua_getfield(L, index, "wall_top"); + if(lua_istable(L, -1)) + nodebox.wall_top = read_aabb3f(L, -1, BS); + lua_pop(L, 1); + + lua_getfield(L, index, "wall_bottom"); + if(lua_istable(L, -1)) + nodebox.wall_bottom = read_aabb3f(L, -1, BS); + lua_pop(L, 1); + + lua_getfield(L, index, "wall_side"); + if(lua_istable(L, -1)) + nodebox.wall_side = read_aabb3f(L, -1, BS); + lua_pop(L, 1); + } + return nodebox; +} + /* MaterialProperties */ @@ -931,33 +993,16 @@ static ContentFeatures read_content_features(lua_State *L, int index) f.damage_per_second = getintfield_default(L, index, "damage_per_second", f.damage_per_second); - lua_getfield(L, index, "selection_box"); - if(lua_istable(L, -1)){ - f.selection_box.type = (NodeBoxType)getenumfield(L, -1, "type", - es_NodeBoxType, NODEBOX_REGULAR); - - lua_getfield(L, -1, "fixed"); - if(lua_istable(L, -1)) - f.selection_box.fixed = read_aabbox3df32(L, -1, BS); - lua_pop(L, 1); - - lua_getfield(L, -1, "wall_top"); - if(lua_istable(L, -1)) - f.selection_box.wall_top = read_aabbox3df32(L, -1, BS); - lua_pop(L, 1); - - lua_getfield(L, -1, "wall_bottom"); - if(lua_istable(L, -1)) - f.selection_box.wall_bottom = read_aabbox3df32(L, -1, BS); - lua_pop(L, 1); - - lua_getfield(L, -1, "wall_side"); - if(lua_istable(L, -1)) - f.selection_box.wall_side = read_aabbox3df32(L, -1, BS); - lua_pop(L, 1); - } + lua_getfield(L, index, "node_box"); + if(lua_istable(L, -1)) + f.node_box = read_nodebox(L, -1); lua_pop(L, 1); + lua_getfield(L, index, "selection_box"); + if(lua_istable(L, -1)) + f.selection_box = read_nodebox(L, -1); + lua_pop(L, 1); + lua_getfield(L, index, "material"); if(lua_istable(L, -1)){ f.material = read_material_properties(L, -1); @@ -4274,7 +4319,7 @@ void scriptapi_luaentity_get_properties(lua_State *L, u16 id, lua_getfield(L, -1, "collisionbox"); if(lua_istable(L, -1)) - prop->collisionbox = read_aabbox3df32(L, -1, 1.0); + prop->collisionbox = read_aabb3f(L, -1, 1.0); lua_pop(L, 1); getstringfield(L, -1, "visual", prop->visual); diff --git a/src/test.cpp b/src/test.cpp index 4226df544..8bfce386c 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "utility.h" #include "serialization.h" #include "voxel.h" +#include "collision.h" #include #include "porting.h" #include "content_mapnode.h" @@ -816,6 +817,153 @@ struct TestMapSector }; #endif +struct TestCollision +{ + void Run() + { + /* + axisAlignedCollision + */ + + for(s16 bx = -3; bx <= 3; bx++) + for(s16 by = -3; by <= 3; by++) + for(s16 bz = -3; bz <= 3; bz++) + { + // X- + { + aabb3f s(bx, by, bz, bx+1, by+1, bz+1); + aabb3f m(bx-2, by, bz, bx-1, by+1, bz+1); + v3f v(1, 0, 0); + f32 dtime = 0; + assert(axisAlignedCollision(s, m, v, 0, dtime) == 0); + assert(fabs(dtime - 1.000) < 0.001); + } + { + aabb3f s(bx, by, bz, bx+1, by+1, bz+1); + aabb3f m(bx-2, by, bz, bx-1, by+1, bz+1); + v3f v(-1, 0, 0); + f32 dtime = 0; + assert(axisAlignedCollision(s, m, v, 0, dtime) == -1); + } + { + aabb3f s(bx, by, bz, bx+1, by+1, bz+1); + aabb3f m(bx-2, by+1.5, bz, bx-1, by+2.5, bz-1); + v3f v(1, 0, 0); + f32 dtime; + assert(axisAlignedCollision(s, m, v, 0, dtime) == -1); + } + { + aabb3f s(bx, by, bz, bx+1, by+1, bz+1); + aabb3f m(bx-2, by-1.5, bz, bx-1.5, by+0.5, bz+1); + v3f v(0.5, 0.1, 0); + f32 dtime; + assert(axisAlignedCollision(s, m, v, 0, dtime) == 0); + assert(fabs(dtime - 3.000) < 0.001); + } + { + aabb3f s(bx, by, bz, bx+1, by+1, bz+1); + aabb3f m(bx-2, by-1.5, bz, bx-1.5, by+0.5, bz+1); + v3f v(0.5, 0.1, 0); + f32 dtime; + assert(axisAlignedCollision(s, m, v, 0, dtime) == 0); + assert(fabs(dtime - 3.000) < 0.001); + } + + // X+ + { + aabb3f s(bx, by, bz, bx+1, by+1, bz+1); + aabb3f m(bx+2, by, bz, bx+3, by+1, bz+1); + v3f v(-1, 0, 0); + f32 dtime; + assert(axisAlignedCollision(s, m, v, 0, dtime) == 0); + assert(fabs(dtime - 1.000) < 0.001); + } + { + aabb3f s(bx, by, bz, bx+1, by+1, bz+1); + aabb3f m(bx+2, by, bz, bx+3, by+1, bz+1); + v3f v(1, 0, 0); + f32 dtime; + assert(axisAlignedCollision(s, m, v, 0, dtime) == -1); + } + { + aabb3f s(bx, by, bz, bx+1, by+1, bz+1); + aabb3f m(bx+2, by, bz+1.5, bx+3, by+1, bz+3.5); + v3f v(-1, 0, 0); + f32 dtime; + assert(axisAlignedCollision(s, m, v, 0, dtime) == -1); + } + { + aabb3f s(bx, by, bz, bx+1, by+1, bz+1); + aabb3f m(bx+2, by-1.5, bz, bx+2.5, by-0.5, bz+1); + v3f v(-0.5, 0.2, 0); + f32 dtime; + assert(axisAlignedCollision(s, m, v, 0, dtime) == 1); // Y, not X! + assert(fabs(dtime - 2.500) < 0.001); + } + { + aabb3f s(bx, by, bz, bx+1, by+1, bz+1); + aabb3f m(bx+2, by-1.5, bz, bx+2.5, by-0.5, bz+1); + v3f v(-0.5, 0.3, 0); + f32 dtime; + assert(axisAlignedCollision(s, m, v, 0, dtime) == 0); + assert(fabs(dtime - 2.000) < 0.001); + } + + // TODO: Y-, Y+, Z-, Z+ + + // misc + { + aabb3f s(bx, by, bz, bx+2, by+2, bz+2); + aabb3f m(bx+2.3, by+2.29, bz+2.29, bx+4.2, by+4.2, bz+4.2); + v3f v(-1./3, -1./3, -1./3); + f32 dtime; + assert(axisAlignedCollision(s, m, v, 0, dtime) == 0); + assert(fabs(dtime - 0.9) < 0.001); + } + { + aabb3f s(bx, by, bz, bx+2, by+2, bz+2); + aabb3f m(bx+2.29, by+2.3, bz+2.29, bx+4.2, by+4.2, bz+4.2); + v3f v(-1./3, -1./3, -1./3); + f32 dtime; + assert(axisAlignedCollision(s, m, v, 0, dtime) == 1); + assert(fabs(dtime - 0.9) < 0.001); + } + { + aabb3f s(bx, by, bz, bx+2, by+2, bz+2); + aabb3f m(bx+2.29, by+2.29, bz+2.3, bx+4.2, by+4.2, bz+4.2); + v3f v(-1./3, -1./3, -1./3); + f32 dtime; + assert(axisAlignedCollision(s, m, v, 0, dtime) == 2); + assert(fabs(dtime - 0.9) < 0.001); + } + { + aabb3f s(bx, by, bz, bx+2, by+2, bz+2); + aabb3f m(bx-4.2, by-4.2, bz-4.2, bx-2.3, by-2.29, bz-2.29); + v3f v(1./7, 1./7, 1./7); + f32 dtime; + assert(axisAlignedCollision(s, m, v, 0, dtime) == 0); + assert(fabs(dtime - 16.1) < 0.001); + } + { + aabb3f s(bx, by, bz, bx+2, by+2, bz+2); + aabb3f m(bx-4.2, by-4.2, bz-4.2, bx-2.29, by-2.3, bz-2.29); + v3f v(1./7, 1./7, 1./7); + f32 dtime; + assert(axisAlignedCollision(s, m, v, 0, dtime) == 1); + assert(fabs(dtime - 16.1) < 0.001); + } + { + aabb3f s(bx, by, bz, bx+2, by+2, bz+2); + aabb3f m(bx-4.2, by-4.2, bz-4.2, bx-2.29, by-2.29, bz-2.3); + v3f v(1./7, 1./7, 1./7); + f32 dtime; + assert(axisAlignedCollision(s, m, v, 0, dtime) == 2); + assert(fabs(dtime - 16.1) < 0.001); + } + } + } +}; + struct TestSocket { void Run() @@ -1279,6 +1427,7 @@ void run_tests() TESTPARAMS(TestVoxelManipulator, ndef); //TEST(TestMapBlock); //TEST(TestMapSector); + TEST(TestCollision); if(INTERNET_SIMULATOR == false){ TEST(TestSocket); dout_con<<"=== BEGIN RUNNING UNIT TESTS FOR CONNECTION ==="< Date: Sat, 4 Feb 2012 02:20:05 +0200 Subject: [PATCH 2/6] Add groups = {} to item definition --- data/builtin.lua | 4 ++++ data/mods/default/init.lua | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/data/builtin.lua b/data/builtin.lua index 3a38b60ad..b8891c066 100644 --- a/data/builtin.lua +++ b/data/builtin.lua @@ -359,6 +359,7 @@ minetest.nodedef_default = { type="node", -- name intentionally not defined here description = "", + groups = {}, inventory_image = "", wield_image = "", wield_scale = {x=1,y=1,z=1}, @@ -418,6 +419,7 @@ minetest.craftitemdef_default = { type="craft", -- name intentionally not defined here description = "", + groups = {}, inventory_image = "", wield_image = "", wield_scale = {x=1,y=1,z=1}, @@ -435,6 +437,7 @@ minetest.tooldef_default = { type="tool", -- name intentionally not defined here description = "", + groups = {}, inventory_image = "", wield_image = "", wield_scale = {x=1,y=1,z=1}, @@ -452,6 +455,7 @@ minetest.noneitemdef_default = { -- This is used for the hand and unknown items type="none", -- name intentionally not defined here description = "", + groups = {}, inventory_image = "", wield_image = "", wield_scale = {x=1,y=1,z=1}, diff --git a/data/mods/default/init.lua b/data/mods/default/init.lua index f0e6b6dc2..d07337379 100644 --- a/data/mods/default/init.lua +++ b/data/mods/default/init.lua @@ -308,6 +308,12 @@ -- Item definition options (register_node, register_craftitem, register_tool) -- { -- description = "Steel Axe", +-- groups = {}, -- key=name, value=rating; rating=1..3. +-- if rating not applicable, use 1. +-- eg. {wool=1, fluffy=3} +-- {soil=2, outerspace=1, crumbly=1} +-- {hard=3, brittle=3, spikes=2 +-- {hard=1, metal=1, spikes=1} -- inventory_image = "default_tool_steelaxe.png", -- wield_image = "", -- wield_scale = {x=1,y=1,z=1}, From e15de8b70dc13e24aefca7fdd2a17be5f150ae49 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Sun, 5 Feb 2012 12:02:40 +0200 Subject: [PATCH 3/6] Modify light values to work a bit better with non-smooth lighting --- src/light.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/light.cpp b/src/light.cpp index 93e498620..89bddb1c4 100644 --- a/src/light.cpp +++ b/src/light.cpp @@ -21,7 +21,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #if 1 /* -Made using this and adding 230 as the second last one: +Made using this and: +- adding 220 as the second last one +- replacing the third last one (212) with 195 #!/usr/bin/python @@ -63,8 +65,8 @@ u8 light_decode_table[LIGHT_MAX+1] = 121, 146, 176, -212, -230, +195, +220, 255, }; #endif From 88cdd3a363668cfb2fd07b5381cce29738aed0bf Mon Sep 17 00:00:00 2001 From: Kahrl Date: Tue, 24 Jan 2012 00:00:26 +0100 Subject: [PATCH 4/6] Players stay in environment even when dead, damage flash and fall damage fixes Don't set m_removed on dead players (dead players are indicated by hp == 0). Local damage flash is shown whatever the cause was (even from Lua set_hp). PlayerCAO damage flash matches duration of local damage flash. Fall damage is dealt much more consistently (this is done by disallowing jumping when speed.Y is very negative, up to now jumping could sometimes negate fall damage) --- src/client.cpp | 10 +++++ src/content_cao.cpp | 35 ++++++++++++---- src/environment.cpp | 19 ++------- src/player.cpp | 9 +++-- src/server.cpp | 82 +++++++++++++++++++------------------- src/server.h | 2 +- src/serverremoteplayer.cpp | 16 +++++++- src/serverremoteplayer.h | 1 - 8 files changed, 104 insertions(+), 70 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 0463aa81c..bc303bc4b 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -1171,8 +1171,18 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) std::istringstream is(datastring, std::ios_base::binary); Player *player = m_env.getLocalPlayer(); assert(player != NULL); + u8 oldhp = player->hp; u8 hp = readU8(is); player->hp = hp; + + if(hp < oldhp) + { + // Add to ClientEvent queue + ClientEvent event; + event.type = CE_PLAYER_DAMAGE; + event.player_damage.amount = oldhp - hp; + m_client_event_queue.push_back(event); + } } else if(command == TOCLIENT_MOVE_PLAYER) { diff --git a/src/content_cao.cpp b/src/content_cao.cpp index a2708674b..3c30a0819 100644 --- a/src/content_cao.cpp +++ b/src/content_cao.cpp @@ -2067,6 +2067,7 @@ private: bool m_is_local_player; LocalPlayer *m_local_player; float m_damage_visual_timer; + bool m_dead; public: PlayerCAO(IGameDef *gamedef, ClientEnvironment *env): @@ -2078,7 +2079,8 @@ public: m_yaw(0), m_is_local_player(false), m_local_player(NULL), - m_damage_visual_timer(0) + m_damage_visual_timer(0), + m_dead(false) { if(gamedef == NULL) ClientActiveObject::registerType(getType(), create); @@ -2100,6 +2102,8 @@ public: m_position = readV3F1000(is); // yaw m_yaw = readF1000(is); + // dead + m_dead = readU8(is); pos_translator.init(m_position); @@ -2129,6 +2133,8 @@ public: { if(m_is_local_player) return NULL; + if(m_dead) + return NULL; return &m_selection_box; } v3f getPosition() @@ -2204,6 +2210,7 @@ public: m_text->setPosition(v3f(0, (f32)BS*2.1, 0)); updateTextures(""); + updateVisibility(); updateNodePos(); } @@ -2221,11 +2228,11 @@ public: if(m_node == NULL) return; - m_node->setVisible(true); - u8 li = decode_light(light_at_pos); video::SColor color(255,li,li,li); setMeshColor(m_node->getMesh(), color); + + updateVisibility(); } v3s16 getLightPosition() @@ -2233,6 +2240,14 @@ public: return floatToInt(m_position+v3f(0,BS*1.5,0), BS); } + void updateVisibility() + { + if(m_node == NULL) + return; + + m_node->setVisible(!m_dead); + } + void updateNodePos() { if(m_node == NULL) @@ -2248,6 +2263,7 @@ public: void step(float dtime, ClientEnvironment *env) { pos_translator.translate(dtime); + updateVisibility(); updateNodePos(); if(m_damage_visual_timer > 0){ @@ -2279,13 +2295,16 @@ public: { // damage s16 damage = readS16(is); - - if(m_is_local_player) - m_env->damageLocalPlayer(damage, false); - - m_damage_visual_timer = 0.5; + m_damage_visual_timer = 0.05; + if(damage >= 2) + m_damage_visual_timer += 0.05 * damage; updateTextures("^[brighten"); } + else if(cmd == 2) // died or respawned + { + m_dead = readU8(is); + updateVisibility(); + } } void updateTextures(const std::string &mod) diff --git a/src/environment.cpp b/src/environment.cpp index 7c2aef272..6f1d8ff55 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -797,6 +797,8 @@ void ServerEnvironment::clearAllObjects() i.atEnd()==false; i++) { ServerActiveObject* obj = i.getNode()->getValue(); + if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER) + continue; u16 id = i.getNode()->getKey(); v3f objectpos = obj->getBasePosition(); // Delete static object if block is loaded @@ -1983,16 +1985,7 @@ void ClientEnvironment::step(float dtime) { f32 damage_f = (info.speed - tolerance)/BS*factor; u16 damage = (u16)(damage_f+0.5); - if(lplayer->hp > damage) - lplayer->hp -= damage; - else - lplayer->hp = 0; - - ClientEnvEvent event; - event.type = CEE_PLAYER_DAMAGE; - event.player_damage.amount = damage; - event.player_damage.send_to_server = true; - m_client_event_queue.push_back(event); + damageLocalPlayer(damage, true); } } } @@ -2022,11 +2015,7 @@ void ClientEnvironment::step(float dtime) if(damage_per_second != 0) { - ClientEnvEvent event; - event.type = CEE_PLAYER_DAMAGE; - event.player_damage.amount = damage_per_second; - event.player_damage.send_to_server = true; - m_client_event_queue.push_back(event); + damageLocalPlayer(damage_per_second, true); } } diff --git a/src/player.cpp b/src/player.cpp index 6506c43c3..068b51790 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -713,14 +713,17 @@ void LocalPlayer::applyControl(float dtime) } else if(touching_ground) { - v3f speed = getSpeed(); /* NOTE: The d value in move() affects jump height by raising the height at which the jump speed is kept at its starting value */ - speed.Y = 6.5*BS; - setSpeed(speed); + v3f speed = getSpeed(); + if(speed.Y >= -0.5*BS) + { + speed.Y = 6.5*BS; + setSpeed(speed); + } } // Use the oscillating value for getting out of water // (so that the player doesn't fly on the surface) diff --git a/src/server.cpp b/src/server.cpp index a0c8a0092..bf90b2aa7 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1270,7 +1270,8 @@ void Server::AsyncRunStep() /* Handle player HPs (die if hp=0) */ - HandlePlayerHP(player, 0); + if(player->hp == 0 && player->m_hp_not_sent) + DiePlayer(player); /* Send player inventories and HPs if necessary @@ -1284,9 +1285,9 @@ void Server::AsyncRunStep() } /* - Add to environment if is not in respawn screen + Add to environment */ - if(!player->m_is_in_environment && !player->m_respawn_active){ + if(!player->m_is_in_environment){ player->m_removed = false; player->setId(0); m_env->addActiveObject(player); @@ -2129,6 +2130,10 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // Send HP SendPlayerHP(player); + // Show death screen if necessary + if(player->hp == 0) + SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0)); + // Send time of day { SharedBuffer data = makePacket_TOCLIENT_TIME_OF_DAY( @@ -2159,11 +2164,6 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER"); } - /* - Check HP, respawn if necessary - */ - HandlePlayerHP(player, 0); - /* Print out action */ @@ -2662,16 +2662,25 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) std::istringstream is(datastring, std::ios_base::binary); u8 damage = readU8(is); + ServerRemotePlayer *srp = static_cast(player); + if(g_settings->getBool("enable_damage")) { actionstream<getName()<<" damaged by " <<(int)damage<<" hp at "<getPosition()/BS) <setHP(srp->getHP() - damage); + + if(srp->getHP() == 0 && srp->m_hp_not_sent) + DiePlayer(srp); + + if(srp->m_hp_not_sent) + SendPlayerHP(player); } else { + // Force send (to correct the client's predicted HP) SendPlayerHP(player); } } @@ -2751,8 +2760,6 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) if(player->hp != 0) return; - srp->m_respawn_active = false; - RespawnPlayer(player); actionstream<getName()<<" respawns at " @@ -2811,6 +2818,13 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) infostream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="<getName() + <<" tried to interact, but is dead!"<m_last_good_position; // Update wielded item @@ -3968,26 +3982,14 @@ void Server::SendTexturesRequested(u16 peer_id,core::list tosend Something random */ -void Server::HandlePlayerHP(Player *player, s16 damage) +void Server::DiePlayer(Player *player) { ServerRemotePlayer *srp = static_cast(player); - if(srp->m_respawn_active) - return; - - if(player->hp > damage) - { - if(damage != 0){ - player->hp -= damage; - SendPlayerHP(player); - } - return; - } - - infostream<<"Server::HandlePlayerHP(): Player " + infostream<<"Server::DiePlayer(): Player " <getName()<<" dies"<hp = 0; + srp->setHP(0); // Trigger scripted stuff scriptapi_on_dieplayer(m_lua, srp); @@ -3999,24 +4001,13 @@ void Server::HandlePlayerHP(Player *player, s16 damage) } SendPlayerHP(player); - - RemoteClient *client = getClient(player->peer_id); - if(client->net_proto_version >= 3) - { - SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0)); - srp->m_removed = true; - srp->m_respawn_active = true; - } - else - { - RespawnPlayer(player); - } + SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0)); } void Server::RespawnPlayer(Player *player) { - player->hp = 20; ServerRemotePlayer *srp = static_cast(player); + srp->setHP(20); bool repositioned = scriptapi_on_respawnplayer(m_lua, srp); if(!repositioned){ v3f pos = findSpawnPos(m_env->getServerMap()); @@ -4268,6 +4259,14 @@ ServerRemotePlayer *Server::emergePlayer(const char *name, u16 peer_id) // Got one. player->peer_id = peer_id; + // Re-add player to environment + if(player->m_removed) + { + player->m_removed = false; + player->setId(0); + m_env->addActiveObject(player); + } + // Reset inventory to creative if in creative mode if(g_settings->getBool("creative_mode")) { @@ -4305,12 +4304,13 @@ ServerRemotePlayer *Server::emergePlayer(const char *name, u16 peer_id) v3f pos = findSpawnPos(m_env->getServerMap()); player = new ServerRemotePlayer(m_env, pos, peer_id, name); + ServerRemotePlayer *srp = static_cast(player); /* Add player to environment */ m_env->addPlayer(player); + m_env->addActiveObject(srp); /* Run scripts */ - ServerRemotePlayer *srp = static_cast(player); scriptapi_on_newplayer(m_lua, srp); /* Add stuff to inventory */ diff --git a/src/server.h b/src/server.h index 4fdb60065..40f0fe582 100644 --- a/src/server.h +++ b/src/server.h @@ -592,7 +592,7 @@ private: Something random */ - void HandlePlayerHP(Player *player, s16 damage); + void DiePlayer(Player *player); void RespawnPlayer(Player *player); void UpdateCrafting(u16 peer_id); diff --git a/src/serverremoteplayer.cpp b/src/serverremoteplayer.cpp index b4dbbdb1b..728ffe026 100644 --- a/src/serverremoteplayer.cpp +++ b/src/serverremoteplayer.cpp @@ -34,7 +34,6 @@ ServerRemotePlayer::ServerRemotePlayer(ServerEnvironment *env): m_wield_index(0), m_inventory_not_sent(false), m_hp_not_sent(false), - m_respawn_active(false), m_is_in_environment(false), m_time_from_last_punch(0), m_position_not_sent(false) @@ -159,6 +158,8 @@ std::string ServerRemotePlayer::getClientInitializationData() writeV3F1000(os, getPosition()); // yaw writeF1000(os, getYaw()); + // dead + writeU8(os, getHP() == 0); return os.str(); } @@ -247,6 +248,19 @@ void ServerRemotePlayer::setHP(s16 hp_) if(hp != oldhp) m_hp_not_sent = true; + + // On death or reincarnation send an active object message + if((hp == 0) != (oldhp == 0)) + { + std::ostringstream os(std::ios::binary); + // command (2 = update death state) + writeU8(os, 2); + // dead? + writeU8(os, hp == 0); + // create message and add to list + ActiveObjectMessage aom(getId(), false, os.str()); + m_messages_out.push_back(aom); + } } s16 ServerRemotePlayer::getHP() { diff --git a/src/serverremoteplayer.h b/src/serverremoteplayer.h index 9d9437646..94926c824 100644 --- a/src/serverremoteplayer.h +++ b/src/serverremoteplayer.h @@ -90,7 +90,6 @@ public: int m_wield_index; bool m_inventory_not_sent; bool m_hp_not_sent; - bool m_respawn_active; bool m_is_in_environment; // Incremented by step(), read and reset by Server float m_time_from_last_punch; From f34f75291b7a410d0132a05ab2f2ca5c0baa0131 Mon Sep 17 00:00:00 2001 From: sapier Date: Sun, 5 Feb 2012 12:47:10 +0100 Subject: [PATCH 5/6] disabled optimization for debug build to enable step by step debugging in gdb --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ee02d66f0..4fbc65798 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -281,7 +281,7 @@ else() endif() set(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG ${RELEASE_WARNING_FLAGS} ${WARNING_FLAGS} -O3 -ffast-math -Wall -fomit-frame-pointer -pipe -funroll-loops") - set(CMAKE_CXX_FLAGS_DEBUG "-g -O1 -Wall ${WARNING_FLAGS}") + set(CMAKE_CXX_FLAGS_DEBUG "-g -O0 -Wall ${WARNING_FLAGS}") if(USE_GPROF) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -pg") From ffe6025f610b03100af10b8d4b248494deb0c1fa Mon Sep 17 00:00:00 2001 From: sapier Date: Sun, 5 Feb 2012 17:23:08 +0100 Subject: [PATCH 6/6] Added object <-> object collision handling (player + luaentity support only) --- src/activeobject.h | 1 + src/collision.cpp | 35 +++++++++++++++++++++++++++++++---- src/collision.h | 7 +++---- src/content_cao.cpp | 31 +++++++++++++++++++++++++++++-- src/content_sao.cpp | 30 ++++++++++++++++++++---------- src/content_sao.h | 8 ++++++++ src/environment.cpp | 37 ++++++++++++++++++++++++++++++++++--- src/environment.h | 8 ++++++++ src/player.cpp | 9 +++++---- src/player.h | 7 ++++--- src/serverremoteplayer.cpp | 11 +++++++++++ src/serverremoteplayer.h | 5 +++-- 12 files changed, 157 insertions(+), 32 deletions(-) diff --git a/src/activeobject.h b/src/activeobject.h index c46ae61b2..7a5952f18 100644 --- a/src/activeobject.h +++ b/src/activeobject.h @@ -62,6 +62,7 @@ public: virtual u8 getType() const = 0; + virtual aabb3f* getCollisionBox() = 0; protected: u16 m_id; // 0 is invalid, "no id" }; diff --git a/src/collision.cpp b/src/collision.cpp index d131c7a20..78597ac68 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -22,8 +22,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "map.h" #include "nodedef.h" #include "gamedef.h" +#include "environment.h" #include "log.h" #include +#include // Helper function: // Checks for collision of a moving aabbox with a static aabbox @@ -183,11 +185,14 @@ bool wouldCollideWithCeiling( } -collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef, +collisionMoveResult collisionMoveSimple(Environment* env, f32 pos_max_d, const aabb3f &box_0, f32 stepheight, f32 dtime, v3f &pos_f, v3f &speed_f, v3f &accel_f) { + Map * map = &env->getMap(); + IGameDef * gamedef = env->getGameDef(); + TimeTaker tt("collisionMoveSimple"); collisionMoveResult result; @@ -252,6 +257,27 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef, assert(cboxes.size() == is_unloaded.size()); assert(cboxes.size() == is_step_up.size()); + { + TimeTaker tt3("collisionMoveSimple collect object boxes"); + + /* add object boxes to cboxes */ + core::list objects = env->getActiveObjects(); + + for (core::list::Iterator iter = objects.begin(); + iter != objects.end(); iter++) + { + aabb3f* object_collisionbox = (*iter)->getCollisionBox(); + //TODO do we need to check if it's really near enough? + if (object_collisionbox != NULL) + { + cboxes.push_back(*object_collisionbox); + is_unloaded.push_back(false); + is_step_up.push_back(false); + } + } + } //tt3 + + /* Collision detection */ @@ -277,7 +303,8 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef, loopcount++; if(loopcount >= 100) { - infostream<<"collisionMoveSimple: WARNING: Loop count exceeded, aborting to avoid infiniite loop"< -class Map; -class IGameDef; +class Environment; struct collisionMoveResult { @@ -42,13 +41,13 @@ struct collisionMoveResult }; // Moves using a single iteration; speed should not exceed pos_max_d/dtime -collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef, +collisionMoveResult collisionMoveSimple(Environment* env, f32 pos_max_d, const aabb3f &box_0, f32 stepheight, f32 dtime, v3f &pos_f, v3f &speed_f, v3f &accel_f); // Moves using as many iterations as needed -collisionMoveResult collisionMovePrecise(Map *map, IGameDef *gamedef, +collisionMoveResult collisionMovePrecise(Environment* env, f32 pos_max_d, const aabb3f &box_0, f32 stepheight, f32 dtime, v3f &pos_f, v3f &speed_f, v3f &accel_f); diff --git a/src/content_cao.cpp b/src/content_cao.cpp index 3e330028a..b9afd8199 100644 --- a/src/content_cao.cpp +++ b/src/content_cao.cpp @@ -1701,6 +1701,7 @@ private: int m_anim_num_frames; float m_anim_framelength; float m_anim_timer; + aabb3f m_collisionbox; public: LuaEntityCAO(IGameDef *gamedef, ClientEnvironment *env): @@ -1879,8 +1880,7 @@ public: v3f p_pos = m_position; v3f p_velocity = m_velocity; v3f p_acceleration = m_acceleration; - IGameDef *gamedef = env->getGameDef(); - moveresult = collisionMovePrecise(&env->getMap(), gamedef, + moveresult = collisionMovePrecise(env, pos_max_d, box, stepheight, dtime, p_pos, p_velocity, p_acceleration); // Apply results @@ -2047,6 +2047,21 @@ public: updateTexturePos(); } } + + aabb3f* getCollisionBox() { + if (m_prop->physical) { + //update collision box + m_collisionbox.MinEdge = m_prop->collisionbox.MinEdge * BS; + m_collisionbox.MaxEdge = m_prop->collisionbox.MaxEdge * BS; + + m_collisionbox.MinEdge += m_position; + m_collisionbox.MaxEdge += m_position; + + return &m_collisionbox; + } + + return NULL; + } }; // Prototype @@ -2070,6 +2085,7 @@ private: LocalPlayer *m_local_player; float m_damage_visual_timer; bool m_dead; + aabb3f m_collisionbox; public: PlayerCAO(IGameDef *gamedef, ClientEnvironment *env): @@ -2116,6 +2132,17 @@ public: } } + aabb3f* getCollisionBox() { + //update collision box + m_collisionbox.MinEdge = m_selection_box.MinEdge; + m_collisionbox.MaxEdge = m_selection_box.MaxEdge; + + m_collisionbox.MinEdge += m_position; + m_collisionbox.MaxEdge += m_position; + + return &m_collisionbox; + } + ~PlayerCAO() { if(m_node) diff --git a/src/content_sao.cpp b/src/content_sao.cpp index cb6be2f2c..e8290a8db 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -165,8 +165,7 @@ void ItemSAO::step(float dtime, bool send_recommended) v3f pos_f_old = pos_f; v3f accel_f = v3f(0,0,0); f32 stepheight = 0; - IGameDef *gamedef = m_env->getGameDef(); - moveresult = collisionMoveSimple(&m_env->getMap(), gamedef, + moveresult = collisionMoveSimple(m_env, pos_max_d, box, stepheight, dtime, pos_f, m_speed_f, accel_f); @@ -409,8 +408,7 @@ void RatSAO::step(float dtime, bool send_recommended) v3f pos_f_old = pos_f; v3f accel_f = v3f(0,0,0); f32 stepheight = 0; - IGameDef *gamedef = m_env->getGameDef(); - moveresult = collisionMoveSimple(&m_env->getMap(), gamedef, + moveresult = collisionMoveSimple(m_env, pos_max_d, box, stepheight, dtime, pos_f, m_speed_f, accel_f); m_touching_ground = moveresult.touching_ground; @@ -660,8 +658,7 @@ void Oerkki1SAO::step(float dtime, bool send_recommended) v3f pos_f_old = pos_f; v3f accel_f = v3f(0,0,0); f32 stepheight = 0; - IGameDef *gamedef = m_env->getGameDef(); - moveresult = collisionMovePrecise(&m_env->getMap(), gamedef, + moveresult = collisionMovePrecise(m_env, pos_max_d, box, stepheight, dtime, pos_f, m_speed_f, accel_f); m_touching_ground = moveresult.touching_ground; @@ -910,8 +907,7 @@ void FireflySAO::step(float dtime, bool send_recommended) v3f pos_f_old = pos_f; v3f accel_f = v3f(0,0,0); f32 stepheight = 0; - IGameDef *gamedef = m_env->getGameDef(); - moveresult = collisionMoveSimple(&m_env->getMap(), gamedef, + moveresult = collisionMoveSimple(m_env, pos_max_d, box, stepheight, dtime, pos_f, m_speed_f, accel_f); m_touching_ground = moveresult.touching_ground; @@ -1634,8 +1630,7 @@ void LuaEntitySAO::step(float dtime, bool send_recommended) v3f p_pos = m_base_position; v3f p_velocity = m_velocity; v3f p_acceleration = m_acceleration; - IGameDef *gamedef = m_env->getGameDef(); - moveresult = collisionMovePrecise(&m_env->getMap(), gamedef, + moveresult = collisionMovePrecise(m_env, pos_max_d, box, stepheight, dtime, p_pos, p_velocity, p_acceleration); // Apply results @@ -1844,3 +1839,18 @@ void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end) m_messages_out.push_back(aom); } +aabb3f* LuaEntitySAO::getCollisionBox() { + if (m_prop->physical) { + //update collision box + m_collisionbox.MinEdge = m_prop->collisionbox.MinEdge * BS; + m_collisionbox.MaxEdge = m_prop->collisionbox.MaxEdge * BS; + + m_collisionbox.MinEdge += m_base_position; + m_collisionbox.MaxEdge += m_base_position; + + return &m_collisionbox; + } + + return NULL; +} + diff --git a/src/content_sao.h b/src/content_sao.h index f0c9cea90..6ff2a04fe 100644 --- a/src/content_sao.h +++ b/src/content_sao.h @@ -32,6 +32,7 @@ public: static ServerActiveObject* create(ServerEnvironment *env, v3f pos, const std::string &data); void step(float dtime, bool send_recommended); + inline aabb3f* getCollisionBox() { return NULL; } private: float m_timer1; float m_age; @@ -51,6 +52,7 @@ public: ItemStack createItemStack(); void punch(ServerActiveObject *puncher, float time_from_last_punch); float getMinimumSavedMovement(){ return 0.1*BS; } + inline aabb3f* getCollisionBox() { return NULL; } private: std::string m_itemstring; bool m_itemstring_changed; @@ -71,6 +73,7 @@ public: std::string getClientInitializationData(); std::string getStaticData(); void punch(ServerActiveObject *puncher, float time_from_last_punch); + inline aabb3f* getCollisionBox() { return NULL; } private: bool m_is_active; IntervalLimiter m_inactive_interval; @@ -97,6 +100,7 @@ public: std::string getStaticData(); void punch(ServerActiveObject *puncher, float time_from_last_punch); bool isPeaceful(){return false;} + inline aabb3f* getCollisionBox() { return NULL; } private: void doDamage(u16 d); @@ -125,6 +129,7 @@ public: void step(float dtime, bool send_recommended); std::string getClientInitializationData(); std::string getStaticData(); + inline aabb3f* getCollisionBox() { return NULL; } private: bool m_is_active; IntervalLimiter m_inactive_interval; @@ -155,6 +160,7 @@ public: void step(float dtime, bool send_recommended); void punch(ServerActiveObject *puncher, float time_from_last_punch); bool isPeaceful(); + inline aabb3f* getCollisionBox() { return NULL; } private: void sendPosition(); void setPropertyDefaults(); @@ -222,6 +228,7 @@ public: void setSprite(v2s16 p, int num_frames, float framelength, bool select_horiz_by_yawpitch); std::string getName(); + aabb3f* getCollisionBox(); private: void sendPosition(bool do_interpolate, bool is_movement_end); @@ -238,6 +245,7 @@ private: v3f m_last_sent_velocity; float m_last_sent_position_timer; float m_last_sent_move_precision; + aabb3f m_collisionbox; }; #endif diff --git a/src/environment.cpp b/src/environment.cpp index 6f1d8ff55..f74299d6c 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -920,7 +920,7 @@ void ServerEnvironment::step(float dtime) v3f playerpos = player->getPosition(); // Move - player->move(dtime, *m_map, 100*BS); + player->move(dtime, this, 100*BS); /* Add footsteps to grass @@ -1803,6 +1803,22 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) } } +//get a list of all active objects +core::list ServerEnvironment::getActiveObjects() { + core::list retval; + + if (m_active_objects.size() > 0 ) + { + for (core::map::Iterator iter=m_active_objects.getIterator(); + iter.atEnd()==false; iter++) { + + retval.push_back(iter.getNode()->getValue()); + } + } + + return retval; +} + #ifndef SERVER @@ -1963,7 +1979,7 @@ void ClientEnvironment::step(float dtime) Move the lplayer. This also does collision detection. */ - lplayer->move(dtime_part, *m_map, position_max_increment, + lplayer->move(dtime_part, this, position_max_increment, &player_collisions); } } @@ -2034,7 +2050,7 @@ void ClientEnvironment::step(float dtime) if(player->isLocal() == false) { // Move - player->move(dtime, *m_map, 100*BS); + player->move(dtime, this, 100*BS); } @@ -2311,6 +2327,21 @@ ClientEnvEvent ClientEnvironment::getClientEvent() return m_client_event_queue.pop_front(); } +//get a list of all active objects +core::list ClientEnvironment::getActiveObjects() { + core::list retval; + + if (m_active_objects.size() > 0 ) + { + for (core::map::Iterator iter=m_active_objects.getIterator(); + iter.atEnd()==false; iter++) + { + retval.push_back(iter.getNode()->getValue()); + } + } + return retval; +} + #endif // #ifndef SERVER diff --git a/src/environment.h b/src/environment.h index 89c9fd676..e8338e37b 100644 --- a/src/environment.h +++ b/src/environment.h @@ -87,6 +87,9 @@ public: return m_time_of_day; } + virtual IGameDef *getGameDef() = 0; + virtual core::list getActiveObjects() = 0; + protected: // peer_ids in here should be unique, except that there may be many 0s core::list m_players; @@ -280,6 +283,8 @@ public: // This makes stuff happen void step(f32 dtime); + //get a list of all active objects + core::list getActiveObjects(); private: /* @@ -462,6 +467,9 @@ public: // Get event from queue. CEE_NONE is returned if queue is empty. ClientEnvEvent getClientEvent(); + + //get a list of all active objects + core::list getActiveObjects(); private: ClientMap *m_map; scene::ISceneManager *m_smgr; diff --git a/src/player.cpp b/src/player.cpp index 1aedf4a63..61cc2de7e 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -199,9 +199,10 @@ LocalPlayer::~LocalPlayer() { } -void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, +void LocalPlayer::move(f32 dtime, Environment* env, f32 pos_max_d, core::list *collision_info) { + Map& map = env->getMap(); INodeDefManager *nodemgr = m_gamedef->ndef(); v3f position = getPosition(); @@ -312,7 +313,7 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, v3f accel_f = v3f(0,0,0); - collisionMoveResult result = collisionMoveSimple(&map, m_gamedef, + collisionMoveResult result = collisionMoveSimple(env, pos_max_d, playerbox, player_stepheight, dtime, position, m_speed, accel_f); @@ -420,9 +421,9 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, } } -void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d) +void LocalPlayer::move(f32 dtime,Environment* env, f32 pos_max_d) { - move(dtime, map, pos_max_d, NULL); + move(dtime, env, pos_max_d, NULL); } void LocalPlayer::applyControl(float dtime) diff --git a/src/player.h b/src/player.h index d62fb6111..69db8ac51 100644 --- a/src/player.h +++ b/src/player.h @@ -31,6 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc., class Map; class IGameDef; struct CollisionInfo; +class Environment; class Player { @@ -42,7 +43,7 @@ public: void resetInventory(); //void move(f32 dtime, Map &map); - virtual void move(f32 dtime, Map &map, f32 pos_max_d) = 0; + virtual void move(f32 dtime, Environment* env, f32 pos_max_d) = 0; v3f getSpeed() { @@ -231,9 +232,9 @@ public: return true; } - void move(f32 dtime, Map &map, f32 pos_max_d, + void move(f32 dtime, Environment* env, f32 pos_max_d, core::list *collision_info); - void move(f32 dtime, Map &map, f32 pos_max_d); + void move(f32 dtime, Environment* env, f32 pos_max_d); void applyControl(float dtime); diff --git a/src/serverremoteplayer.cpp b/src/serverremoteplayer.cpp index 728ffe026..6663d22f8 100644 --- a/src/serverremoteplayer.cpp +++ b/src/serverremoteplayer.cpp @@ -262,9 +262,20 @@ void ServerRemotePlayer::setHP(s16 hp_) m_messages_out.push_back(aom); } } + s16 ServerRemotePlayer::getHP() { return hp; } +aabb3f* ServerRemotePlayer::getCollisionBox() { + m_collisionbox.MinEdge = aabb3f(-BS/3.,0.0,-BS/3., BS/3.,BS*2.0,BS/3.).MinEdge; + m_collisionbox.MaxEdge = aabb3f(-BS/3.,0.0,-BS/3., BS/3.,BS*2.0,BS/3.).MaxEdge; + + m_collisionbox.MinEdge += m_position; + m_collisionbox.MaxEdge += m_position; + + return &m_collisionbox; +} + diff --git a/src/serverremoteplayer.h b/src/serverremoteplayer.h index 94926c824..6de36eebc 100644 --- a/src/serverremoteplayer.h +++ b/src/serverremoteplayer.h @@ -40,7 +40,7 @@ public: virtual bool isLocal() const { return false; } - virtual void move(f32 dtime, Map &map, f32 pos_max_d) + virtual void move(f32 dtime, Environment* env, f32 pos_max_d) { } @@ -93,9 +93,10 @@ public: bool m_is_in_environment; // Incremented by step(), read and reset by Server float m_time_from_last_punch; - + aabb3f* getCollisionBox(); private: bool m_position_not_sent; + aabb3f m_collisionbox; }; #endif