1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-08-01 17:38:41 +00:00

Merge branch 'object_object_collision' into sapier_experimental

This commit is contained in:
sapier 2012-02-05 20:35:54 +01:00
commit 413afa77d0
30 changed files with 1365 additions and 719 deletions

View file

@ -359,6 +359,7 @@ minetest.nodedef_default = {
type="node", type="node",
-- name intentionally not defined here -- name intentionally not defined here
description = "", description = "",
groups = {},
inventory_image = "", inventory_image = "",
wield_image = "", wield_image = "",
wield_scale = {x=1,y=1,z=1}, wield_scale = {x=1,y=1,z=1},
@ -418,6 +419,7 @@ minetest.craftitemdef_default = {
type="craft", type="craft",
-- name intentionally not defined here -- name intentionally not defined here
description = "", description = "",
groups = {},
inventory_image = "", inventory_image = "",
wield_image = "", wield_image = "",
wield_scale = {x=1,y=1,z=1}, wield_scale = {x=1,y=1,z=1},
@ -435,6 +437,7 @@ minetest.tooldef_default = {
type="tool", type="tool",
-- name intentionally not defined here -- name intentionally not defined here
description = "", description = "",
groups = {},
inventory_image = "", inventory_image = "",
wield_image = "", wield_image = "",
wield_scale = {x=1,y=1,z=1}, 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", type="none",
-- name intentionally not defined here -- name intentionally not defined here
description = "", description = "",
groups = {},
inventory_image = "", inventory_image = "",
wield_image = "", wield_image = "",
wield_scale = {x=1,y=1,z=1}, wield_scale = {x=1,y=1,z=1},

View file

@ -308,6 +308,12 @@
-- Item definition options (register_node, register_craftitem, register_tool) -- Item definition options (register_node, register_craftitem, register_tool)
-- { -- {
-- description = "Steel Axe", -- 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", -- inventory_image = "default_tool_steelaxe.png",
-- wield_image = "", -- wield_image = "",
-- wield_scale = {x=1,y=1,z=1}, -- wield_scale = {x=1,y=1,z=1},

View file

@ -0,0 +1 @@
default

93
data/mods/stairs/init.lua Normal file
View file

@ -0,0 +1,93 @@
stairs = {}
-- Node will be called stairs:stair_<subname>
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_<subname>
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}_<subname>
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")

View file

@ -283,7 +283,7 @@ else()
endif() 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_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) if(USE_GPROF)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -pg") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -pg")

View file

@ -62,6 +62,7 @@ public:
virtual u8 getType() const = 0; virtual u8 getType() const = 0;
virtual aabb3f* getCollisionBox() = 0;
protected: protected:
u16 m_id; // 0 is invalid, "no id" u16 m_id; // 0 is invalid, "no id"
}; };

View file

