diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 008c9d4..d3e254a 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -455,6 +455,7 @@ Special groups - 3: node is removed without tool wear immediately (torch) - disable_jump: Player (and possibly other things) cannot jump from node - fall_damage_add_percent: damage speed = speed * (1 + value/100) +- bouncy: value is bounce speed in percent Known damage and digging time defining groups ---------------------------------------------- diff --git a/src/collision.cpp b/src/collision.cpp index 09a7df7..143b559 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -211,6 +211,8 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef, std::vector cboxes; std::vector is_unloaded; std::vector is_step_up; + std::vector bouncy_values; + std::vector node_positions; { //TimeTaker tt2("collisionMoveSimple collect boxes"); ScopeProfiler sp(g_profiler, "collisionMoveSimple collect boxes avg", SPT_AVG); @@ -228,11 +230,14 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef, for(s16 y = min_y; y <= max_y; y++) for(s16 z = min_z; z <= max_z; z++) { + v3s16 p(x,y,z); try{ // Object collides into walkable nodes - MapNode n = map->getNode(v3s16(x,y,z)); - if(gamedef->getNodeDefManager()->get(n).walkable == false) + MapNode n = map->getNode(p); + const ContentFeatures &f = gamedef->getNodeDefManager()->get(n); + if(f.walkable == false) continue; + int n_bouncy_value = itemgroup_get(f.groups, "bouncy"); std::vector nodeboxes = n.getNodeBoxes(gamedef->ndef()); for(std::vector::iterator @@ -245,21 +250,27 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef, cboxes.push_back(box); is_unloaded.push_back(false); is_step_up.push_back(false); + bouncy_values.push_back(n_bouncy_value); + node_positions.push_back(p); } } catch(InvalidPositionException &e) { // Collide with unloaded nodes - aabb3f box = getNodeBox(v3s16(x,y,z), BS); + aabb3f box = getNodeBox(p, BS); cboxes.push_back(box); is_unloaded.push_back(true); is_step_up.push_back(false); + bouncy_values.push_back(0); + node_positions.push_back(p); } } } // tt2 assert(cboxes.size() == is_unloaded.size()); assert(cboxes.size() == is_step_up.size()); + assert(cboxes.size() == bouncy_values.size()); + assert(cboxes.size() == node_positions.size()); /* Collision detection @@ -342,6 +353,10 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef, cbox.MaxEdge.Y - movingbox.MinEdge.Y, d)); + // Get bounce multiplier + bool bouncy = (bouncy_values[nearest_boxindex] >= 1); + float bounce = -(float)bouncy_values[nearest_boxindex] / 100.0; + // Move to the point of collision and reduce dtime by nearest_dtime if(nearest_dtime < 0) { @@ -361,30 +376,58 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef, pos_f += speed_f * nearest_dtime; dtime -= nearest_dtime; } + + bool is_collision = true; + if(is_unloaded[nearest_boxindex]) + is_collision = false; + + CollisionInfo info; + info.type = COLLISION_NODE; + info.node_p = node_positions[nearest_boxindex]; + info.bouncy = bouncy; + info.old_speed = speed_f; // Set the speed component that caused the collision to zero if(step_up) { // Special case: Handle stairs is_step_up[nearest_boxindex] = true; + is_collision = false; } else if(nearest_collided == 0) // X { - speed_f.X = 0; + if(fabs(speed_f.X) > BS*3) + speed_f.X *= bounce; + else + speed_f.X = 0; result.collides = true; result.collides_xz = true; } else if(nearest_collided == 1) // Y { - speed_f.Y = 0; + if(fabs(speed_f.Y) > BS*3) + speed_f.Y *= bounce; + else + speed_f.Y = 0; result.collides = true; } else if(nearest_collided == 2) // Z { - speed_f.Z = 0; + if(fabs(speed_f.Z) > BS*3) + speed_f.Z *= bounce; + else + speed_f.Z = 0; result.collides = true; result.collides_xz = true; } + + info.new_speed = speed_f; + if(info.new_speed.getDistanceFrom(info.old_speed) < 0.1*BS) + is_collision = false; + + if(is_collision){ + result.collisions.push_back(info); + } } } diff --git a/src/collision.h b/src/collision.h index 243c4b2..52a7bbb 100644 --- a/src/collision.h +++ b/src/collision.h @@ -26,12 +26,35 @@ with this program; if not, write to the Free Software Foundation, Inc., class Map; class IGameDef; +enum CollisionType +{ + COLLISION_NODE +}; + +struct CollisionInfo +{ + enum CollisionType type; + v3s16 node_p; // COLLISION_NODE + bool bouncy; + v3f old_speed; + v3f new_speed; + + CollisionInfo(): + type(COLLISION_NODE), + node_p(-32768,-32768,-32768), + bouncy(false), + old_speed(0,0,0), + new_speed(0,0,0) + {} +}; + struct collisionMoveResult { bool touching_ground; bool collides; bool collides_xz; bool standing_on_unloaded; + std::vector collisions; collisionMoveResult(): touching_ground(false), @@ -72,16 +95,5 @@ bool wouldCollideWithCeiling( f32 y_increase, f32 d); -enum CollisionType -{ - COLLISION_FALL -}; - -struct CollisionInfo -{ - CollisionType t; - f32 speed; -}; - #endif diff --git a/src/environment.cpp b/src/environment.cpp index 2926795..24943ad 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -2033,25 +2033,26 @@ void ClientEnvironment::step(float dtime) i = player_collisions.begin(); i != player_collisions.end(); i++) { + f32 pre_factor = 1; // 1 hp per node/s + f32 tolerance = BS*14; // 5 without damage + f32 post_factor = 1; // 1 hp per node/s CollisionInfo &info = *i; - if(info.t == COLLISION_FALL) + if(info.type == COLLISION_NODE) { - //f32 tolerance = BS*10; // 2 without damage - //f32 tolerance = BS*12; // 3 without damage - f32 tolerance = BS*14; // 5 without damage - f32 factor = 1; const ContentFeatures &f = m_gamedef->ndef()-> - get(m_map->getNodeNoEx(lplayer->getStandingNodePos())); + get(m_map->getNodeNoEx(info.node_p)); // Determine fall damage multiplier int addp = itemgroup_get(f.groups, "fall_damage_add_percent"); - info.speed *= (1.0 + (float)addp/100.0); - if(info.speed > tolerance) - { - f32 damage_f = (info.speed - tolerance)/BS*factor; - u16 damage = (u16)(damage_f+0.5); - if(damage != 0) - damageLocalPlayer(damage, true); - } + pre_factor = 1.0 + (float)addp/100.0; + } + float speed = (info.new_speed - info.old_speed).getLength(); + speed *= pre_factor; + if(speed > tolerance) + { + f32 damage_f = (speed - tolerance)/BS * post_factor; + u16 damage = (u16)(damage_f+0.5); + if(damage != 0) + damageLocalPlayer(damage, true); } } diff --git a/src/localplayer.cpp b/src/localplayer.cpp index 1611162..15b6fd1 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -189,7 +189,7 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, bool touching_ground_was = touching_ground; touching_ground = result.touching_ground; - bool standing_on_unloaded = result.standing_on_unloaded; + //bool standing_on_unloaded = result.standing_on_unloaded; /* Check the nodes under the player to see from which node the @@ -282,18 +282,25 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, /* Report collisions */ + bool bouncy_jump = false; if(collision_info) { - // Report fall collision - if(old_speed.Y < m_speed.Y - 0.1 && !standing_on_unloaded) - { - CollisionInfo info; - info.t = COLLISION_FALL; - info.speed = m_speed.Y - old_speed.Y; + for(size_t i=0; ipush_back(info); + if(info.new_speed.Y - info.old_speed.Y > 0.1*BS && + info.bouncy) + bouncy_jump = true; } } + if(bouncy_jump && control.jump){ + m_speed.Y += 6.5*BS; + touching_ground = false; + MtEvent *e = new SimpleTriggerEvent("PlayerJump"); + m_gamedef->event()->put(e); + } + if(!touching_ground_was && touching_ground){ MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround"); m_gamedef->event()->put(e); @@ -479,8 +486,9 @@ void LocalPlayer::applyControl(float dtime) v3f speed = getSpeed(); if(speed.Y >= -0.5*BS) { - speed.Y = 6.5*BS; + speed.Y += 6.5*BS; setSpeed(speed); + m_can_jump = false; MtEvent *e = new SimpleTriggerEvent("PlayerJump"); m_gamedef->event()->put(e);