Add bouncy node group
authorPerttu Ahola <celeron55@gmail.com>
Sat, 1 Sep 2012 09:58:37 +0000 (12:58 +0300)
committerPerttu Ahola <celeron55@gmail.com>
Sat, 1 Sep 2012 09:58:37 +0000 (12:58 +0300)
doc/lua_api.txt
src/collision.cpp
src/collision.h
src/environment.cpp
src/localplayer.cpp

index 008c9d40cab44edb668357b4b1db75062dad2a9f..d3e254a2d8643fad1368050df654b092c02bc80d 100644 (file)
@@ -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
 ----------------------------------------------
index 09a7df7c2c069265c36ba4a1245fb9ec480cb472..143b559fa7e427e3e7a3078e9f2ddc82a370da86 100644 (file)
@@ -211,6 +211,8 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef,
        std::vector<aabb3f> cboxes;
        std::vector<bool> is_unloaded;
        std::vector<bool> is_step_up;
+       std::vector<int> bouncy_values;
+       std::vector<v3s16> node_positions;
        {
        //TimeTaker tt2("collisionMoveSimple collect boxes");
     ScopeProfiler sp(g_profiler, "collisionMoveSimple collect boxes avg", SPT_AVG);
@@ -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<aabb3f> nodeboxes = n.getNodeBoxes(gamedef->ndef());
                        for(std::vector<aabb3f>::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);
+                       }
                }
        }
 
index 243c4b294732439b75bd562eda7915fadb940c80..52a7bbb7dfe8ebb4f54b8df3978be9b0892ae2a0 100644 (file)
@@ -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<CollisionInfo> 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
 
index 2926795425b870f730b8a4477f9ca6f3b2e2a2d4..24943ad5fa94fe128fadbf318f460f1d86d35340 100644 (file)
@@ -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);
                }
        }
        
index 16111629e60662a36178046e72f713174eb8944c..15b6fd15def7fd62901936207b91a662d36d7bb9 100644 (file)
@@ -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; i<result.collisions.size(); i++){
+                       const CollisionInfo &info = result.collisions[i];
                        collision_info->push_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);