@ -1171,8 +1171,18 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
std::istringstream is(datastring, std::ios_base::binary); std::istringstream is(datastring, std::ios_base::binary);
Player *player = m_env.getLocalPlayer(); Player *player = m_env.getLocalPlayer();
assert(player != NULL); assert(player != NULL);
u8 oldhp = player->hp;
u8 hp = readU8(is); u8 hp = readU8(is);
player->hp = hp; 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) else if(command == TOCLIENT_MOVE_PLAYER)
{ {

View file

@ -22,28 +22,266 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "map.h" #include "map.h"
#include "nodedef.h" #include "nodedef.h"
#include "gamedef.h" #include "gamedef.h"
#include "environment.h"
#include "log.h"
#include <vector>
#include <list>
collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef, // Helper function:
f32 pos_max_d, const core::aabbox3d<f32> &box_0, // Checks for collision of a moving aabbox with a static aabbox
f32 dtime, v3f &pos_f, v3f &speed_f) // 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<aabb3f> &staticboxes,
const aabb3f &movingbox,
f32 y_increase, f32 d)
{
//TimeTaker tt("wouldCollideWithCeiling");
assert(y_increase >= 0);
for(std::vector<aabb3f>::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(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; 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<aabb3f> cboxes;
std::vector<bool> is_unloaded;
std::vector<bool> 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<aabb3f> nodeboxes = n.getNodeBoxes(gamedef->ndef());
for(std::vector<aabb3f>::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());
{
TimeTaker tt3("collisionMoveSimple collect object boxes");
/* add object boxes to cboxes */
core::list<ActiveObject*> objects = env->getActiveObjects();
for (core::list<ActiveObject*>::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 Collision detection
*/ */
// position in nodes
v3s16 pos_i = floatToInt(pos_f, BS);
/* /*
Collision uncertainty radius Collision uncertainty radius
Make it a bit larger than the maximum distance of movement Make it a bit larger than the maximum distance of movement
@ -55,47 +293,127 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef,
// This should always apply, otherwise there are glitches // This should always apply, otherwise there are glitches
assert(d > pos_max_d); assert(d > pos_max_d);
/* int loopcount = 0;
Calculate collision box
*/
core::aabbox3d<f32> box = box_0;
box.MaxEdge += pos_f;
box.MinEdge += pos_f;
core::aabbox3d<f32> oldbox = box_0;
oldbox.MaxEdge += oldpos_f;
oldbox.MinEdge += oldpos_f;
/* while(dtime > BS*1e-10)
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++)
{ {
try{ TimeTaker tt3("collisionMoveSimple dtime loop");
// Object collides into walkable nodes
MapNode n = map->getNode(v3s16(x,y,z)); // Avoid infinite loop
if(gamedef->getNodeDefManager()->get(n).walkable == false) loopcount++;
if(loopcount >= 100)
{
infostream<<"collisionMoveSimple: WARNING: Loop count exceeded, "
"aborting to avoid infiniite loop"<<std::endl;
dtime = 0;
break;
}
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; continue;
}
catch(InvalidPositionException &e) // Find nearest collision of the two boxes (raytracing-like)
{ f32 dtime_tmp;
// Doing nothing here will block the object from int collided = axisAlignedCollision(
// walking over map borders 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;
} }
core::aabbox3d<f32> nodebox = getNodeBox(v3s16(x,y,z), BS); 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. See if the object is touching ground.
@ -107,104 +425,40 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef,
Use 0.15*BS so that it is easier to get on a node. Use 0.15*BS so that it is easier to get on a node.
*/ */
if( if(
//fabs(nodebox.MaxEdge.Y-box.MinEdge.Y) < d cbox.MaxEdge.X-d > box.MinEdge.X &&
fabs(nodebox.MaxEdge.Y-box.MinEdge.Y) < 0.15*BS cbox.MinEdge.X+d < box.MaxEdge.X &&
&& nodebox.MaxEdge.X-d > box.MinEdge.X cbox.MaxEdge.Z-d > box.MinEdge.Z &&
&& nodebox.MinEdge.X+d < box.MaxEdge.X cbox.MinEdge.Z+d < box.MaxEdge.Z
&& nodebox.MaxEdge.Z-d > box.MinEdge.Z
&& nodebox.MinEdge.Z+d < box.MaxEdge.Z
){ ){
if(is_step_up[boxindex])
{
pos_f.Y += (cbox.MaxEdge.Y - box.MinEdge.Y);
box = box_0;
box.MinEdge += pos_f;
box.MaxEdge += pos_f;
}
if(fabs(cbox.MaxEdge.Y-box.MinEdge.Y) < 0.15*BS)
{
result.touching_ground = true; result.touching_ground = true;
} if(is_unloaded[boxindex])
result.standing_on_unloaded = 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(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;
} }
} }
/*
If this is a collision, revert the pos_f in the main
direction.
*/
if(other_axes_overlap && main_axis_collides)
{
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;
} }
}
} // xyz
return result; return result;
} }
collisionMoveResult collisionMovePrecise(Map *map, IGameDef *gamedef, collisionMoveResult collisionMovePrecise(Environment* env,
f32 pos_max_d, const core::aabbox3d<f32> &box_0, f32 pos_max_d, const aabb3f &box_0,
f32 dtime, v3f &pos_f, v3f &speed_f) f32 stepheight, f32 dtime,
v3f &pos_f, v3f &speed_f, v3f &accel_f)
{ {
TimeTaker tt("collisionMovePrecise");
infostream<<"start collisionMovePrecise\n";
collisionMoveResult final_result; 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 // Don't allow overly huge dtime
if(dtime > 2.0) if(dtime > 2.0)
dtime = 2.0; dtime = 2.0;
@ -216,6 +470,16 @@ collisionMoveResult collisionMovePrecise(Map *map, IGameDef *gamedef,
{ {
loopcount++; 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; f32 dtime_part;
if(dtime_downcount > dtime_max_increment) if(dtime_downcount > dtime_max_increment)
{ {
@ -233,18 +497,22 @@ collisionMoveResult collisionMovePrecise(Map *map, IGameDef *gamedef,
dtime_downcount = 0; dtime_downcount = 0;
} }
collisionMoveResult result = collisionMoveSimple(map, gamedef, collisionMoveResult result = collisionMoveSimple(env,
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) if(result.touching_ground)
final_result.touching_ground = true; final_result.touching_ground = true;
if(result.collides) if(result.collides)
final_result.collides = true; 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); while(dtime_downcount > 0.001);
infostream<<"end collisionMovePrecise\n";
return final_result; return final_result;
} }

View file

@ -21,30 +21,52 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define COLLISION_HEADER #define COLLISION_HEADER
#include "common_irrlicht.h" #include "common_irrlicht.h"
#include <vector>
class Map; class Environment;
class IGameDef;
struct collisionMoveResult struct collisionMoveResult
{ {
bool touching_ground; bool touching_ground;
bool collides; bool collides;
bool collides_xz;
bool standing_on_unloaded;
collisionMoveResult(): collisionMoveResult():
touching_ground(false), 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 // 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 core::aabbox3d<f32> &box_0, f32 pos_max_d, const aabb3f &box_0,
f32 dtime, v3f &pos_f, v3f &speed_f); f32 stepheight, f32 dtime,
v3f &pos_f, v3f &speed_f, v3f &accel_f);
// Moves using as many iterations as needed // Moves using as many iterations as needed
collisionMoveResult collisionMovePrecise(Map *map, IGameDef *gamedef, collisionMoveResult collisionMovePrecise(Environment* env,
f32 pos_max_d, const core::aabbox3d<f32> &box_0, f32 pos_max_d, const aabb3f &box_0,
f32 dtime, v3f &pos_f, v3f &speed_f); 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<aabb3f> &staticboxes,
const aabb3f &movingbox,
f32 y_increase, f32 d);
enum CollisionType enum CollisionType
{ {

View file

@ -1701,6 +1701,7 @@ private:
int m_anim_num_frames; int m_anim_num_frames;
float m_anim_framelength; float m_anim_framelength;
float m_anim_timer; float m_anim_timer;
aabb3f m_collisionbox;
public: public:
LuaEntityCAO(IGameDef *gamedef, ClientEnvironment *env): LuaEntityCAO(IGameDef *gamedef, ClientEnvironment *env):
@ -1901,22 +1902,23 @@ public:
box.MinEdge *= BS; box.MinEdge *= BS;
box.MaxEdge *= BS; box.MaxEdge *= BS;
collisionMoveResult moveresult; 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_pos = m_position;
v3f p_velocity = m_velocity; v3f p_velocity = m_velocity;
IGameDef *gamedef = env->getGameDef(); v3f p_acceleration = m_acceleration;
moveresult = collisionMovePrecise(&env->getMap(), gamedef, moveresult = collisionMovePrecise(env,
pos_max_d, box, dtime, p_pos, p_velocity); pos_max_d, box, stepheight, dtime,
p_pos, p_velocity, p_acceleration);
// Apply results // Apply results
m_position = p_pos; m_position = p_pos;
m_velocity = p_velocity; m_velocity = p_velocity;
m_acceleration = p_acceleration;
bool is_end_position = moveresult.collides; bool is_end_position = moveresult.collides;
pos_translator.update(m_position, is_end_position, dtime); pos_translator.update(m_position, is_end_position, dtime);
pos_translator.translate(dtime); pos_translator.translate(dtime);
updateNodePos(); updateNodePos();
m_velocity += dtime * m_acceleration;
} else { } else {
m_position += dtime * m_velocity + 0.5 * dtime * dtime * m_acceleration; m_position += dtime * m_velocity + 0.5 * dtime * dtime * m_acceleration;
m_velocity += dtime * m_acceleration; m_velocity += dtime * m_acceleration;
@ -2097,6 +2099,21 @@ public:
updateTexturePos(); 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 // Prototype
@ -2119,6 +2136,8 @@ private:
bool m_is_local_player; bool m_is_local_player;
LocalPlayer *m_local_player; LocalPlayer *m_local_player;
float m_damage_visual_timer; float m_damage_visual_timer;
bool m_dead;
aabb3f m_collisionbox;
public: public:
PlayerCAO(IGameDef *gamedef, ClientEnvironment *env): PlayerCAO(IGameDef *gamedef, ClientEnvironment *env):
@ -2130,7 +2149,8 @@ public:
m_yaw(0), m_yaw(0),
m_is_local_player(false), m_is_local_player(false),
m_local_player(NULL), m_local_player(NULL),
m_damage_visual_timer(0) m_damage_visual_timer(0),
m_dead(false)
{ {
if(gamedef == NULL) if(gamedef == NULL)
ClientActiveObject::registerType(getType(), create); ClientActiveObject::registerType(getType(), create);
@ -2152,6 +2172,8 @@ public:
m_position = readV3F1000(is); m_position = readV3F1000(is);
// yaw // yaw
m_yaw = readF1000(is); m_yaw = readF1000(is);
// dead
m_dead = readU8(is);
pos_translator.init(m_position); pos_translator.init(m_position);
@ -2162,6 +2184,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() ~PlayerCAO()
{ {
if(m_node) if(m_node)
@ -2181,6 +2214,8 @@ public:
{ {
if(m_is_local_player) if(m_is_local_player)
return NULL; return NULL;
if(m_dead)
return NULL;
return &m_selection_box; return &m_selection_box;
} }
v3f getPosition() v3f getPosition()
@ -2256,6 +2291,7 @@ public:
m_text->setPosition(v3f(0, (f32)BS*2.1, 0)); m_text->setPosition(v3f(0, (f32)BS*2.1, 0));
updateTextures(""); updateTextures("");
updateVisibility();
updateNodePos(); updateNodePos();
} }
@ -2273,11 +2309,11 @@ public:
if(m_node == NULL) if(m_node == NULL)
return; return;
m_node->setVisible(true);
u8 li = decode_light(light_at_pos); u8 li = decode_light(light_at_pos);
video::SColor color(255,li,li,li); video::SColor color(255,li,li,li);
setMeshColor(m_node->getMesh(), color); setMeshColor(m_node->getMesh(), color);
updateVisibility();
} }
v3s16 getLightPosition() v3s16 getLightPosition()
@ -2285,6 +2321,14 @@ public:
return floatToInt(m_position+v3f(0,BS*1.5,0), BS); 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() void updateNodePos()
{ {
if(m_node == NULL) if(m_node == NULL)
@ -2300,6 +2344,7 @@ public:
void step(float dtime, ClientEnvironment *env) void step(float dtime, ClientEnvironment *env)
{ {
pos_translator.translate(dtime); pos_translator.translate(dtime);
updateVisibility();
updateNodePos(); updateNodePos();
if(m_damage_visual_timer > 0){ if(m_damage_visual_timer > 0){
@ -2331,13 +2376,16 @@ public:
{ {
// damage // damage
s16 damage = readS16(is); s16 damage = readS16(is);
m_damage_visual_timer = 0.05;
if(m_is_local_player) if(damage >= 2)
m_env->damageLocalPlayer(damage, false); m_damage_visual_timer += 0.05 * damage;
m_damage_visual_timer = 0.5;
updateTextures("^[brighten"); updateTextures("^[brighten");
} }
else if(cmd == 2) // died or respawned
{
m_dead = readU8(is);
updateVisibility();
}
} }
void updateTextures(const std::string &mod) void updateTextures(const std::string &mod)

View file

@ -985,6 +985,72 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
u16 indices[] = {0,1,2,2,3,0}; u16 indices[] = {0,1,2,2,3,0};
collector.append(material_rail, vertices, 4, indices, 6); collector.append(material_rail, vertices, 4, indices, 6);
break;} 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<aabb3f> boxes = n.getNodeBoxes(nodedef);
for(std::vector<aabb3f>::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;}
} }
} }
} }

View file

@ -161,9 +161,11 @@ void ItemSAO::step(float dtime, bool send_recommended)
m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime); m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);
v3f pos_f = getBasePosition(); v3f pos_f = getBasePosition();
v3f pos_f_old = pos_f; v3f pos_f_old = pos_f;
IGameDef *gamedef = m_env->getGameDef(); v3f accel_f = v3f(0,0,0);
moveresult = collisionMoveSimple(&m_env->getMap(), gamedef, f32 stepheight = 0;
pos_max_d, box, dtime, pos_f, m_speed_f); moveresult = collisionMoveSimple(m_env,
pos_max_d, box, stepheight, dtime,
pos_f, m_speed_f, accel_f);
if(send_recommended == false) if(send_recommended == false)
return; return;
@ -402,9 +404,11 @@ void RatSAO::step(float dtime, bool send_recommended)
m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime); m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);
v3f pos_f = getBasePosition(); v3f pos_f = getBasePosition();
v3f pos_f_old = pos_f; v3f pos_f_old = pos_f;
IGameDef *gamedef = m_env->getGameDef(); v3f accel_f = v3f(0,0,0);
moveresult = collisionMoveSimple(&m_env->getMap(), gamedef, f32 stepheight = 0;
pos_max_d, box, dtime, pos_f, m_speed_f); moveresult = collisionMoveSimple(m_env,
pos_max_d, box, stepheight, dtime,
pos_f, m_speed_f, accel_f);
m_touching_ground = moveresult.touching_ground; m_touching_ground = moveresult.touching_ground;
setBasePosition(pos_f); setBasePosition(pos_f);
@ -650,9 +654,11 @@ void Oerkki1SAO::step(float dtime, bool send_recommended)
m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);*/ m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);*/
v3f pos_f = getBasePosition(); v3f pos_f = getBasePosition();
v3f pos_f_old = pos_f; v3f pos_f_old = pos_f;
IGameDef *gamedef = m_env->getGameDef(); v3f accel_f = v3f(0,0,0);
moveresult = collisionMovePrecise(&m_env->getMap(), gamedef, f32 stepheight = 0;
pos_max_d, box, dtime, pos_f, m_speed_f); moveresult = collisionMovePrecise(m_env,
pos_max_d, box, stepheight, dtime,
pos_f, m_speed_f, accel_f);
m_touching_ground = moveresult.touching_ground; m_touching_ground = moveresult.touching_ground;
// Do collision damage // Do collision damage
@ -897,9 +903,11 @@ void FireflySAO::step(float dtime, bool send_recommended)
m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime); m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);
v3f pos_f = getBasePosition(); v3f pos_f = getBasePosition();
v3f pos_f_old = pos_f; v3f pos_f_old = pos_f;
IGameDef *gamedef = m_env->getGameDef(); v3f accel_f = v3f(0,0,0);
moveresult = collisionMoveSimple(&m_env->getMap(), gamedef, f32 stepheight = 0;
pos_max_d, box, dtime, pos_f, m_speed_f); moveresult = collisionMoveSimple(m_env,
pos_max_d, box, stepheight, dtime,
pos_f, m_speed_f, accel_f);
m_touching_ground = moveresult.touching_ground; m_touching_ground = moveresult.touching_ground;
setBasePosition(pos_f); setBasePosition(pos_f);
@ -1616,16 +1624,17 @@ void LuaEntitySAO::step(float dtime, bool send_recommended)
box.MaxEdge *= BS; box.MaxEdge *= BS;
collisionMoveResult moveresult; collisionMoveResult moveresult;
f32 pos_max_d = BS*0.25; // Distance per iteration 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_velocity = m_velocity;
IGameDef *gamedef = m_env->getGameDef(); v3f p_acceleration = m_acceleration;
moveresult = collisionMovePrecise(&m_env->getMap(), gamedef, moveresult = collisionMovePrecise(m_env,
pos_max_d, box, dtime, p_pos, p_velocity); pos_max_d, box, stepheight, dtime,
p_pos, p_velocity, p_acceleration);
// Apply results // Apply results
setBasePosition(p_pos); m_base_position = p_pos;
m_velocity = p_velocity; m_velocity = p_velocity;
m_acceleration = p_acceleration;
m_velocity += dtime * m_acceleration;
} else { } else {
m_base_position += dtime * m_velocity + 0.5 * dtime m_base_position += dtime * m_velocity + 0.5 * dtime
* dtime * m_acceleration; * dtime * m_acceleration;
@ -1828,3 +1837,18 @@ void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
m_messages_out.push_back(aom); 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;
}

