Index: ./Games/Pingus/src/actions/bomber.cxx =================================================================== RCS file: /var/lib/cvs/Games/Pingus/src/actions/bomber.cxx,v retrieving revision 1.32 diff -u -r1.32 bomber.cxx --- ./Games/Pingus/src/actions/bomber.cxx 18 Mar 2003 17:03:02 -0000 1.32 +++ ./Games/Pingus/src/actions/bomber.cxx 5 Apr 2003 23:43:14 -0000 @@ -82,7 +82,9 @@ { sprite.update (); - Movers::LinearMover mover(WorldObj::get_world(), pingu->get_pos()); + Vector old_pos = pingu->get_pos(); + + Movers::LinearMover mover(WorldObj::get_world(), old_pos); Vector velocity = pingu->get_velocity(); @@ -99,9 +101,15 @@ return; } - // If the Bomber hasn't 'exploded' yet and it has hit the ground too quickly + float dist_to_ground = pingu->get_pos().y - old_pos.y; + float gravity = WorldObj::get_world()->get_gravity(); + + // If the Bomber hasn't 'exploded' yet and it has hit the ground too quickly. + // Use equation [v^2 = u^2 + 2as] to work out at what velocity the Pingu hit + // the ground. if (sprite.get_frame () <= 9 && rel_getpixel(0, -1) != Groundtype::GP_NOTHING - && velocity.y > deadly_velocity) + && deadly_velocity * deadly_velocity <= velocity.y * velocity.y + + 2.0f * gravity * dist_to_ground) { pingu->set_action(Actions::Splashed); return; Index: ./Games/Pingus/src/actions/faller.cxx =================================================================== RCS file: /var/lib/cvs/Games/Pingus/src/actions/faller.cxx,v retrieving revision 1.34 diff -u -r1.34 faller.cxx --- ./Games/Pingus/src/actions/faller.cxx 3 Nov 2002 22:20:29 -0000 1.34 +++ ./Games/Pingus/src/actions/faller.cxx 5 Apr 2003 23:43:15 -0000 @@ -1,4 +1,4 @@ -// $Id: faller.cxx,v 1.34 2002/11/03 22:20:29 grumbel Exp $ +// $Id: faller.cxx,v 1.39 2003/03/25 00:56:33 grumbel Exp $ // // Pingus - A free Lemmings clone // Copyright (C) 2000 Ingo Ruhnke @@ -19,17 +19,19 @@ #include #include "../col_map.hxx" +#include "../colliders/pingu_collider.hxx" #include "../debug.hxx" #include "../globals.hxx" -#include "../graphic_context.hxx" +#include "../gui/graphic_context.hxx" +#include "../movers/linear_mover.hxx" #include "../pingu.hxx" #include "../string_converter.hxx" +#include "../world.hxx" +#include "../worldobj.hxx" #include "faller.hxx" namespace Actions { -const float Faller::deadly_velocity = 20.0f; - Faller::Faller (Pingu* p) : PinguAction(p), faller(Sprite("Pingus/faller" + to_string(pingu->get_owner ()), "pingus")), @@ -49,47 +51,117 @@ tumbler.update(); faller.update(); - // Pingu stands on ground - if (rel_getpixel(0, -1) != Groundtype::GP_NOTHING) - { - pingu->set_action(Actions::Walker); - return; - } - // FIXME: This should be triggered at a later point, when close to // FIXME: deadly_velocity or something like that. A translation // FIXME: animation for the floater might also help if (pingu->get_velocity().y > 5.0 && pingu->request_fall_action()) return; - // Move the Faller according to the forces that currently exist, which - // includes gravity. - move_with_forces (); - - // Now that the Pingu is moved, check if he hits the ground. - // FIXME: shouldn't this be done by move_with_forces - if (rel_getpixel(0, -1) != Groundtype::GP_NOTHING) - { // Ping is on ground/water/something - if ( rel_getpixel(0, -1) == Groundtype::GP_WATER - || rel_getpixel(0, -1) == Groundtype::GP_LAVA) - { - pingu->set_action(Actions::Drown); - return; - } - else + float gravity = WorldObj::get_world()->get_gravity(); + Vector old_velocity = pingu->get_velocity(); + + // Apply gravity + pingu->set_velocity(old_velocity + Vector (0.0f, gravity)); + Vector new_velocity = pingu->get_velocity(); + + // Use equation [s = ut + 0.5at^2]. As t = 1 update in this case, + // [s = u + 0.5a]. Pingu used to use [s = u + a]. This resulted in a + // model where only acceleration only occurred at integer update intervals + // (i.e. 1, 2, 3...). Acceleration in between the updates was not modelled. + // Using the proper equation enables acceleration in between the updates to + // be modelled. + Vector move = old_velocity + Vector(0.0f, 0.5f * gravity); + + Vector old_pos = pingu->get_pos(); + + Movers::LinearMover mover(WorldObj::get_world(), old_pos); + + bool collided; + int pingu_action; + + do + { + // Move the Pingu as far is it can go + mover.update(move, Colliders::PinguCollider(pingu_height)); + + pingu->set_pos(mover.get_pos()); + + collided = mover.collided(); + + // If the Pingu collided with something... + if (collided) { - // Did we stop too fast? - if (fabs(pingu->get_velocity().y) > deadly_velocity) + move = mover.remaining(); + + int collision_pixel = mover.collided_with(); + + if (collision_pixel == Groundtype::GP_WATER + || collision_pixel == Groundtype::GP_LAVA) { - pingu->set_action(Actions::Splashed); - return; + pingu->set_action(Actions::Drown); } - else if (fabs(pingu->get_velocity().x) > deadly_velocity) + // If the Pingu collided into something while moving down... + else if (new_velocity.y > 0.0f + && rel_getpixel(0, -1) != Groundtype::GP_NOTHING) { - pout(PINGUS_DEBUG_ACTIONS) << "Pingu: x Smashed on ground, jumping" << std::endl; + float dist_to_ground = pingu->get_pos().y - old_pos.y; + + // Use equation [v^2 = u^2 + 2as] to work out at what velocity the + // Pingu hit the ground. + if (deadly_velocity * deadly_velocity <= + old_velocity.y * old_velocity.y + + 2.0f * gravity * dist_to_ground) + { + pingu->set_action(Actions::Splashed); + } + else + { + // Round down the Pingu's y pos otherwise a Pingu with y = 1.9 + // and another with y = 1.0 both on the same platform could + // react differently when falling off it. + pingu->set_pos(Vector(pingu->get_pos().x, + static_cast(pingu->get_pos().y)) ); + + // Pingu has safely collided with the ground + pingu->set_action(Actions::Walker); + + // Reset the velocity of the Pingu. + new_velocity.x = 0.0f; + new_velocity.y = 0.0f; + } + } + // If the Pingu collided into something while moving up... + // NB: +1 because Mover backs out of something it has collided with. + else if (new_velocity.y < 0.0f + && rel_getpixel(0, pingu_height + 1) != Groundtype::GP_NOTHING) + { + // Don't make the Pingu go up any further. + move.y = 0.0f; + new_velocity.y = 0.0f; + } + else + { + // Make Pingu bounce off wall + move.x = -(move.x / 3.0f); + new_velocity.x = -(new_velocity.x / 3.0f); + + // Make the Pingu face the correct direction. NB: Pingu may + // previously have been facing in the opposite direction of its + // velocity because of an explosion. + if (new_velocity.x > 0.0f) + pingu->direction.right(); + else + pingu->direction.left(); } } + + // Update the Pingu's velocity + pingu->set_velocity(new_velocity); + + pingu_action = pingu->get_action(); } + // Loop if the Pingu still needs to be moved by the Faller code + while (collided && pingu_action == Actions::Faller); } void Index: ./Games/Pingus/src/actions/jumper.cxx =================================================================== RCS file: /var/lib/cvs/Games/Pingus/src/actions/jumper.cxx,v retrieving revision 1.18 diff -u -r1.18 jumper.cxx --- ./Games/Pingus/src/actions/jumper.cxx 19 Feb 2003 09:50:36 -0000 1.18 +++ ./Games/Pingus/src/actions/jumper.cxx 5 Apr 2003 23:43:15 -0000 @@ -17,10 +17,13 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#include #include "../vector.hxx" #include "../gui/graphic_context.hxx" #include "../pingu.hxx" #include "../string_converter.hxx" +#include "../world.hxx" +#include "../worldobj.hxx" #include "jumper.hxx" namespace Actions { @@ -41,10 +44,14 @@ void Jumper::update () { + // Using v^2 = u^2 + 2as gives us v = sqrt(2sa). In this case, sqrt(2s) is + // 10. + float velocity = 10.0f * sqrt(WorldObj::get_world()->get_gravity()); + if (pingu->direction.is_right()) { - pingu->set_velocity(pingu->get_velocity() + Vector(10.0, -10.0)); + pingu->set_velocity(pingu->get_velocity() + Vector(velocity, -velocity)); } else { - pingu->set_velocity(pingu->get_velocity() + Vector(-10.0, -10.0)); + pingu->set_velocity(pingu->get_velocity() + Vector(-velocity, -velocity)); } // Move the pingu in the air, so that it can start 'falling' Index: ./Games/Pingus/src/actions/walker.cxx =================================================================== RCS file: /var/lib/cvs/Games/Pingus/src/actions/walker.cxx,v retrieving revision 1.32 diff -u -r1.32 walker.cxx --- ./Games/Pingus/src/actions/walker.cxx 4 Mar 2003 11:26:18 -0000 1.32 +++ ./Games/Pingus/src/actions/walker.cxx 5 Apr 2003 23:43:16 -0000 @@ -64,7 +64,12 @@ */ - if (rel_getpixel(0, -1) == Groundtype::GP_WATER) + if (rel_getpixel(0, -1) == Groundtype::GP_WATER + || rel_getpixel(0, -1) == Groundtype::GP_LAVA + || rel_getpixel(1, 0) == Groundtype::GP_WATER + || rel_getpixel(1, 0) == Groundtype::GP_LAVA + || rel_getpixel(1, pingu_height) == Groundtype::GP_WATER + || rel_getpixel(1, pingu_height) == Groundtype::GP_LAVA) { pingu->set_action(Actions::Drown); return; Index: ./Games/Pingus/src/mover.cxx =================================================================== RCS file: /var/lib/cvs/Games/Pingus/src/mover.cxx,v retrieving revision 1.2 diff -u -r1.2 mover.cxx --- ./Games/Pingus/src/mover.cxx 9 Mar 2003 20:41:30 -0000 1.2 +++ ./Games/Pingus/src/mover.cxx 5 Apr 2003 23:43:17 -0000 @@ -31,6 +31,13 @@ { } +void Mover::operator() (const bool& collided_arg, + const int& collision_pixel_arg) +{ + collision = collided_arg; + collision_pixel = collision_pixel_arg; +} + Vector Mover::get_pos() const { return pos; @@ -44,6 +51,11 @@ bool Mover::collided() const { return collision; +} + +int Mover::collided_with() const +{ + return collision_pixel; } /* EOF */ Index: ./Games/Pingus/src/mover.hxx =================================================================== RCS file: /var/lib/cvs/Games/Pingus/src/mover.hxx,v retrieving revision 1.2 diff -u -r1.2 mover.hxx --- ./Games/Pingus/src/mover.hxx 9 Mar 2003 20:41:30 -0000 1.2 +++ ./Games/Pingus/src/mover.hxx 5 Apr 2003 23:43:17 -0000 @@ -34,6 +34,10 @@ /** Destructor of abstract class */ virtual ~Mover() = 0; + /** Sets the status variables of the mover. Crucially used by Colliders. */ + void operator() (const bool& collided_arg, + const int& collision_pixel_arg); + /** Updates the position of the object taking into account collisions */ virtual void update(const Vector& move, const Collider& collider) = 0; @@ -46,6 +50,9 @@ /** Get whether object stopped moving because it collided with something */ bool collided() const; + /** Get the "pixel" that the object collided with */ + int collided_with() const; + protected: /** World in which the object should move */ World* const world; @@ -56,8 +63,12 @@ /** Move vector remaining after a collision */ Vector remaining_move; + private: /** Flag to denote whether object has had a collision */ bool collision; + + /** The "pixel" that the object collided with */ + int collision_pixel; }; #endif Index: ./Games/Pingus/src/pingu_enums.cxx =================================================================== RCS file: /var/lib/cvs/Games/Pingus/src/pingu_enums.cxx,v retrieving revision 1.4 diff -u -r1.4 pingu_enums.cxx --- ./Games/Pingus/src/pingu_enums.cxx 25 Mar 2003 00:56:33 -0000 1.4 +++ ./Games/Pingus/src/pingu_enums.cxx 5 Apr 2003 23:43:17 -0000 @@ -19,9 +19,10 @@ #include "pingu_enums.hxx" -// Pingu "globals". Make [deadly_velocity = 20 * sqrt("normal gravity")] so -// that the "deadly distance" is the same and therefore doesn't break levels. -const float deadly_velocity = 10.0f; +// Pingu "globals". Make deadly_velocity above sqrt(2 * 210 pixels * "normal +// gravity") and below sqrt(2 * 211 pixels * "normal gravity") so that the +// "deadly distance" is the same and therefore doesn't break levels. +const float deadly_velocity = 10.26f; const int pingu_height = 26; namespace Actions { Index: ./Games/Pingus/src/collider.cxx =================================================================== RCS file: /var/lib/cvs/Games/Pingus/src/collider.cxx,v retrieving revision 1.3 diff -u -r1.3 collider.cxx --- ./Games/Pingus/src/collider.cxx 18 Mar 2003 17:03:01 -0000 1.3 +++ ./Games/Pingus/src/collider.cxx 5 Apr 2003 23:43:18 -0000 @@ -30,14 +30,13 @@ { } -bool Collider::operator() (World* const world, Vector current_pos, +void Collider::operator() (Mover& mover, World* const world, + const Vector& current_pos, const Vector& step_vector) const { UNUSED_ARG(world); UNUSED_ARG(current_pos); UNUSED_ARG(step_vector); - - return false; } int Collider::getpixel(World* const world, const Vector& pos) const Index: ./Games/Pingus/src/collider.hxx =================================================================== RCS file: /var/lib/cvs/Games/Pingus/src/collider.hxx,v retrieving revision 1.3 diff -u -r1.3 collider.hxx --- ./Games/Pingus/src/collider.hxx 18 Mar 2003 17:03:01 -0000 1.3 +++ ./Games/Pingus/src/collider.hxx 5 Apr 2003 23:43:18 -0000 @@ -20,6 +20,7 @@ #ifndef HEADER_PINGUS_COLLIDER_HXX #define HEADER_PINGUS_COLLIDER_HXX +class Mover; class Vector; class World; @@ -33,8 +34,10 @@ virtual ~Collider() = 0; /** Find out if object will collide with something */ - virtual bool operator() (World* const world, Vector current_pos, - const Vector& step_vector) const = 0; + virtual void operator() (Mover& mover, World* const world, + const Vector& current_pos, + const Vector& step_vector) + const = 0; protected: /** Get the Collision Map pixel at the specified position in the specified Index: ./Games/Pingus/src/colliders/pingu_collider.cxx =================================================================== RCS file: /var/lib/cvs/Games/Pingus/src/colliders/pingu_collider.cxx,v retrieving revision 1.3 diff -u -r1.3 pingu_collider.cxx --- ./Games/Pingus/src/colliders/pingu_collider.cxx 18 Mar 2003 17:03:02 -0000 1.3 +++ ./Games/Pingus/src/colliders/pingu_collider.cxx 5 Apr 2003 23:43:19 -0000 @@ -17,6 +17,7 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#include "../mover.hxx" #include "../pingu_action.hxx" #include "../groundtype.hxx" #include "../vector.hxx" @@ -32,55 +33,57 @@ { } -bool PinguCollider::operator() (World* const world, Vector current_pos, +void PinguCollider::operator() (Mover& mover, World* const world, + const Vector& current_pos, const Vector& step_vector) const { - Vector new_pos = current_pos + step_vector; - int pixel; bool falling = false; - bool collided = false; if (step_vector.y > 0.0f) falling = true; - // If the Pingu is going to move sideways to the next pixel... - if (static_cast(new_pos.x) != static_cast(current_pos.x)) + Vector check_pos = current_pos + step_vector; + float final_check_pos_y = check_pos.y - height; + + // If the Pingu is not moving sideways when going to the next pixel... + if (static_cast(check_pos.x) == static_cast(current_pos.x)) { - float top_of_pingu = new_pos.y - height; + // ...make loop only check the top or bottom pixel of the Pingu + if (falling) + final_check_pos_y = check_pos.y; + else + check_pos.y = final_check_pos_y; + } - for (; new_pos.y >= top_of_pingu; --new_pos.y) - { - pixel = getpixel(world, new_pos); + int colmap_pixel; + int collision_pixel; + bool collided = false; - // If there is something in the way, then Pingu has collided with - // something. However, if not falling and colliding with a - // Bridge, allow Pingu to go through it. - if ((!falling || pixel != Groundtype::GP_BRIDGE) - && pixel != Groundtype::GP_NOTHING) - { - collided = true; - break; - } - } - } - // If the Pingu is not falling... - else if (!falling) + for (; check_pos.y >= final_check_pos_y; --check_pos.y) { - pixel = getpixel(world, Vector(new_pos.x, new_pos.y - height)); + colmap_pixel = getpixel(world, check_pos); - // If the top of the Pingu has hit something except a bridge... - if (pixel != Groundtype::GP_NOTHING && pixel != Groundtype::GP_BRIDGE) + // If there is something in the way, then Pingu has collided with + // something. However, if not falling and colliding with a + // Bridge, allow Pingu to go through it. + if ((!falling || colmap_pixel != Groundtype::GP_BRIDGE) + && colmap_pixel != Groundtype::GP_NOTHING) { collided = true; + collision_pixel = colmap_pixel; + + // If a deadly pixel is found... + if (colmap_pixel == Groundtype::GP_WATER + || colmap_pixel == Groundtype::GP_LAVA) + { + // ...get out of the loop. + break; + } } } - // If the Pingu's "feet" has hit something... - else if (getpixel(world, new_pos) != Groundtype::GP_NOTHING) - { - collided = true; - } - return collided; + // Pass back information to the mover. + mover(collided, collision_pixel); } } // namespace Colliders Index: ./Games/Pingus/src/colliders/pingu_collider.hxx =================================================================== RCS file: /var/lib/cvs/Games/Pingus/src/colliders/pingu_collider.hxx,v retrieving revision 1.3 diff -u -r1.3 pingu_collider.hxx --- ./Games/Pingus/src/colliders/pingu_collider.hxx 18 Mar 2003 17:03:02 -0000 1.3 +++ ./Games/Pingus/src/colliders/pingu_collider.hxx 5 Apr 2003 23:43:19 -0000 @@ -35,8 +35,9 @@ /** Find out if a Pingu at the specified position is colliding with something */ - bool operator() (World* const world, Vector current_pos, - const Vector& step_vector) const; + void operator() (Mover& mover, World* const world, + const Vector& current_pos, const Vector& step_vector) + const; private: /** Pingu could be on its belly. Therefore, this is the current height of Index: ./Games/Pingus/src/movers/linear_mover.cxx =================================================================== RCS file: /var/lib/cvs/Games/Pingus/src/movers/linear_mover.cxx,v retrieving revision 1.3 diff -u -r1.3 linear_mover.cxx --- ./Games/Pingus/src/movers/linear_mover.cxx 18 Mar 2003 17:03:02 -0000 1.3 +++ ./Games/Pingus/src/movers/linear_mover.cxx 5 Apr 2003 23:43:19 -0000 @@ -19,6 +19,7 @@ #include "linear_mover.hxx" #include "../collider.hxx" +#include "../groundtype.hxx" namespace Movers { @@ -42,18 +43,23 @@ // Make the step vector (i.e. change to a unit vector) step_vector.normalize(); - collision = false; + // Reset the status of the mover. + (*this)(false, Groundtype::GP_NOTHING); // Move to the destination one unit vector at a time - for (int i = 0; i < move_length && !collision; ++i) + for (int i = 0; i <= move_length && !collided(); ++i) { - collision = collision_check(world, pos, step_vector); + // If a fraction of a move is left... + if (i == move_length) + step_vector = target_pos - pos; + + collision_check(*this, world, pos, step_vector); pos += step_vector; } // If on a collision pixel, back away from it. - if (collision) + if (collided()) pos -= step_vector; remaining_move = target_pos - pos;