View file

@ -32,6 +32,7 @@ public:
static ServerActiveObject* create(ServerEnvironment *env, v3f pos, static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
const std::string &data); const std::string &data);
void step(float dtime, bool send_recommended); void step(float dtime, bool send_recommended);
inline aabb3f* getCollisionBox() { return NULL; }
private: private:
float m_timer1; float m_timer1;
float m_age; float m_age;
@ -51,6 +52,7 @@ public:
ItemStack createItemStack(); ItemStack createItemStack();
void punch(ServerActiveObject *puncher, float time_from_last_punch); void punch(ServerActiveObject *puncher, float time_from_last_punch);
float getMinimumSavedMovement(){ return 0.1*BS; } float getMinimumSavedMovement(){ return 0.1*BS; }
inline aabb3f* getCollisionBox() { return NULL; }
private: private:
std::string m_itemstring; std::string m_itemstring;
bool m_itemstring_changed; bool m_itemstring_changed;
@ -71,6 +73,7 @@ public:
std::string getClientInitializationData(); std::string getClientInitializationData();
std::string getStaticData(); std::string getStaticData();
void punch(ServerActiveObject *puncher, float time_from_last_punch); void punch(ServerActiveObject *puncher, float time_from_last_punch);
inline aabb3f* getCollisionBox() { return NULL; }
private: private:
bool m_is_active; bool m_is_active;
IntervalLimiter m_inactive_interval; IntervalLimiter m_inactive_interval;
@ -97,6 +100,7 @@ public:
std::string getStaticData(); std::string getStaticData();
void punch(ServerActiveObject *puncher, float time_from_last_punch); void punch(ServerActiveObject *puncher, float time_from_last_punch);
bool isPeaceful(){return false;} bool isPeaceful(){return false;}
inline aabb3f* getCollisionBox() { return NULL; }
private: private:
void doDamage(u16 d); void doDamage(u16 d);
@ -125,6 +129,7 @@ public:
void step(float dtime, bool send_recommended); void step(float dtime, bool send_recommended);
std::string getClientInitializationData(); std::string getClientInitializationData();
std::string getStaticData(); std::string getStaticData();
inline aabb3f* getCollisionBox() { return NULL; }
private: private:
bool m_is_active; bool m_is_active;
IntervalLimiter m_inactive_interval; IntervalLimiter m_inactive_interval;
@ -155,6 +160,7 @@ public:
void step(float dtime, bool send_recommended); void step(float dtime, bool send_recommended);
void punch(ServerActiveObject *puncher, float time_from_last_punch); void punch(ServerActiveObject *puncher, float time_from_last_punch);
bool isPeaceful(); bool isPeaceful();
inline aabb3f* getCollisionBox() { return NULL; }
private: private:
void sendPosition(); void sendPosition();
void setPropertyDefaults(); void setPropertyDefaults();
@ -222,6 +228,7 @@ public:
void setSprite(v2s16 p, int num_frames, float framelength, void setSprite(v2s16 p, int num_frames, float framelength,
bool select_horiz_by_yawpitch); bool select_horiz_by_yawpitch);
std::string getName(); std::string getName();
aabb3f* getCollisionBox();
private: private:
void sendPosition(bool do_interpolate, bool is_movement_end); void sendPosition(bool do_interpolate, bool is_movement_end);
@ -238,6 +245,7 @@ private:
v3f m_last_sent_velocity; v3f m_last_sent_velocity;
float m_last_sent_position_timer; float m_last_sent_position_timer;
float m_last_sent_move_precision; float m_last_sent_move_precision;
aabb3f m_collisionbox;
}; };
#endif #endif

View file

@ -812,6 +812,8 @@ void ServerEnvironment::clearAllObjects()
i.atEnd()==false; i++) i.atEnd()==false; i++)
{ {
ServerActiveObject* obj = i.getNode()->getValue(); ServerActiveObject* obj = i.getNode()->getValue();
if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
continue;
u16 id = i.getNode()->getKey(); u16 id = i.getNode()->getKey();
v3f objectpos = obj->getBasePosition(); v3f objectpos = obj->getBasePosition();
// Delete static object if block is loaded // Delete static object if block is loaded
@ -933,7 +935,7 @@ void ServerEnvironment::step(float dtime)
v3f playerpos = player->getPosition(); v3f playerpos = player->getPosition();
// Move // Move
player->move(dtime, *m_map, 100*BS); player->move(dtime, this, 100*BS);
/* /*
Add footsteps to grass Add footsteps to grass
@ -1816,6 +1818,22 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete)
} }
} }
//get a list of all active objects
core::list<ActiveObject*> ServerEnvironment::getActiveObjects() {
core::list<ActiveObject*> retval;
if (m_active_objects.size() > 0 )
{
for (core::map<u16, ServerActiveObject*>::Iterator iter=m_active_objects.getIterator();
iter.atEnd()==false; iter++) {
retval.push_back(iter.getNode()->getValue());
}
}
return retval;
}
#ifndef SERVER #ifndef SERVER
@ -1976,7 +1994,7 @@ void ClientEnvironment::step(float dtime)
Move the lplayer. Move the lplayer.
This also does collision detection. This also does collision detection.
*/ */
lplayer->move(dtime_part, *m_map, position_max_increment, lplayer->move(dtime_part, this, position_max_increment,
&player_collisions); &player_collisions);
} }
} }
@ -1998,16 +2016,7 @@ void ClientEnvironment::step(float dtime)
{ {
f32 damage_f = (info.speed - tolerance)/BS*factor; f32 damage_f = (info.speed - tolerance)/BS*factor;
u16 damage = (u16)(damage_f+0.5); u16 damage = (u16)(damage_f+0.5);
if(lplayer->hp > damage) damageLocalPlayer(damage, true);
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);
} }
} }
} }
@ -2037,11 +2046,7 @@ void ClientEnvironment::step(float dtime)
if(damage_per_second != 0) if(damage_per_second != 0)
{ {
ClientEnvEvent event; damageLocalPlayer(damage_per_second, true);
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);
} }
} }
@ -2060,7 +2065,7 @@ void ClientEnvironment::step(float dtime)
if(player->isLocal() == false) if(player->isLocal() == false)
{ {
// Move // Move
player->move(dtime, *m_map, 100*BS); player->move(dtime, this, 100*BS);
} }
@ -2337,6 +2342,21 @@ ClientEnvEvent ClientEnvironment::getClientEvent()
return m_client_event_queue.pop_front(); return m_client_event_queue.pop_front();
} }
//get a list of all active objects
core::list<ActiveObject*> ClientEnvironment::getActiveObjects() {
core::list<ActiveObject*> retval;
if (m_active_objects.size() > 0 )
{
for (core::map<u16, ClientActiveObject*>::Iterator iter=m_active_objects.getIterator();
iter.atEnd()==false; iter++)
{
retval.push_back(iter.getNode()->getValue());
}
}
return retval;
}
#endif // #ifndef SERVER #endif // #ifndef SERVER

View file

@ -87,6 +87,9 @@ public:
return m_time_of_day; return m_time_of_day;
} }
virtual IGameDef *getGameDef() = 0;
virtual core::list<ActiveObject*> getActiveObjects() = 0;
protected: protected:
// peer_ids in here should be unique, except that there may be many 0s // peer_ids in here should be unique, except that there may be many 0s
core::list<Player*> m_players; core::list<Player*> m_players;
@ -283,6 +286,8 @@ public:
// This makes stuff happen // This makes stuff happen
void step(f32 dtime); void step(f32 dtime);
//get a list of all active objects
core::list<ActiveObject*> getActiveObjects();
private: private:
/* /*
@ -465,6 +470,9 @@ public:
// Get event from queue. CEE_NONE is returned if queue is empty. // Get event from queue. CEE_NONE is returned if queue is empty.
ClientEnvEvent getClientEvent(); ClientEnvEvent getClientEvent();
//get a list of all active objects
core::list<ActiveObject*> getActiveObjects();
private: private:
ClientMap *m_map; ClientMap *m_map;
scene::ISceneManager *m_smgr; scene::ISceneManager *m_smgr;

View file

@ -293,45 +293,41 @@ PointedThing getPointedThing(Client *client, v3f player_position,
core::line3d<f32> shootline, f32 d, core::line3d<f32> shootline, f32 d,
bool liquids_pointable, bool liquids_pointable,
bool look_for_object, bool look_for_object,
core::aabbox3d<f32> &hilightbox, std::vector<aabb3f> &hilightboxes,
bool &should_show_hilightbox,
ClientActiveObject *&selected_object) ClientActiveObject *&selected_object)
{ {
PointedThing result; PointedThing result;
hilightbox = core::aabbox3d<f32>(0,0,0,0,0,0); hilightboxes.clear();
should_show_hilightbox = false;
selected_object = NULL; selected_object = NULL;
INodeDefManager *nodedef = client->getNodeDefManager();
// First try to find a pointed at active object // First try to find a pointed at active object
if(look_for_object) if(look_for_object)
{ {
selected_object = client->getSelectedActiveObject(d*BS, selected_object = client->getSelectedActiveObject(d*BS,
camera_position, shootline); camera_position, shootline);
}
if(selected_object != NULL) if(selected_object != NULL)
{ {
core::aabbox3d<f32> *selection_box if(selected_object->doShowSelectionBox())
= selected_object->getSelectionBox(); {
// Box should exist because object was returned in the aabb3f *selection_box = selected_object->getSelectionBox();
// first place // Box should exist because object was
// returned in the first place
assert(selection_box); assert(selection_box);
v3f pos = selected_object->getPosition(); v3f pos = selected_object->getPosition();
hilightboxes.push_back(aabb3f(
hilightbox = core::aabbox3d<f32>(
selection_box->MinEdge + pos, selection_box->MinEdge + pos,
selection_box->MaxEdge + pos selection_box->MaxEdge + pos));
); }
should_show_hilightbox = selected_object->doShowSelectionBox();
result.type = POINTEDTHING_OBJECT; result.type = POINTEDTHING_OBJECT;
result.object_id = selected_object->getId(); result.object_id = selected_object->getId();
return result; return result;
} }
}
// That didn't work, try to find a pointed at node // That didn't work, try to find a pointed at node
@ -366,28 +362,6 @@ PointedThing getPointedThing(Client *client, v3f player_position,
if(!isPointableNode(n, client, liquids_pointable)) if(!isPointableNode(n, client, liquids_pointable))
continue; continue;
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)
{
core::aabbox3d<f32> box = f.selection_box.fixed;
box.MinEdge += npf;
box.MaxEdge += npf;
v3s16 facedirs[6] = { v3s16 facedirs[6] = {
v3s16(-1,0,0), v3s16(-1,0,0),
v3s16(1,0,0), v3s16(1,0,0),
@ -397,165 +371,79 @@ PointedThing getPointedThing(Client *client, v3f player_position,
v3s16(0,0,1), v3s16(0,0,1),
}; };
core::aabbox3d<f32> faceboxes[6] = { std::vector<aabb3f> boxes = n.getSelectionBoxes(client->ndef());
v3s16 np(x,y,z);
v3f npf = intToFloat(np, BS);
for(std::vector<aabb3f>::const_iterator
i = boxes.begin();
i != boxes.end(); i++)
{
aabb3f box = *i;
box.MinEdge += npf;
box.MaxEdge += npf;
f32 d = 0.001*BS;
aabb3f faceboxes[6] = {
// X- // X-
core::aabbox3d<f32>( aabb3f(
box.MinEdge.X, box.MinEdge.Y, box.MinEdge.Z, box.MinEdge.X, box.MinEdge.Y, box.MinEdge.Z,
box.MinEdge.X+d, box.MaxEdge.Y, box.MaxEdge.Z box.MinEdge.X+d, box.MaxEdge.Y, box.MaxEdge.Z
), ),
// X+ // X+
core::aabbox3d<f32>( aabb3f(
box.MaxEdge.X-d, box.MinEdge.Y, box.MinEdge.Z, box.MaxEdge.X-d, box.MinEdge.Y, box.MinEdge.Z,
box.MaxEdge.X, box.MaxEdge.Y, box.MaxEdge.Z box.MaxEdge.X, box.MaxEdge.Y, box.MaxEdge.Z
), ),
// Y- // Y-
core::aabbox3d<f32>( aabb3f(
box.MinEdge.X, box.MinEdge.Y, box.MinEdge.Z, box.MinEdge.X, box.MinEdge.Y, box.MinEdge.Z,
box.MaxEdge.X, box.MinEdge.Y+d, box.MaxEdge.Z box.MaxEdge.X, box.MinEdge.Y+d, box.MaxEdge.Z
), ),
// Y+ // Y+
core::aabbox3d<f32>( aabb3f(
box.MinEdge.X, box.MaxEdge.Y-d, box.MinEdge.Z, box.MinEdge.X, box.MaxEdge.Y-d, box.MinEdge.Z,
box.MaxEdge.X, box.MaxEdge.Y, box.MaxEdge.Z box.MaxEdge.X, box.MaxEdge.Y, box.MaxEdge.Z
), ),
// Z- // Z-
core::aabbox3d<f32>( aabb3f(
box.MinEdge.X, box.MinEdge.Y, box.MinEdge.Z, box.MinEdge.X, box.MinEdge.Y, box.MinEdge.Z,
box.MaxEdge.X, box.MaxEdge.Y, box.MinEdge.Z+d box.MaxEdge.X, box.MaxEdge.Y, box.MinEdge.Z+d
), ),
// Z+ // Z+
core::aabbox3d<f32>( aabb3f(
box.MinEdge.X, box.MinEdge.Y, box.MaxEdge.Z-d, box.MinEdge.X, box.MinEdge.Y, box.MaxEdge.Z-d,
box.MaxEdge.X, box.MaxEdge.Y, box.MaxEdge.Z 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 = faceboxes[j].getCenter();
v3f centerpoint = npf + facedir_f * BS/2;
f32 distance = (centerpoint - camera_position).getLength(); f32 distance = (centerpoint - camera_position).getLength();
if(distance >= mindistance) if(distance >= mindistance)
continue; continue;
if(!faceboxes[i].intersectsWithLine(shootline)) if(!faceboxes[j].intersectsWithLine(shootline))
continue; continue;
result.type = POINTEDTHING_NODE; result.type = POINTEDTHING_NODE;
result.node_undersurface = np; 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<f32> 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] =
{
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<f32>(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;
}
}
}
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<f32> 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<f32> 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; mindistance = distance;
//hilightbox = facebox; hilightboxes.clear();
for(std::vector<aabb3f>::const_iterator
const float d = 0.502; i2 = boxes.begin();
core::aabbox3d<f32> nodebox i2 != boxes.end(); i2++)
(-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d); {
v3f nodepos_f = intToFloat(np, BS); aabb3f box = *i2;
nodebox.MinEdge += nodepos_f; box.MinEdge += npf + v3f(-d,-d,-d);
nodebox.MaxEdge += nodepos_f; box.MaxEdge += npf + v3f(d,d,d);
hilightbox = nodebox; hilightboxes.push_back(box);
should_show_hilightbox = true; }
}
} }
} // if distance < mindistance
} // for dirs
} // regular block
} // for coords } // for coords
return result; return result;
@ -1112,9 +1000,6 @@ void the_game(
else else
hotbar_imagesize = 64; hotbar_imagesize = 64;
// Hilight boxes collected during the loop and displayed
core::list< core::aabbox3d<f32> > hilightboxes;
// Info text // Info text
std::wstring infotext; std::wstring infotext;
@ -1821,8 +1706,8 @@ void the_game(
core::line3d<f32> shootline(camera_position, core::line3d<f32> shootline(camera_position,
camera_position + camera_direction * BS * (d+1)); camera_position + camera_direction * BS * (d+1));
core::aabbox3d<f32> hilightbox; // Hilight boxes collected during the loop and displayed
bool should_show_hilightbox = false; std::vector<aabb3f> hilightboxes;
ClientActiveObject *selected_object = NULL; ClientActiveObject *selected_object = NULL;
PointedThing pointed = getPointedThing( PointedThing pointed = getPointedThing(
@ -1831,7 +1716,7 @@ void the_game(
camera_position, shootline, d, camera_position, shootline, d,
playeritem_liquids_pointable, !ldown_for_dig, playeritem_liquids_pointable, !ldown_for_dig,
// output // output
hilightbox, should_show_hilightbox, hilightboxes,
selected_object); selected_object);
if(pointed != pointed_old) if(pointed != pointed_old)
@ -1840,12 +1725,6 @@ void the_game(
//dstream<<"Pointing at "<<pointed.dump()<<std::endl; //dstream<<"Pointing at "<<pointed.dump()<<std::endl;
} }
/*
Visualize selection
*/
if(should_show_hilightbox)
hilightboxes.push_back(hilightbox);
/* /*
Stop digging when Stop digging when
- releasing left mouse button - releasing left mouse button
@ -2470,7 +2349,8 @@ void the_game(
if(show_hud) if(show_hud)
{ {
for(core::list<aabb3f>::Iterator i=hilightboxes.begin(); for(std::vector<aabb3f>::const_iterator
i = hilightboxes.begin();
i != hilightboxes.end(); i++) i != hilightboxes.end(); i++)
{ {
/*infostream<<"hilightbox min=" /*infostream<<"hilightbox min="

View file

@ -21,7 +21,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#if 1 #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 #!/usr/bin/python
@ -63,8 +65,8 @@ u8 light_decode_table[LIGHT_MAX+1] =
121, 121,
146, 146,
176, 176,
212, 195,
230, 220,
255, 255,
}; };
#endif #endif

View file

@ -132,7 +132,98 @@ v3s16 MapNode::getWallMountedDir(INodeDefManager *nodemgr) const
} }
} }
static std::vector<aabb3f> transformNodeBox(const MapNode &n,
const NodeBox &nodebox, INodeDefManager *nodemgr)
{
std::vector<aabb3f> boxes;
if(nodebox.type == NODEBOX_FIXED)
{
const std::vector<aabb3f> &fixed = nodebox.fixed;
int facedir = n.getFaceDir(nodemgr);
for(std::vector<aabb3f>::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<aabb3f> MapNode::getNodeBoxes(INodeDefManager *nodemgr) const
{
const ContentFeatures &f = nodemgr->get(*this);
return transformNodeBox(*this, f.node_box, nodemgr);
}
std::vector<aabb3f> MapNode::getSelectionBoxes(INodeDefManager *nodemgr) const
{
const ContentFeatures &f = nodemgr->get(*this);
return transformNodeBox(*this, f.selection_box, nodemgr);
}
u32 MapNode::serializedLength(u8 version) u32 MapNode::serializedLength(u8 version)
{ {

View file

@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "irrlichttypes.h" #include "irrlichttypes.h"
#include "light.h" #include "light.h"
#include <vector>
class INodeDefManager; class INodeDefManager;
@ -195,6 +196,17 @@ struct MapNode
u8 getWallMounted(INodeDefManager *nodemgr) const; u8 getWallMounted(INodeDefManager *nodemgr) const;
v3s16 getWallMountedDir(INodeDefManager *nodemgr) const; v3s16 getWallMountedDir(INodeDefManager *nodemgr) const;
/*
Gets list of node boxes (used for rendering (NDT_NODEBOX)
and collision)
*/
std::vector<aabb3f> getNodeBoxes(INodeDefManager *nodemgr) const;
/*
Gets list of selection boxes
*/
std::vector<aabb3f> getSelectionBoxes(INodeDefManager *nodemgr) const;
/* /*
Serialization functions Serialization functions
*/ */

View file

@ -32,34 +32,74 @@ with this program; if not, write to the Free Software Foundation, Inc.,
NodeBox 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 void NodeBox::serialize(std::ostream &os) const
{ {
writeU8(os, 0); // version writeU8(os, 1); // version
writeU8(os, type); writeU8(os, type);
writeV3F1000(os, fixed.MinEdge);
writeV3F1000(os, fixed.MaxEdge); if(type == NODEBOX_FIXED)
{
writeU16(os, fixed.size());
for(std::vector<aabb3f>::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.MinEdge);
writeV3F1000(os, wall_top.MaxEdge); writeV3F1000(os, wall_top.MaxEdge);
writeV3F1000(os, wall_bottom.MinEdge); writeV3F1000(os, wall_bottom.MinEdge);
writeV3F1000(os, wall_bottom.MaxEdge); writeV3F1000(os, wall_bottom.MaxEdge);
writeV3F1000(os, wall_side.MinEdge); writeV3F1000(os, wall_side.MinEdge);
writeV3F1000(os, wall_side.MaxEdge); writeV3F1000(os, wall_side.MaxEdge);
}
} }
void NodeBox::deSerialize(std::istream &is) void NodeBox::deSerialize(std::istream &is)
{ {
int version = readU8(is); int version = readU8(is);
if(version != 0) if(version != 1)
throw SerializationError("unsupported NodeBox version"); throw SerializationError("unsupported NodeBox version");
reset();
type = (enum NodeBoxType)readU8(is); type = (enum NodeBoxType)readU8(is);
fixed.MinEdge = readV3F1000(is);
fixed.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.MinEdge = readV3F1000(is);
wall_top.MaxEdge = readV3F1000(is); wall_top.MaxEdge = readV3F1000(is);
wall_bottom.MinEdge = readV3F1000(is); wall_bottom.MinEdge = readV3F1000(is);
wall_bottom.MaxEdge = readV3F1000(is); wall_bottom.MaxEdge = readV3F1000(is);
wall_side.MinEdge = readV3F1000(is); wall_side.MinEdge = readV3F1000(is);
wall_side.MaxEdge = readV3F1000(is); wall_side.MaxEdge = readV3F1000(is);
}
} }
/* /*
@ -143,6 +183,7 @@ void ContentFeatures::reset()
liquid_viscosity = 0; liquid_viscosity = 0;
light_source = 0; light_source = 0;
damage_per_second = 0; damage_per_second = 0;
node_box = NodeBox();
selection_box = NodeBox(); selection_box = NodeBox();
material = MaterialProperties(); material = MaterialProperties();
// Make unknown blocks diggable // Make unknown blocks diggable
@ -154,7 +195,7 @@ void ContentFeatures::reset()
void ContentFeatures::serialize(std::ostream &os) void ContentFeatures::serialize(std::ostream &os)
{ {
writeU8(os, 1); // version writeU8(os, 2); // version
os<<serializeString(name); os<<serializeString(name);
writeU8(os, drawtype); writeU8(os, drawtype);
writeF1000(os, visual_scale); writeF1000(os, visual_scale);
@ -187,6 +228,7 @@ void ContentFeatures::serialize(std::ostream &os)
writeU8(os, liquid_viscosity); writeU8(os, liquid_viscosity);
writeU8(os, light_source); writeU8(os, light_source);
writeU32(os, damage_per_second); writeU32(os, damage_per_second);
node_box.serialize(os);
selection_box.serialize(os); selection_box.serialize(os);
material.serialize(os); material.serialize(os);
writeU8(os, legacy_facedir_simple); writeU8(os, legacy_facedir_simple);
@ -196,7 +238,7 @@ void ContentFeatures::serialize(std::ostream &os)
void ContentFeatures::deSerialize(std::istream &is) void ContentFeatures::deSerialize(std::istream &is)
{ {
int version = readU8(is); int version = readU8(is);
if(version != 1) if(version != 2)
throw SerializationError("unsupported ContentFeatures version"); throw SerializationError("unsupported ContentFeatures version");
name = deSerializeString(is); name = deSerializeString(is);
drawtype = (enum NodeDrawType)readU8(is); drawtype = (enum NodeDrawType)readU8(is);
@ -232,6 +274,7 @@ void ContentFeatures::deSerialize(std::istream &is)
liquid_viscosity = readU8(is); liquid_viscosity = readU8(is);
light_source = readU8(is); light_source = readU8(is);
damage_per_second = readU32(is); damage_per_second = readU32(is);
node_box.deSerialize(is);
selection_box.deSerialize(is); selection_box.deSerialize(is);
material.deSerialize(is); material.deSerialize(is);
legacy_facedir_simple = readU8(is); legacy_facedir_simple = readU8(is);
@ -509,6 +552,7 @@ public:
case NDT_PLANTLIKE: case NDT_PLANTLIKE:
case NDT_FENCELIKE: case NDT_FENCELIKE:
case NDT_RAILLIKE: case NDT_RAILLIKE:
case NDT_NODEBOX:
f->solidness = 0; f->solidness = 0;
break; break;
} }

View file

@ -62,7 +62,7 @@ enum LiquidType
enum NodeBoxType enum NodeBoxType
{ {
NODEBOX_REGULAR, // Regular block; allows buildable_to 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) NODEBOX_WALLMOUNTED, // Box for wall mounted nodes; (top, bottom, side)
}; };
@ -71,22 +71,16 @@ struct NodeBox
enum NodeBoxType type; enum NodeBoxType type;
// NODEBOX_REGULAR (no parameters) // NODEBOX_REGULAR (no parameters)
// NODEBOX_FIXED // NODEBOX_FIXED
core::aabbox3d<f32> fixed; std::vector<aabb3f> fixed;
// NODEBOX_WALLMOUNTED // NODEBOX_WALLMOUNTED
core::aabbox3d<f32> wall_top; aabb3f wall_top;
core::aabbox3d<f32> wall_bottom; aabb3f wall_bottom;
core::aabbox3d<f32> wall_side; // being at the -X side aabb3f wall_side; // being at the -X side
NodeBox(): NodeBox()
type(NODEBOX_REGULAR), { reset(); }
// 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)
{}
void reset();
void serialize(std::ostream &os) const; void serialize(std::ostream &os) const;
void deSerialize(std::istream &is); void deSerialize(std::istream &is);
}; };
@ -122,6 +116,7 @@ enum NodeDrawType
NDT_PLANTLIKE, NDT_PLANTLIKE,
NDT_FENCELIKE, NDT_FENCELIKE,
NDT_RAILLIKE, NDT_RAILLIKE,
NDT_NODEBOX,
}; };
#define CF_SPECIAL_COUNT 2 #define CF_SPECIAL_COUNT 2
@ -193,6 +188,7 @@ struct ContentFeatures
// Amount of light the node emits // Amount of light the node emits
u8 light_source; u8 light_source;
u32 damage_per_second; u32 damage_per_second;
NodeBox node_box;
NodeBox selection_box; NodeBox selection_box;
MaterialProperties material; MaterialProperties material;
// Compatibility with old maps // Compatibility with old maps

View file

@ -199,29 +199,21 @@ 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<CollisionInfo> *collision_info) core::list<CollisionInfo> *collision_info)
{ {
Map& map = env->getMap();
INodeDefManager *nodemgr = m_gamedef->ndef(); INodeDefManager *nodemgr = m_gamedef->ndef();
v3f position = getPosition(); v3f position = getPosition();
v3f oldpos = position;
v3s16 oldpos_i = floatToInt(oldpos, BS);
v3f old_speed = m_speed; v3f old_speed = m_speed;
/*std::cout<<"oldpos_i=("<<oldpos_i.X<<","<<oldpos_i.Y<<","
<<oldpos_i.Z<<")"<<std::endl;*/
/*
Calculate new position
*/
position += m_speed * dtime;
// Skip collision detection if a special movement mode is used // Skip collision detection if a special movement mode is used
bool free_move = g_settings->getBool("free_move"); bool free_move = g_settings->getBool("free_move");
if(free_move) if(free_move)
{ {
position += m_speed * dtime;
setPosition(position); setPosition(position);
return; return;
} }
@ -230,9 +222,6 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
Collision detection Collision detection
*/ */
// Player position in nodes
v3s16 pos_i = floatToInt(position, BS);
/* /*
Check if player is in water (the oscillating value) Check if player is in water (the oscillating value)
*/ */
@ -282,30 +271,12 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
is_climbing = false; 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_radius = BS*0.35;
float player_height = BS*1.7; float player_height = BS*1.7;
// Maximum distance over border for sneaking // Maximum distance over border for sneaking
f32 sneak_max = BS*0.4; 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 If sneaking, keep in range from the last walked node and don't
fall off from it fall off from it
@ -321,23 +292,8 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
if(position.Y < min_y) if(position.Y < min_y)
{ {
position.Y = min_y; position.Y = min_y;
//v3f old_speed = m_speed;
if(m_speed.Y < 0) if(m_speed.Y < 0)
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 +301,31 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
Calculate player collision box (new and old) Calculate player collision box (new and old)
*/ */
core::aabbox3d<f32> playerbox( core::aabbox3d<f32> playerbox(
position.X - player_radius, -player_radius,
position.Y - 0.0, 0.0,
position.Z - player_radius, -player_radius,
position.X + player_radius, player_radius,
position.Y + player_height, player_height,
position.Z + player_radius player_radius
);
core::aabbox3d<f32> 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
); );
float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
v3f accel_f = v3f(0,0,0);
collisionMoveResult result = collisionMoveSimple(env,
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 If the player's feet touch the topside of any node, this is
set to true. set to true.
Player is allowed to jump when this is true. Player is allowed to jump when this is true.
*/ */
touching_ground = false; touching_ground = result.touching_ground;
/*std::cout<<"Checking collisions for (" bool standing_on_unloaded = result.standing_on_unloaded;
<<oldpos_i.X<<","<<oldpos_i.Y<<","<<oldpos_i.Z
<<") -> ("
<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z
<<"):"<<std::endl;*/
bool standing_on_unloaded = false;
/*
Go through every node around the player
*/
for(s16 y = oldpos_i.Y - 1; y <= oldpos_i.Y + 2; y++)
for(s16 z = oldpos_i.Z - 1; z <= oldpos_i.Z + 1; z++)
for(s16 x = oldpos_i.X - 1; x <= oldpos_i.X + 1; x++)
{
bool is_unloaded = false;
try{
// Player collides into walkable nodes
if(nodemgr->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<f32> 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
/* /*
Check the nodes under the player to see from which node the Check the nodes under the player to see from which node the
@ -610,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) void LocalPlayer::applyControl(float dtime)
@ -713,15 +524,18 @@ void LocalPlayer::applyControl(float dtime)
} }
else if(touching_ground) else if(touching_ground)
{ {
v3f speed = getSpeed();
/* /*
NOTE: The d value in move() affects jump height by NOTE: The d value in move() affects jump height by
raising the height at which the jump speed is kept raising the height at which the jump speed is kept
at its starting value at its starting value
*/ */
v3f speed = getSpeed();
if(speed.Y >= -0.5*BS)
{
speed.Y = 6.5*BS; speed.Y = 6.5*BS;
setSpeed(speed); setSpeed(speed);
} }
}
// Use the oscillating value for getting out of water // Use the oscillating value for getting out of water
// (so that the player doesn't fly on the surface) // (so that the player doesn't fly on the surface)
else if(in_water) else if(in_water)

View file

@ -31,6 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
class Map; class Map;
class IGameDef; class IGameDef;
struct CollisionInfo; struct CollisionInfo;
class Environment;
class Player class Player
{ {
@ -42,7 +43,7 @@ public:
void resetInventory(); void resetInventory();
//void move(f32 dtime, Map &map); //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() v3f getSpeed()
{ {
@ -231,9 +232,9 @@ public:
return true; return true;
} }
void move(f32 dtime, Map &map, f32 pos_max_d, void move(f32 dtime, Environment* env, f32 pos_max_d,
core::list<CollisionInfo> *collision_info); core::list<CollisionInfo> *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); void applyControl(float dtime);

View file

@ -381,6 +381,7 @@ struct EnumString es_DrawType[] =
{NDT_PLANTLIKE, "plantlike"}, {NDT_PLANTLIKE, "plantlike"},
{NDT_FENCELIKE, "fencelike"}, {NDT_FENCELIKE, "fencelike"},
{NDT_RAILLIKE, "raillike"}, {NDT_RAILLIKE, "raillike"},
{NDT_NODEBOX, "nodebox"},
{0, NULL}, {0, NULL},
}; };
@ -587,32 +588,93 @@ static video::SColor readARGB8(lua_State *L, int index)
return color; return color;
} }
static core::aabbox3d<f32> read_aabbox3df32(lua_State *L, int index, f32 scale) static aabb3f read_aabb3f(lua_State *L, int index, f32 scale)
{ {
core::aabbox3d<f32> box; aabb3f box;
if(lua_istable(L, -1)){ if(lua_istable(L, index)){
lua_rawgeti(L, -1, 1); lua_rawgeti(L, index, 1);
box.MinEdge.X = lua_tonumber(L, -1) * scale; box.MinEdge.X = lua_tonumber(L, -1) * scale;
lua_pop(L, 1); lua_pop(L, 1);
lua_rawgeti(L, -1, 2); lua_rawgeti(L, index, 2);
box.MinEdge.Y = lua_tonumber(L, -1) * scale; box.MinEdge.Y = lua_tonumber(L, -1) * scale;
lua_pop(L, 1); lua_pop(L, 1);
lua_rawgeti(L, -1, 3); lua_rawgeti(L, index, 3);
box.MinEdge.Z = lua_tonumber(L, -1) * scale; box.MinEdge.Z = lua_tonumber(L, -1) * scale;
lua_pop(L, 1); lua_pop(L, 1);
lua_rawgeti(L, -1, 4); lua_rawgeti(L, index, 4);
box.MaxEdge.X = lua_tonumber(L, -1) * scale; box.MaxEdge.X = lua_tonumber(L, -1) * scale;
lua_pop(L, 1); lua_pop(L, 1);
lua_rawgeti(L, -1, 5); lua_rawgeti(L, index, 5);
box.MaxEdge.Y = lua_tonumber(L, -1) * scale; box.MaxEdge.Y = lua_tonumber(L, -1) * scale;
lua_pop(L, 1); lua_pop(L, 1);
lua_rawgeti(L, -1, 6); lua_rawgeti(L, index, 6);
box.MaxEdge.Z = lua_tonumber(L, -1) * scale; box.MaxEdge.Z = lua_tonumber(L, -1) * scale;
lua_pop(L, 1); lua_pop(L, 1);
} }
return box; return box;
} }
static std::vector<aabb3f> read_aabb3f_vector(lua_State *L, int index, f32 scale)
{
std::vector<aabb3f> 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 MaterialProperties
*/ */
@ -932,31 +994,14 @@ static ContentFeatures read_content_features(lua_State *L, int index)
f.damage_per_second = getintfield_default(L, index, f.damage_per_second = getintfield_default(L, index,
"damage_per_second", f.damage_per_second); "damage_per_second", f.damage_per_second);
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"); 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)) if(lua_istable(L, -1))
f.selection_box.fixed = read_aabbox3df32(L, -1, BS); f.selection_box = read_nodebox(L, -1);
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_pop(L, 1); lua_pop(L, 1);
lua_getfield(L, index, "material"); lua_getfield(L, index, "material");
@ -4363,7 +4408,7 @@ void scriptapi_luaentity_get_properties(lua_State *L, u16 id,
lua_getfield(L, -1, "collisionbox"); lua_getfield(L, -1, "collisionbox");
if(lua_istable(L, -1)) 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); lua_pop(L, 1);
getstringfield(L, -1, "visual", prop->visual); getstringfield(L, -1, "visual", prop->visual);

View file

@ -1270,7 +1270,8 @@ void Server::AsyncRunStep()
/* /*
Handle player HPs (die if hp=0) 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 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->m_removed = false;
player->setId(0); player->setId(0);
m_env->addActiveObject(player); m_env->addActiveObject(player);
@ -2129,6 +2130,10 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
// Send HP // Send HP
SendPlayerHP(player); 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 // Send time of day
{ {
SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY( SharedBuffer<u8> 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"); 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 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); std::istringstream is(datastring, std::ios_base::binary);
u8 damage = readU8(is); u8 damage = readU8(is);
ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
if(g_settings->getBool("enable_damage")) if(g_settings->getBool("enable_damage"))
{ {
actionstream<<player->getName()<<" damaged by " actionstream<<player->getName()<<" damaged by "
<<(int)damage<<" hp at "<<PP(player->getPosition()/BS) <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
<<std::endl; <<std::endl;
HandlePlayerHP(player, damage); srp->setHP(srp->getHP() - damage);
if(srp->getHP() == 0 && srp->m_hp_not_sent)
DiePlayer(srp);
if(srp->m_hp_not_sent)
SendPlayerHP(player);
} }
else else
{ {
// Force send (to correct the client's predicted HP)
SendPlayerHP(player); SendPlayerHP(player);
} }
} }
@ -2751,8 +2760,6 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
if(player->hp != 0) if(player->hp != 0)
return; return;
srp->m_respawn_active = false;
RespawnPlayer(player); RespawnPlayer(player);
actionstream<<player->getName()<<" respawns at " actionstream<<player->getName()<<" respawns at "
@ -2811,6 +2818,13 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
infostream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="<<item_i<<", pointed="<<pointed.dump()<<std::endl; infostream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="<<item_i<<", pointed="<<pointed.dump()<<std::endl;
if(player->hp == 0)
{
infostream<<"TOSERVER_INTERACT: "<<srp->getName()
<<" tried to interact, but is dead!"<<std::endl;
return;
}
v3f player_pos = srp->m_last_good_position; v3f player_pos = srp->m_last_good_position;
// Update wielded item // Update wielded item
@ -3968,26 +3982,14 @@ void Server::SendTexturesRequested(u16 peer_id,core::list<TextureRequest> tosend
Something random Something random
*/ */
void Server::HandlePlayerHP(Player *player, s16 damage) void Server::DiePlayer(Player *player)
{ {
ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player); ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
if(srp->m_respawn_active) infostream<<"Server::DiePlayer(): Player "
return;
if(player->hp > damage)
{
if(damage != 0){
player->hp -= damage;
SendPlayerHP(player);
}
return;
}
infostream<<"Server::HandlePlayerHP(): Player "
<<player->getName()<<" dies"<<std::endl; <<player->getName()<<" dies"<<std::endl;
player->hp = 0; srp->setHP(0);
// Trigger scripted stuff // Trigger scripted stuff
scriptapi_on_dieplayer(m_lua, srp); scriptapi_on_dieplayer(m_lua, srp);
@ -3999,24 +4001,13 @@ void Server::HandlePlayerHP(Player *player, s16 damage)
} }
SendPlayerHP(player); 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)); SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0));
srp->m_removed = true;
srp->m_respawn_active = true;
}
else
{
RespawnPlayer(player);
}
} }
void Server::RespawnPlayer(Player *player) void Server::RespawnPlayer(Player *player)
{ {
player->hp = 20;
ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player); ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
srp->setHP(20);
bool repositioned = scriptapi_on_respawnplayer(m_lua, srp); bool repositioned = scriptapi_on_respawnplayer(m_lua, srp);
if(!repositioned){ if(!repositioned){
v3f pos = findSpawnPos(m_env->getServerMap()); v3f pos = findSpawnPos(m_env->getServerMap());
@ -4268,6 +4259,14 @@ ServerRemotePlayer *Server::emergePlayer(const char *name, u16 peer_id)
// Got one. // Got one.
player->peer_id = peer_id; 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 // Reset inventory to creative if in creative mode
if(g_settings->getBool("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()); v3f pos = findSpawnPos(m_env->getServerMap());
player = new ServerRemotePlayer(m_env, pos, peer_id, name); player = new ServerRemotePlayer(m_env, pos, peer_id, name);
ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
/* Add player to environment */ /* Add player to environment */
m_env->addPlayer(player); m_env->addPlayer(player);
m_env->addActiveObject(srp);
/* Run scripts */ /* Run scripts */
ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
scriptapi_on_newplayer(m_lua, srp); scriptapi_on_newplayer(m_lua, srp);
/* Add stuff to inventory */ /* Add stuff to inventory */

View file

@ -592,7 +592,7 @@ private:
Something random Something random
*/ */
void HandlePlayerHP(Player *player, s16 damage); void DiePlayer(Player *player);
void RespawnPlayer(Player *player); void RespawnPlayer(Player *player);
void UpdateCrafting(u16 peer_id); void UpdateCrafting(u16 peer_id);

View file

@ -34,7 +34,6 @@ ServerRemotePlayer::ServerRemotePlayer(ServerEnvironment *env):
m_wield_index(0), m_wield_index(0),
m_inventory_not_sent(false), m_inventory_not_sent(false),
m_hp_not_sent(false), m_hp_not_sent(false),
m_respawn_active(false),
m_is_in_environment(false), m_is_in_environment(false),
m_time_from_last_punch(0), m_time_from_last_punch(0),
m_position_not_sent(false) m_position_not_sent(false)
@ -159,6 +158,8 @@ std::string ServerRemotePlayer::getClientInitializationData()
writeV3F1000(os, getPosition()); writeV3F1000(os, getPosition());
// yaw // yaw
writeF1000(os, getYaw()); writeF1000(os, getYaw());
// dead
writeU8(os, getHP() == 0);
return os.str(); return os.str();
} }
@ -247,10 +248,34 @@ void ServerRemotePlayer::setHP(s16 hp_)
if(hp != oldhp) if(hp != oldhp)
m_hp_not_sent = true; 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() s16 ServerRemotePlayer::getHP()
{ {
return hp; 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;
}

View file

@ -40,7 +40,7 @@ public:
virtual bool isLocal() const virtual bool isLocal() const
{ return false; } { return false; }
virtual void move(f32 dtime, Map &map, f32 pos_max_d) virtual void move(f32 dtime, Environment* env, f32 pos_max_d)
{ {
} }
@ -90,13 +90,13 @@ public:
int m_wield_index; int m_wield_index;
bool m_inventory_not_sent; bool m_inventory_not_sent;
bool m_hp_not_sent; bool m_hp_not_sent;
bool m_respawn_active;
bool m_is_in_environment; bool m_is_in_environment;
// Incremented by step(), read and reset by Server // Incremented by step(), read and reset by Server
float m_time_from_last_punch; float m_time_from_last_punch;
aabb3f* getCollisionBox();
private: private:
bool m_position_not_sent; bool m_position_not_sent;
aabb3f m_collisionbox;
}; };
#endif #endif

View file

@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "utility.h" #include "utility.h"
#include "serialization.h" #include "serialization.h"
#include "voxel.h" #include "voxel.h"
#include "collision.h"
#include <sstream> #include <sstream>
#include "porting.h" #include "porting.h"
#include "content_mapnode.h" #include "content_mapnode.h"
@ -816,6 +817,153 @@ struct TestMapSector
}; };
#endif #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 struct TestSocket
{ {
void Run() void Run()
@ -1279,6 +1427,7 @@ void run_tests()
TESTPARAMS(TestVoxelManipulator, ndef); TESTPARAMS(TestVoxelManipulator, ndef);
//TEST(TestMapBlock); //TEST(TestMapBlock);
//TEST(TestMapSector); //TEST(TestMapSector);
TEST(TestCollision);
if(INTERNET_SIMULATOR == false){ if(INTERNET_SIMULATOR == false){
TEST(TestSocket); TEST(TestSocket);
dout_con<<"=== BEGIN RUNNING UNIT TESTS FOR CONNECTION ==="<<std::endl; dout_con<<"=== BEGIN RUNNING UNIT TESTS FOR CONNECTION ==="<<std::endl;

View file

@ -58,6 +58,14 @@ struct AtlasPointer
v2f size; // Size in atlas v2f size; // Size in atlas
u16 tiled; // X-wise tiling count. If 0, width of atlas is width of image. u16 tiled; // X-wise tiling count. If 0, width of atlas is width of image.
AtlasPointer():
id(0),
atlas(NULL),
pos(0,0),
size(1,1),
tiled(1)
{}
AtlasPointer( AtlasPointer(
u16 id_, u16 id_,
video::ITexture *atlas_=NULL, video::ITexture *atlas_=NULL,