- Yet another try in the endless quest for perfect collision detection.
authorMatthias Braun <matze@braunis.de>
Fri, 31 Mar 2006 01:18:10 +0000 (01:18 +0000)
committerMatthias Braun <matze@braunis.de>
Fri, 31 Mar 2006 01:18:10 +0000 (01:18 +0000)
   This version seems to be working quiet good. Only bug I could find was when
   jumping on skull tiles that are jumping themselfes at the same moment...
- Change paths to be no game objects anymore
- slightly changed path syntax

SVN-Revision: 3139

36 files changed:
data/images/objects/platforms/big.png [new file with mode: 0644]
data/images/objects/platforms/big.sprite [new file with mode: 0644]
data/images/objects/platforms/small.png [new file with mode: 0644]
data/images/objects/platforms/small.sprite [new file with mode: 0644]
data/images/objects/trampoline/trampoline.sprite
data/images/tiles/doodads/iceshrub.sprite
data/levels/test/light.stl
data/levels/test/platform.stl
data/levels/test/simple.stl
data/levels/test/yeti.stl
data/levels/world2/christoph2.stl
data/levels/world2/level2.stl
src/badguy/badguy.cpp
src/badguy/stalactite.cpp
src/collision.hpp
src/collision_hit.hpp
src/game_session.cpp
src/level.cpp
src/level.hpp
src/math/vector.hpp
src/moving_object.hpp
src/object/camera.cpp
src/object/camera.hpp
src/object/path.cpp
src/object/path.hpp
src/object/platform.cpp
src/object/platform.hpp
src/object/player.cpp
src/object/player.hpp
src/object/powerup.cpp
src/object/rock.cpp
src/physic.cpp
src/physic.hpp
src/sector.cpp
src/sector.hpp
src/title.cpp

diff --git a/data/images/objects/platforms/big.png b/data/images/objects/platforms/big.png
new file mode 100644 (file)
index 0000000..c53d32b
Binary files /dev/null and b/data/images/objects/platforms/big.png differ
diff --git a/data/images/objects/platforms/big.sprite b/data/images/objects/platforms/big.sprite
new file mode 100644 (file)
index 0000000..dda1e87
--- /dev/null
@@ -0,0 +1,6 @@
+(supertux-sprite
+  (action
+    (name "default")
+    (images "big.png")
+  )
+)
diff --git a/data/images/objects/platforms/small.png b/data/images/objects/platforms/small.png
new file mode 100644 (file)
index 0000000..880915b
Binary files /dev/null and b/data/images/objects/platforms/small.png differ
diff --git a/data/images/objects/platforms/small.sprite b/data/images/objects/platforms/small.sprite
new file mode 100644 (file)
index 0000000..2c00179
--- /dev/null
@@ -0,0 +1,6 @@
+(supertux-sprite
+  (action
+    (name "default")
+    (images "small.png")
+  )
+)
index f7775be..4b9e58d 100644 (file)
@@ -1,5 +1,6 @@
 (supertux-sprite
  (action
+  (name "normal")
   (x-offset 0)
   (y-offset 0)
   (images "trampoline1-0.png"
index 35b4c9e..03c1227 100644 (file)
@@ -1,5 +1,6 @@
 (supertux-sprite
  (action
+  (name "normal")
   (images "iceshrub.png")
  )
 )
index c19971f..a880c45 100644 (file)
@@ -78,6 +78,6 @@
   (mriceblock (x 1180) (y 15) (stay-on-platform #f))
   (mriceblock (x 939) (y 22) (stay-on-platform #f))
   (mriceblock (x 703) (y 25) (stay-on-platform #f))
-  (stalactite (x 322) (y 287) (stay-on-platform #f))
+  (stalactite (x 322) (y 288) (stay-on-platform #f))
 )
 )
index c20008d..e25eb38 100644 (file)
          (speed 0.500000)
        )
 
-   (path 
-     (name "path1") 
-     (circular #t) 
-     (nodes 
-       (node (x 0)   (y 0)) 
-       (node (x 0)   (y 0)) 
-       (node (x 0)   (y -100)) 
-       (node (x -100)(y -100)) 
-       )
+   (platform 
+     (path
+       (mode "pingpong") 
+       (node (x 200)    (y 850)) 
+       (node (x 200)    (y 750))
+       (node (x 100)    (y 750))
      )
-   (path 
-     (name "path2") 
-     (circular #t) 
-     (nodes 
-       (node (x 0)   (y 0)    (time 1)) 
-       (node (x 0)   (y 0)    (time 1)) 
-       (node (x 0)   (y -100) (time 1)) 
-       (node (x 0)   (y -100) (time 1)) 
-       )
+     (sprite "images/objects/platforms/small.sprite")
+   )
+   (platform
+     (path
+       (mode "pingpong")  
+       (node (x 264)   (y 850)    (time 1)) 
+       (node (x 264)   (y 850)    (time 1)) 
+       (node (x 264)   (y 750) (time 1)) 
+       (node (x 264)   (y 750) (time 1))
      )
-   (path 
-     (name "path3") 
-     (circular #t) 
-     (nodes 
-       (node (x 0)   (y 0)    (time 1)) 
-       (node (x 0)   (y 0)    (time 1)) 
-       (node (x 0)   (y -100) (time 2)) 
-       )
+     (sprite "images/objects/platforms/small.sprite")
+   )
+   (platform
+     (path
+       (mode "circular")
+       (node (x 328)   (y 850)    (time 1)) 
+       (node (x 328)   (y 850)    (time 1)) 
+       (node (x 328)   (y 750) (time 2))
      )
-   (path 
-     (name "path4") 
-     (circular #t) 
-     (nodes 
-       (node (x 0)   (y 0)    (time 1)) 
-       (node (x 0)   (y -100) (time 1)) 
-       (node (x 0)   (y -200) (time 1)) 
-       (node (x 0)   (y -100) (time 1)) 
-       )
+     (sprite "images/objects/platforms/small.sprite")
+   )
+   (platform
+     (path
+       (mode "circular")
+       (node (x 392)   (y 850)    (time 1)) 
+       (node (x 392)   (y 750) (time 1)) 
+       (node (x 392)   (y 650) (time 0.6)) 
+       (node (x 392)   (y 750) (time 1)) 
      )
-   (path 
-     (name "path5") 
-     (circular #f) 
-     (nodes 
-       (node)
-       (node (y -100))
-       (node (x 100))
-       (node (y 0))
-       (node (x 0))
-       )
+     (sprite "images/objects/platforms/small.sprite")
+   )
+   (platform
+     (path
+       (mode "circular")
+       (node (x 456) (y 750))
+       (node (x 100) (y 300))
+       (node (x 456) (y 300))
+       (node (x 456) (y 750))
      )
-
-   (platform (path "path1") (x 200) (y 850) (type "block1"))
-   (platform (path "path1") (x 232) (y 850) (type "block3"))   
-   (platform (path "path2") (x 264) (y 850) (type "block1"))
-   (platform (path "path2") (x 296) (y 850) (type "block3"))
-   (platform (path "path3") (x 328) (y 850) (type "block1"))
-   (platform (path "path3") (x 360) (y 850) (type "block3"))
-   (platform (path "path4") (x 392) (y 850) (type "block1"))
-   (platform (path "path4") (x 424) (y 850) (type "block3"))
-   (platform (path "path5") (x 456) (y 850) (type "block1"))
-   (platform (path "path5") (x 488) (y 850) (type "block3"))
+     (sprite "images/objects/platforms/small.sprite")
+   )
    (powerup (x 100) (y 700) (sprite "images/powerups/egg/egg.sprite"))
    (jumpy (x 140) (y 750))
    (spawnpoint (name "main") (x 340) (y 800))
index c16c763..ca9065d 100644 (file)
@@ -11,7 +11,7 @@
                 (speed 0.5))
     (spawnpoint (name "main") (x 150) (y 100))
     (rock (x 50) (y 50))
-    ;(rock (x 50) (y 100))
+    (rock (x 50) (y 100))
     ;(rock (x 50) (y 150))
     (tilemap
       (layer  "background")
index 836d65b..d909f4a 100644 (file)
@@ -122,25 +122,25 @@ Level.finish();
         )
        (particles-snow
        )
-       (yeti_stalactite (x 97) (y 46))
-       (yeti_stalactite (x 128) (y 47))
-       (yeti_stalactite (x 161) (y 47))
-       (yeti_stalactite (x 191) (y 47))
-       (yeti_stalactite (x 223) (y 48))
-       (yeti_stalactite (x 255) (y 49))
-       (yeti_stalactite (x 288) (y 48))
-       (yeti_stalactite (x 321) (y 49))
-       (yeti_stalactite (x 352) (y 49))
-       (yeti_stalactite (x 384) (y 49))
-       (yeti_stalactite (x 417) (y 49))
-       (yeti_stalactite (x 576) (y 49))
-       (yeti_stalactite (x 545) (y 51))
-       (yeti_stalactite (x 512) (y 50))
-       (yeti_stalactite (x 479) (y 49))
-       (yeti_stalactite (x 448) (y 49))
-       (yeti_stalactite (x 607) (y 49))
-       (yeti_stalactite (x 639) (y 49))
-       (yeti_stalactite (x 671) (y 49))
+       (yeti_stalactite (x 97) (y 32))
+       (yeti_stalactite (x 128) (y 32))
+       (yeti_stalactite (x 161) (y 32))
+       (yeti_stalactite (x 191) (y 32))
+       (yeti_stalactite (x 223) (y 32))
+       (yeti_stalactite (x 255) (y 32))
+       (yeti_stalactite (x 288) (y 32))
+       (yeti_stalactite (x 321) (y 32))
+       (yeti_stalactite (x 352) (y 32))
+       (yeti_stalactite (x 384) (y 32))
+       (yeti_stalactite (x 417) (y 32))
+       (yeti_stalactite (x 576) (y 32))
+       (yeti_stalactite (x 545) (y 32))
+       (yeti_stalactite (x 512) (y 32))
+       (yeti_stalactite (x 479) (y 32))
+       (yeti_stalactite (x 448) (y 32))
+       (yeti_stalactite (x 607) (y 32))
+       (yeti_stalactite (x 639) (y 32))
+       (yeti_stalactite (x 671) (y 32))
    )
  )
 
index f2fad15..15f9563 100644 (file)
       (x 2674)
       (y 1242)
     )
-    (platform (path "path1") (x 1984) (y 1568) (type "block1"))
-    (platform (path "path1") (x 2016) (y 1568) (type "block2"))
-    (platform (path "path1") (x 2048) (y 1568) (type "block2"))
-    (platform (path "path1") (x 2080) (y 1568) (type "block3"))
-    (platform (path "path2") (x 1856) (y 736) (type "block1"))
-    (platform (path "path2") (x 1888) (y 736) (type "block2"))
-    (platform (path "path2") (x 1920) (y 736) (type "block2"))
-    (platform (path "path2") (x 1952) (y 736) (type "block3"))
-    (platform (path "path3") (x 896) (y 736) (type "block1"))
-    (platform (path "path3") (x 928) (y 736) (type "block2"))
-    (platform (path "path3") (x 960) (y 736) (type "block2"))
-    (platform (path "path3") (x 992) (y 736) (type "block3"))
+    (platform
+      (sprite "images/objects/platforms/big.sprite")
+      (path
+        (mode "circular")
+        (node (x 1984)   (y 1568) (time 1)) 
+        (node (x 1984)   (y 1568) (time 3)) 
+        (node (x 1984)   (y 1152) (time 1)) 
+        (node (x 1984)   (y 1152) (time 1)) 
+      )
+    )
+    (platform
+      (sprite "images/objects/platforms/big.sprite")
+      (path
+        (mode "circular")
+        (node (x 1856)   (y 736)  (time 1)) 
+        (node (x 1856)   (y 736)  (time 3)) 
+        (node (x 1856)   (y 1152) (time 1)) 
+        (node (x 1856)   (y 1152) (time 1)) 
+      )
+    )
+    (platform
+      (sprite "images/objects/platforms/big.sprite")
+      (path
+        (mode "circular")
+        (node (x 896)   (y 736) (time 1)) 
+        (node (x 896)   (y 736) (time 2)) 
+        (node (x 896)   (y 288) (time 1)) 
+        (node (x 896)   (y 288) (time 1)) 
+      )
+    )
   )
 )
index d7cebdb..008c12c 100644 (file)
                     (message (_ "Drink me")))
        
         (powerup (x 3168) (y 416)
-                 (sprite "eat-me")
+                 (sprite "images/objects/eat-me/eat-me.sprite")
                  (script "levelflip();")       
         )
       (bonusblock (x 672) (y 1056)
index 85d2895..5cbcf1a 100644 (file)
@@ -177,7 +177,7 @@ BadGuy::collision_player(Player& player, const CollisionHit& )
       (get_bbox().p1.y + get_bbox().p2.y) / 2) {
     // if it's not possible to squish us, then this will hurt
     if(collision_squished(player))
-      return CONTINUE;
+      return ABORT_MOVE;
   }
 
   player.kill(Player::SHRINK);
index ab4fc4d..a058c18 100644 (file)
@@ -76,7 +76,7 @@ Stalactite::collision_solid(GameObject& , const CollisionHit& hit)
   if(state != STALACTITE_FALLING && state != STALACTITE_SQUISHED)
     return FORCE_MOVE;
   
-  if(fabsf(hit.normal.y) > .5) { // hit floor or roof?
+  if(hit.normal.y < .9) { // hit floor?
     state = STALACTITE_SQUISHED;
     set_group(COLGROUP_MOVING_ONLY_STATIC);
     physic.set_velocity_y(0);
index d32de1b..360f94d 100644 (file)
@@ -41,7 +41,7 @@ public:
    * Returns true in case of a collision and fills in the hit structure then.
    */                                                                         
   static bool rectangle_aatriangle(CollisionHit& hit, const Rect& rect,
-      const Vector& movement, const AATriangle& triangle);                                            
+      const Vector& movement, const AATriangle& triangle);
 };
 
 #endif
index 2044603..81547d8 100644 (file)
@@ -33,7 +33,8 @@ enum HitResponse
   /// if this happens to often then the move will just be aborted    
   CONTINUE,
   /// do the move ignoring the collision
-  FORCE_MOVE
+  FORCE_MOVE,
+  TEST
 };
 
 /**
index f495bec..e96f821 100644 (file)
@@ -721,19 +721,19 @@ GameSession::start_sequence(const std::string& sequencename)
   if(sequencename == "endsequence" || sequencename == "fireworks") {
     if(end_sequence)
       return;
-    
+
     end_sequence = ENDSEQUENCE_RUNNING;
-    endsequence_timer.start(level->extro_length);
+    endsequence_timer.start(7.3);
     last_x_pos = -1;
-    sound_manager->play_music("music/" + level->extro_music, false);
-    currentsector->player->invincible_timer.start(level->extro_length);
+    sound_manager->play_music("music/leveldone.ogg", false);
+    currentsector->player->invincible_timer.start(7.3);
 
     // Stop all clocks.
     for(std::vector<GameObject*>::iterator i = currentsector->gameobjects.begin();
         i != currentsector->gameobjects.end(); ++i)
     {
       GameObject* obj = *i;
-      
+
       LevelTime* lt = dynamic_cast<LevelTime*> (obj);
       if(lt)
         lt->stop();
index f589380..57bc4a7 100644 (file)
@@ -52,7 +52,7 @@
 using namespace std;
 
 Level::Level()
-  : name("noname"), author("Mr. X"), extro_music("leveldone.ogg"), extro_length(7.0)
+  : name("noname"), author("Mr. X")
 {
 }
 
@@ -86,17 +86,6 @@ Level::load(const std::string& filepath)
         iter.value()->get(name);
       } else if(token == "author") {
         iter.value()->get(author);
-      } else if(token == "extro") {
-        const lisp::Lisp* ext = iter.lisp();
-        lisp::ListIterator ext_iter(ext);
-        while(ext_iter.next()) {
-          const std::string& ext_token = ext_iter.item();
-          if(ext_token == "music") {
-            ext_iter.value()->get(extro_music);
-          } else if(ext_token == "length") {
-            ext_iter.value()->get(extro_length);
-          }
-        }
       } else if(token == "sector") {
         Sector* sector = new Sector;
         sector->parse(*(iter.lisp()));
index 2175b3c..18d1803 100644 (file)
@@ -35,8 +35,6 @@ class Level
 public:
   std::string name;
   std::string author;
-  std::string extro_music;
-  float extro_length;
   typedef std::vector<Sector*> Sectors;
   Sectors sectors;
 
index 38c1204..6882cf1 100644 (file)
@@ -82,6 +82,13 @@ public:
     return *this;
   }
 
+  const Vector& operator -=(const Vector& other)
+  {
+    x -= other.x;
+    y -= other.y;
+    return *this;
+  }
+
   const Vector& operator *=(float val)
   {
     x *= val;
index 79e415b..a58bc75 100644 (file)
@@ -110,6 +110,10 @@ protected:
   Vector movement;
   /** The collision group */
   CollisionGroup group;
+
+private:
+  // this is only here for internal collision detection use
+  Rect dest;
 };
 
 #endif
index 8a4a9c0..c1077cc 100644 (file)
@@ -33,6 +33,8 @@
 #include "main.hpp"
 #include "object_factory.hpp"
 #include "msg.hpp"
+#include "path.hpp"
+#include "path_walker.hpp"
 
 Camera::Camera(Sector* newsector)
   : sector(newsector), do_backscrolling(true), scrollchange(NONE)
@@ -63,15 +65,14 @@ Camera::parse(const lisp::Lisp& reader)
     reader.get("backscrolling", do_backscrolling);
   } else if(modename == "autoscroll") {
     mode = AUTOSCROLL;
-    std::string use_path;
-    
-    if (!reader.get("path", use_path)) throw std::runtime_error("No path specified in autoscroll camera.");
 
-    autoscrollPath = Path::GetByName(use_path);
-    if (autoscrollPath == NULL) { 
-      msg_warning("Path for autoscroll camera not found! Make sure that the name is spelled correctly and that the path is initialized before the platform in the level file!");
-    }
+    const lisp::Lisp* pathLisp = reader.get_lisp("path");
+    if(pathLisp == NULL)
+      throw std::runtime_error("No path specified in autoscroll camera.");
 
+    autoscroll_path.reset(new Path());
+    autoscroll_path->read(*pathLisp);
+    autoscroll_walker.reset(new PathWalker(autoscroll_path.get()));
   } else if(modename == "manual") {
     mode = MANUAL;
   } else {
@@ -91,7 +92,7 @@ Camera::write(lisp::Writer& writer)
     writer.write_bool("backscrolling", do_backscrolling);
   } else if(mode == AUTOSCROLL) {
     writer.write_string("mode", "autoscroll");
-    writer.write_string("path", autoscrollPath->GetName());
+    autoscroll_path->write(writer);
   } else if(mode == MANUAL) {
     writer.write_string("mode", "manual");
   }
@@ -141,7 +142,7 @@ Camera::update(float elapsed_time)
       update_scroll_normal(elapsed_time);
       break;
     case AUTOSCROLL:
-      update_scroll_autoscroll();
+      update_scroll_autoscroll(elapsed_time);
       break;
     case SCROLLTO:
       update_scroll_to(elapsed_time);
@@ -269,14 +270,13 @@ Camera::update_scroll_normal(float elapsed_time)
 }
 
 void
-Camera::update_scroll_autoscroll()
+Camera::update_scroll_autoscroll(float elapsed_time)
 {
-  Player* player = sector->player;
-  
+  Player* player = sector->player; 
   if(player->is_dying())
     return;
 
-  translation = autoscrollPath->GetPosition();
+  translation += autoscroll_walker->advance(elapsed_time);
 
   keep_in_bounds(translation);
   shake();
index b310822..0be750e 100644 (file)
 
 #include <vector>
 #include <cassert>
+#include <memory>
 
 #include "math/vector.hpp"
 #include "game_object.hpp"
 #include "video/drawing_context.hpp"
 #include "serializable.hpp"
 #include "timer.hpp"
-#include "object/path.hpp"
 
 namespace lisp {
 class Lisp;
 }
 
 class Sector;
+class Path;
+class PathWalker;
 
 class Camera : public GameObject, public Serializable
 {
@@ -81,7 +83,7 @@ public:
 
 private:
   void update_scroll_normal(float elapsed_time);
-  void update_scroll_autoscroll();
+  void update_scroll_autoscroll(float elapsed_time);
   void update_scroll_to(float elapsed_time);
   void keep_in_bounds(Vector& vector);
   void shake();
@@ -100,7 +102,8 @@ private:
   LeftRightScrollChange scrollchange;
 
   // autoscroll mode
-  Path* autoscrollPath;
+  std::auto_ptr<Path> autoscroll_path;
+  std::auto_ptr<PathWalker> autoscroll_walker;
 
   // shaking
   Timer shaketimer;
index c16f1de..e4c415e 100644 (file)
@@ -3,6 +3,7 @@
 //  SuperTux Path
 //  Copyright (C) 2005 Philipp <balinor@pnxs.de>
 //  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
 //
 //  This program is free software; you can redistribute it and/or
 //  modify it under the terms of the GNU General Public License
@@ -18,6 +19,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 <config.h>
 
 #include "path.hpp"
 
 #include <assert.h>
 #include <iostream>
 #include <stdexcept>
+#include <sstream>
 
-// snap to destination if within EPSILON pixels
-#define EPSILON 1.5
-
-Path::Path(const lisp::Lisp& reader)
+Path::Path()
 {
-  circular = true;
-  forward = true;
-
-  if (!reader.get("name", name)) throw std::runtime_error("Path without name");
-  reader.get("circular", circular);
-  reader.get("forward", forward);
-
-  const lisp::Lisp* nodes_lisp = reader.get_lisp("nodes");
-  if(!nodes_lisp) throw std::runtime_error("Path without nodes");
+}
 
-  lisp::ListIterator iter(nodes_lisp);
+Path::~Path()
+{
+}
 
-  PathNode node;
-  node.time = 1;
+void
+Path::read(const lisp::Lisp& reader)
+{
+  lisp::ListIterator iter(&reader);
 
+  mode = CIRCULAR;
   while(iter.next()) {
+    if(iter.item() == "mode") {
+      std::string mode_string;
+      if(!iter.value()->get(mode_string))
+        throw new std::runtime_error("Pathmode not a string");
+
+      if(mode_string == "oneshot")
+        mode = ONE_SHOT;
+      else if(mode_string == "pingpong")
+        mode = PING_PONG;
+      else if(mode_string == "circular")
+        mode = CIRCULAR;
+      else {
+        std::ostringstream msg;
+        msg << "Unknown pathmode '" << mode_string << "' found";
+        throw new std::runtime_error(msg.str());
+      }
+      continue;
+    }
+    
     if(iter.item() != "node") {
       msg_warning("unknown token '" << iter.item() << "' in Path nodes list. Ignored.");
       continue;
@@ -58,108 +74,21 @@ Path::Path(const lisp::Lisp& reader)
     const lisp::Lisp* node_lisp = iter.lisp();
 
     // each new node will inherit all values from the last one
-    node_lisp->get("x", node.position.x);
-    node_lisp->get("y", node.position.y);
+    Node node;
+    node.time = 1;
+    if( (!node_lisp->get("x", node.position.x) ||
+          !node_lisp->get("y", node.position.y)))
+      throw new std::runtime_error("Path node without x and y coordinate specified");
     node_lisp->get("time", node.time);
 
-    if(node.time <= 0) throw std::runtime_error("Path node with non-positive time");
+    if(node.time <= 0)
+      throw std::runtime_error("Path node with non-positive time");
 
-    pathNodes.push_back(node);
-  }
-
-  if (pathNodes.size() < 1) throw std::runtime_error("Path with zero nodes");
-
-  // initial position and velocity will be set with the first update, as timeToGo is initialized to 0.
-  destinationNode = 0;
-
-  // register this path for lookup:
-  registry[name] = this;
-}
-
-Path::~Path()
-{
-  registry.erase(name);
-}
-
-       void
-Path::update(float elapsed_time)
-{
-
-  // TODO: carry excess time over to next node? This is how it was done in camera.cpp:
-  /*
-  if(auto_t - elapsed_time >= 0) {
-    translation += current_dir * elapsed_time;
-    auto_t -= elapsed_time;
-  } else {
-    // do the rest of the old movement
-    translation += current_dir * auto_t;
-    elapsed_time -= auto_t;
-    auto_t = 0;
-
-    // construct path for next point
-    if(auto_idx+1 >= scrollpoints.size()) {
-      keep_in_bounds(translation);
-      return;
-    }
-    Vector distance = scrollpoints[auto_idx+1].position 
-                      - scrollpoints[auto_idx].position;
-    current_dir = distance.unit() * scrollpoints[auto_idx].speed;
-    auto_t = distance.norm() / scrollpoints[auto_idx].speed;
-
-    // do movement for the remaining time
-    translation += current_dir * elapsed_time;
-    auto_t -= elapsed_time;
-    auto_idx++;
-  }
-  */
-
-  // advance to next node at scheduled time
-  if (timeToGo <= 0) {
-    position = pathNodes[destinationNode].position;
-
-    // set destinationNode to next node
-    if (forward) {
-      destinationNode++;
-      if (destinationNode >= (int)pathNodes.size()) {
-       if (circular) {
-         destinationNode = 0;
-       } else {
-         destinationNode = (int)pathNodes.size()-1;
-       }
-      }
-    } else {
-      destinationNode--;
-      if (destinationNode < 0) {
-       if (circular) {
-         destinationNode = (int)pathNodes.size()-1;
-       } else {
-         destinationNode = 0;
-       }
-      }
-    }
-
-    PathNode dn = pathNodes[destinationNode];
-    timeToGo = dn.time;
-    velocity = (dn.position - position) / timeToGo;
-  }
-
-  // move according to stored velocity
-  last_movement = velocity * elapsed_time;
-  position += last_movement;
-  timeToGo -= elapsed_time;
-
-  // stop when we arrive at our destination
-  PathNode dn = pathNodes[destinationNode];
-  if ((position - dn.position).norm() < EPSILON) {
-    velocity = Vector(0,0);
+    nodes.push_back(node);
   }
 
-}
-
-void
-Path::draw(DrawingContext& )
-{
-  // TODO: Add a visible flag, draw the path if true
+  if (nodes.empty())
+    throw std::runtime_error("Path with zero nodes");
 }
 
 void
@@ -167,12 +96,23 @@ Path::write(lisp::Writer& writer)
 {
   writer.start_list("path");
 
-  writer.write_string("name", name);
-  writer.write_bool("circular", circular);
-  writer.write_bool("forward", forward);
+  switch(mode) {
+    case ONE_SHOT:
+      writer.write_string("mode", "oneshot");
+      break;
+    case PING_PONG:
+      writer.write_string("mode", "pingpong");
+      break;
+    case CIRCULAR:
+      writer.write_string("mode", "circular");
+      break;
+    default:
+      msg_warning("Don't know how to write mode " << (int) mode << " ?!?");
+      break;
+  }
 
-  for (int i=0; i < (int)pathNodes.size(); i++) {
-    PathNode node = pathNodes[i];
+  for (size_t i=0; i < nodes.size(); i++) {
+    const Node& node = nodes[i];
 
     writer.start_list("node");
     writer.write_float("x", node.position.x);
@@ -185,29 +125,12 @@ Path::write(lisp::Writer& writer)
   writer.end_list("path");
 }
 
-const Vector&
-Path::GetPosition() {
-  return position;
-}
-
-const Vector&
-Path::GetLastMovement() {
-  return last_movement;
-}
-
-const std::string
-Path::GetName() {
-  return name;
-}
-
-//////////////////////////////////////////////////////////////////////////////
-// static stuff
-
-std::map<std::string,Path*> Path::registry;
-
-Path*
-Path::GetByName(const std::string& name) {
-  return registry[name];
+Vector
+Path::get_base() const
+{
+  if(nodes.empty())
+    return Vector(0, 0);
+  
+  return nodes[0].position;
 }
 
-IMPLEMENT_FACTORY(Path, "path");
index f04623b..60812ec 100644 (file)
@@ -3,6 +3,7 @@
 //  SuperTux Path
 //  Copyright (C) 2005 Philipp <balinor@pnxs.de>
 //  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+//  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
 //
 //  This program is free software; you can redistribute it and/or
 //  modify it under the terms of the GNU General Public License
 #ifndef __PATH_HPP__
 #define __PATH_HPP__
 
-#include <string>
-#include <list>
-#include <map>
-
+#include <vector>
 #include "math/vector.hpp"
-#include "game_object.hpp"
 #include "lisp/lisp.hpp"
 #include "serializable.hpp"
 
-
-/**
- * Helper class that stores an individual node of a Path
- */
-class PathNode
-{
-public:
-  Vector position; /**< position (in pixels) of this node */
-  float time; /**< time (in seconds) to get to this node */
-};
-
-
-/**
- * Path an object can travel along. Made up of multiple nodes of type PathNode.
- */
-class Path : public GameObject, public Serializable
+class Path : public Serializable
 {
 public:
-  Path(const lisp::Lisp& reader);
+  Path();
   ~Path();
 
-  virtual void update(float elapsed_time);
-  virtual void draw(DrawingContext& context);
+  void read(const lisp::Lisp& reader);
+  void write(lisp::Writer& writer);
 
-  virtual void write(lisp::Writer& writer);
-
-  const Vector& GetPosition();
-  const Vector& GetLastMovement();
-
-  const std::string GetName();
-
-  // WARNING: returns NULL if not found !
-  static Path* GetByName(const std::string& name);
+  Vector get_base() const;
 
 private:
-  std::string name; /**< name this path can be referenced with, stored in PathRegistry */
-  bool circular; /**< true: start with the first node once the last one has been reached. false: path will stop at last node */
-  bool forward; /**< true: travel to nodes in the order they were defined. false: inverse order */
-  std::vector<PathNode> pathNodes; /**< list of nodes that make up this path */
-
-  Vector position; /**< current position */
-  Vector velocity; /**< current velocity */
-  Vector last_movement; /**< amount of pixels we moved in the last call to update */
-
-  int destinationNode; /**< current destination Node */
-  float timeToGo; /**< seconds until we arrive at the destination */
-
-  static std::map<std::string,Path*> registry;
+  friend class PathWalker;
+
+  enum WalkMode {
+    // moves from first to last path node and stops
+    ONE_SHOT,
+    // moves from first to last node then in reverse order back to first
+    PING_PONG,
+    // moves from last node back to the first node
+    CIRCULAR
+  };
+
+  /**
+   * Helper class that stores an individual node of a Path
+   */
+  class Node
+  {
+  public:
+    Vector position; /**< the position of this node */
+    float time; /**< time (in seconds) to get from this node to next node */
+  };
+
+  std::vector<Node> nodes;
+  
+  WalkMode mode;
 };
 
 #endif
+
index a28ec87..cbe0188 100644 (file)
 
 #include "platform.hpp"
 
+#include <stdexcept>
 #include "msg.hpp"
 #include "video/drawing_context.hpp"
 #include "resources.hpp"
 #include "player.hpp"
+#include "path.hpp"
+#include "path_walker.hpp"
 #include "sprite/sprite_manager.hpp"
 #include "lisp/lisp.hpp"
 #include "object_factory.hpp"
 
 Platform::Platform(const lisp::Lisp& reader)
 {
-  std::string use_path;
-  std::string type;
+  std::string sprite_name;
+  reader.get("sprite", sprite_name);
+  if(sprite_name == "")
+    throw new std::runtime_error("No sprite specified in platform object"); 
+  sprite.reset(sprite_manager->create(sprite_name));
 
-  reader.get("x", bbox.p1.x);
-  reader.get("y", bbox.p1.y);
-  reader.get("type", type);
-  reader.get("path", use_path);
-  sprite = sprite_manager->create("images/objects/flying_platform/platform.sprite");
-  sprite->set_action(type);
-  bbox.set_size(sprite->get_width(), sprite->get_height());
+  const lisp::Lisp* pathLisp = reader.get_lisp("path");
+  if(pathLisp == NULL)
+    throw new std::runtime_error("No path specified for platform");
+  path.reset(new Path());
+  path->read(*pathLisp);
+  walker.reset(new PathWalker(path.get()));
 
+  bbox.p1 = path->get_base();
+  bbox.set_size(sprite->get_width(), sprite->get_height());
+  
+  set_group(COLGROUP_STATIC);
   flags |= FLAG_SOLID;
-
-  path = Path::GetByName(use_path);
-
-  if (path == NULL) {
-    msg_warning("Path \"" << use_path << "\" for moving platform not found! Make sure that the name is spelled correctly and that the path is initialized before the platform in the level file!");
-  }
-
-  path_offset = bbox.p1;
 }
 
 Platform::~Platform()
 {
-  delete sprite;
 }
 
 //TODO: Squish Tux when standing between platform and solid tile/object
@@ -65,24 +65,25 @@ HitResponse
 Platform::collision(GameObject& other, const CollisionHit& hit)
 {
   if (typeid(other) == typeid(Player)) {
-    Player* player = (Player*) &other;
-    if ((hit.normal.x == 0) && (hit.normal.y == 1)) {
+    if (hit.normal.y >= 0.9) {
       //Tux is standing on the platform
-      player->movement += path->GetLastMovement();
+      //Player* player = (Player*) &other;
+      //player->add_velocity(speed * 1.5);
+      return TEST;
     }
   }
   if(other.get_flags() & FLAG_SOLID) {
     //Collision with a solid tile
-    //does nothing, because the movement vector isn't used at the moment
     return ABORT_MOVE;
   }
   return FORCE_MOVE;
 }
 
 void
-Platform::update(float )
+Platform::update(float elapsed_time)
 {
-  set_pos(path->GetPosition() + path_offset);
+  movement = walker->advance(elapsed_time);
+  speed = movement / elapsed_time;
 }
 
 void
index c6286c1..82eff0b 100644 (file)
@@ -20,6 +20,7 @@
 #ifndef __PLATFORM_H__
 #define __PLATFORM_H__
 
+#include <memory>
 #include "moving_object.hpp"
 #include "sprite/sprite.hpp"
 #include "object/path.hpp"
@@ -36,11 +37,16 @@ public:
   virtual HitResponse collision(GameObject& other, const CollisionHit& hit);
   virtual void update(float elapsed_time);
   virtual void draw(DrawingContext& context);
+  const Vector& get_speed() const
+  {
+    return speed;
+  }
 
 private:
-  Sprite* sprite;
-  Path* path;
-  Vector path_offset;
+  std::auto_ptr<Sprite> sprite;
+  std::auto_ptr<Path> path;
+  std::auto_ptr<PathWalker> walker;
+  Vector speed;
 };
 
 #endif
index 628752e..a279739 100644 (file)
@@ -42,6 +42,7 @@
 #include "trigger/trigger_base.hpp"
 #include "control/joystickkeyboardcontroller.hpp"
 #include "main.hpp"
+#include "platform.hpp"
 #include "badguy/badguy.hpp"
 #include "player_status.hpp"
 #include "msg.hpp"
@@ -122,6 +123,7 @@ Player::init()
     bbox.set_size(31.8, 63.8);
   else
     bbox.set_size(31.8, 31.8);
+  adjust_height = 0;
 
   dir = RIGHT;
   old_dir = dir;
@@ -156,21 +158,17 @@ Player::set_controller(Controller* controller)
 void
 Player::update(float elapsed_time)
 {
-  // do we need to enable gravity again?
-  if(on_ground_flag) {
-    Rect lower = bbox;
-    lower.move(Vector(0, 4.0));
-    if(Sector::current()->is_free_space(lower)) {
-      physic.enable_gravity(true);
-      on_ground_flag = false;
-    }
-  }
-    
   if(dying && dying_timer.check()) {
     dead = true;
     return;
   }
 
+  if(adjust_height != 0) {
+    bbox.move(Vector(0, bbox.get_height() - adjust_height));
+    bbox.set_height(adjust_height);
+    adjust_height = 0;
+  }
+
   if(!controller->hold(Controller::ACTION) && grabbed_object) {
     // move the grabbed object a bit away from tux
     Vector pos = get_pos() + 
@@ -210,6 +208,8 @@ Player::update(float elapsed_time)
              bbox.get_height()*0.66666 - 32);
     grabbed_object->grab(*this, pos, dir);
   }
+
+  on_ground_flag = false;
 }
 
 bool
@@ -525,8 +525,7 @@ Player::set_bonus(BonusType type, bool animate)
     return;
   
   if(player_status->bonus == NO_BONUS) {
-    bbox.set_height(63.8);
-    bbox.move(Vector(0, -32));
+    adjust_height = 63.8;
     if(animate)
       growing_timer.start(GROWING_TIME);
   }
@@ -600,7 +599,7 @@ Player::draw(DrawingContext& context)
     else // dir == RIGHT
       tux_body->set_action("buttjump-right");
     }
-  else if (physic.get_velocity_y() != 0)
+  else if (physic.get_velocity_y() != 0 && !on_ground())
     {
     if(dir == LEFT)
       tux_body->set_action("jump-left");
@@ -731,6 +730,7 @@ Player::collision(GameObject& other, const CollisionHit& hit)
     if(hit.normal.y < 0) { // landed on floor?
       if(physic.get_velocity_y() < 0)
         physic.set_velocity_y(0);
+
       on_ground_flag = true;
 
       // remember normal of this tile
@@ -744,16 +744,41 @@ Player::collision(GameObject& other, const CollisionHit& hit)
         floor_normal.y = (floor_normal.y * 0.9) + (hit.normal.y * 0.1);
       }
 
-      // disable gravity
-      physic.enable_gravity(false);
+      // hack platforms so that we stand normally on them when going down...
+      Platform* platform = dynamic_cast<Platform*> (&other);
+      if(platform != NULL) {
+        if(platform->get_speed().y > 0)
+          physic.set_velocity_y(-platform->get_speed().y);
+        //physic.set_velocity_x(platform->get_speed().x);
+      }
     } else if(hit.normal.y > 0) { // bumped against the roof
       physic.set_velocity_y(.1);
+
+      // hack platform so that we are not glued to it from below
+      Platform* platform = dynamic_cast<Platform*> (&other);
+      if(platform != NULL) {
+        physic.set_velocity_y(-platform->get_speed().y);
+      }      
     }
     
     if(fabsf(hit.normal.x) > .9) { // hit on the side?
       physic.set_velocity_x(0);
     }
 
+    MovingObject* omov = dynamic_cast<MovingObject*> (&other);
+    if(omov != NULL) {
+      Vector mov = movement - omov->get_movement();
+      /*
+      printf("W %p - HITN: %3.1f %3.1f D:%3.1f TM: %3.1f %3.1f TD: %3.1f %3.1f PM: %3.2f %3.1f\n",
+          omov,
+          hit.normal.x, hit.normal.y,
+          hit.depth,
+          movement.x, movement.y,
+          dest.p1.x, dest.p1.y,
+          omov->get_movement().x, omov->get_movement().y);
+      */
+    }
+    
     return CONTINUE;
   }
 
@@ -817,7 +842,7 @@ Player::kill(HurtMode mode)
         {
           growing_timer.start(GROWING_TIME);
           safe_timer.start(TUX_SAFE_TIME + GROWING_TIME);
-          bbox.set_height(31.8);
+          adjust_height = 31.8;
           duck = false;
           player_status->bonus = NO_BONUS;
         }
@@ -898,6 +923,12 @@ Player::check_bounds(Camera* camera)
 }
 
 void
+Player::add_velocity(const Vector& velocity)
+{
+  physic.set_velocity(physic.get_velocity() + velocity);
+}
+
+void
 Player::bounce(BadGuy& )
 {
   if(controller->hold(Controller::JUMP))
index 8803372..add9a09 100644 (file)
@@ -92,8 +92,8 @@ private:
   bool dying;
   bool backflipping;
   int  backflip_direction;
+  
 public:
-
   Direction dir;
   Direction old_dir;
 
@@ -104,7 +104,7 @@ public:
   bool jumping;
   bool can_jump;
   bool butt_jump;
-  
+
   Timer invincible_timer;
   Timer skidding_timer;
   Timer safe_timer;
@@ -148,6 +148,11 @@ public:
   // set kick animation
   void kick();
 
+  /**
+   * Adds velocity to the player (be carefull when using this)
+   */
+  void add_velocity(const Vector& velocity);
+
   void bounce(BadGuy& badguy);
 
   bool is_dead() const
@@ -173,6 +178,8 @@ private:
 
   bool visible;
 
+  float adjust_height;
+
   Portable* grabbed_object;
 
   Sprite* smalltux_gameover;
index 3fce45b..5f28bd7 100644 (file)
@@ -57,8 +57,6 @@ PowerUp::collision(GameObject& other, const CollisionHit& hit)
     if(fabsf(hit.normal.y) > .5) { // roof or ground
       physic.set_velocity_y(0);
     } else { // bumped left or right
-      msg_debug("Normal: " << hit.normal.x << "," << hit.normal.y);
-      msg_debug("LRbounce, new speed: " << physic.get_velocity_x());
       physic.set_velocity_x(-physic.get_velocity_x());
     }
 
index 5b022b5..2b17713 100644 (file)
@@ -35,6 +35,7 @@ Rock::Rock(const lisp::Lisp& reader)
   sprite = sprite_manager->create("images/objects/rock/rock.sprite");
   grabbed = false;
   flags |= FLAG_SOLID | FLAG_PORTABLE;
+  set_group(COLGROUP_MOVING);
 }
 
 Rock::~Rock()
@@ -73,16 +74,21 @@ Rock::update(float elapsed_time)
   }
   
   grabbed = false;
+  printf("%p - V %3.1f %3.1f - P %3.1f %3.1f\n", this,
+          physic.get_velocity().x, physic.get_velocity().y,
+          get_pos().x, get_pos().y);
 }
 
 HitResponse
-Rock::collision(GameObject& object, const CollisionHit& )
+Rock::collision(GameObject& object, const CollisionHit& hit)
 {
   if(grabbed) {
     return FORCE_MOVE;
   }
 
   if(object.get_flags() & FLAG_SOLID) {
+    printf("%p vs %p - %3.1f %3.1f D %3.1f\n", this, &object,
+        hit.normal.x, hit.normal.y, hit.depth);
     physic.set_velocity(0, 0);
     return CONTINUE;
   }
index e9fe94d..329418a 100644 (file)
@@ -56,28 +56,41 @@ Physic::set_velocity(float nvx, float nvy)
   vy = -nvy;
 }
 
+void
+Physic::set_velocity(const Vector& vector)
+{
+  vx = vector.x;
+  vy = vector.y;
+}
+
 void Physic::inverse_velocity_x()
 {
-vx = -vx;
+  vx = -vx;
 }
 
 void Physic::inverse_velocity_y()
 {
-vy = -vy;
+  vy = -vy;
 }
 
 float
-Physic::get_velocity_x()
+Physic::get_velocity_x() const
 {
     return vx;
 }
 
 float
-Physic::get_velocity_y()
+Physic::get_velocity_y() const
 {
     return -vy;
 }
 
+Vector
+Physic::get_velocity() const
+{
+  return Vector(vx, -vy);
+}
+
 void
 Physic::set_acceleration_x(float nax)
 {
@@ -93,20 +106,26 @@ Physic::set_acceleration_y(float nay)
 void
 Physic::set_acceleration(float nax, float nay)
 {
-    ax = nax;
-    ay = -nay;
+  ax = nax;
+  ay = -nay;
 }
 
 float
-Physic::get_acceleration_x()
+Physic::get_acceleration_x() const
 {
-    return ax;
+  return ax;
 }
 
 float
-Physic::get_acceleration_y()
+Physic::get_acceleration_y() const
+{
+  return -ay;
+}
+
+Vector
+Physic::get_acceleration() const
 {
-    return -ay;
+  return Vector(ax, -ay);
 }
 
 void
index 828b1e1..da44f9b 100644 (file)
@@ -37,6 +37,7 @@ public:
 
   /// Sets velocity to a fixed value.
   void set_velocity(float vx, float vy);
+  void set_velocity(const Vector& vector);
 
   void set_velocity_x(float vx);
   void set_velocity_y(float vy);
@@ -45,8 +46,9 @@ public:
   void inverse_velocity_x();
   void inverse_velocity_y();
 
-  float get_velocity_x();
-  float get_velocity_y();
+  Vector get_velocity() const;
+  float get_velocity_x() const;
+  float get_velocity_y() const;
 
   /// Set acceleration.
   /** Sets acceleration applied to the object. (Note that gravity is
@@ -57,8 +59,9 @@ public:
   void set_acceleration_x(float ax);
   void set_acceleration_y(float ay);
 
-  float get_acceleration_x();
-  float get_acceleration_y();
+  Vector get_acceleration() const;
+  float get_acceleration_x() const;
+  float get_acceleration_y() const;
 
   /// Enables or disables handling of gravity.
   void enable_gravity(bool gravity_enabled);
index 7ea513a..071e059 100644 (file)
@@ -575,25 +575,14 @@ Sector::draw(DrawingContext& context)
 static const float DELTA = .001;
 
 void
-Sector::collision_tilemap(MovingObject* object, CollisionHit& hit) const
+Sector::collision_tilemap(const Rect& dest, const Vector& movement,
+                          CollisionHit& hit) const
 {
   // calculate rectangle where the object will move
-  float x1, x2;
-  if(object->get_movement().x >= 0) {
-    x1 = object->get_bbox().p1.x;
-    x2 = object->get_bbox().p2.x + object->get_movement().x;
-  } else {
-    x1 = object->get_bbox().p1.x + object->get_movement().x;
-    x2 = object->get_bbox().p2.x;
-  }
-  float y1, y2;
-  if(object->get_movement().y >= 0) {
-    y1 = object->get_bbox().p1.y;
-    y2 = object->get_bbox().p2.y + object->get_movement().y;
-  } else {
-    y1 = object->get_bbox().p1.y + object->get_movement().y;
-    y2 = object->get_bbox().p2.y;
-  }
+  float x1 = dest.get_left();
+  float x2 = dest.get_right();
+  float y1 = dest.get_top();
+  float y2 = dest.get_bottom();
 
   // test with all tiles in this rectangle
   int starttilex = int(x1) / 32;
@@ -603,8 +592,6 @@ Sector::collision_tilemap(MovingObject* object, CollisionHit& hit) const
   int max_y = int(y2+1);
 
   CollisionHit temphit;
-  Rect dest = object->get_bbox();
-  dest.move(object->movement);
   for(int x = starttilex; x*32 < max_x; ++x) {
     for(int y = starttiley; y*32 < max_y; ++y) {
       const Tile* tile = solids->get_tile(x, y);
@@ -616,7 +603,7 @@ Sector::collision_tilemap(MovingObject* object, CollisionHit& hit) const
       // only handle unisolid when the player is falling down and when he was
       // above the tile before
       if(tile->getAttributes() & Tile::UNISOLID) {
-        if(object->movement.y < 0 || object->get_bbox().p2.y > y*32)
+        if(movement.y < 0 || dest.get_top() - movement.y > y*32)
           continue;
       }
 
@@ -626,7 +613,7 @@ Sector::collision_tilemap(MovingObject* object, CollisionHit& hit) const
         Vector p2((x+1)*32, (y+1)*32);
         triangle = AATriangle(p1, p2, tile->getData());
 
-        if(Collision::rectangle_aatriangle(temphit, dest, object->movement,
+        if(Collision::rectangle_aatriangle(temphit, dest, movement,
               triangle)) {
           if(temphit.time > hit.time && (tile->getAttributes() & Tile::SOLID)) {
             hit = temphit;
@@ -634,8 +621,7 @@ Sector::collision_tilemap(MovingObject* object, CollisionHit& hit) const
         }
       } else { // normal rectangular tile
         Rect rect(x*32, y*32, (x+1)*32, (y+1)*32);
-        if(Collision::rectangle_rectangle(temphit, dest,
-              object->movement, rect)) {
+        if(Collision::rectangle_rectangle(temphit, dest, movement, rect)) {
           if(temphit.time > hit.time && (tile->getAttributes() & Tile::SOLID)) {
             hit = temphit;
           }
@@ -646,14 +632,15 @@ Sector::collision_tilemap(MovingObject* object, CollisionHit& hit) const
 }
 
 uint32_t
-Sector::collision_tile_attributes(MovingObject* object) const
+Sector::collision_tile_attributes(const Rect& dest) const
 {
   /** XXX This function doesn't work correctly as it will check all tiles
    * in the bounding box of the object movement, this might include tiles
    * that have actually never been touched by the object
    * (though this only occures for very fast objects...)
    */
-  
+#if 0
   // calculate rectangle where the object will move
   float x1, x2;
   if(object->get_movement().x >= 0) {
@@ -671,6 +658,11 @@ Sector::collision_tile_attributes(MovingObject* object) const
     y1 = object->get_bbox().p1.y + object->get_movement().y;
     y2 = object->get_bbox().p2.y;
   }
+#endif
+  float x1 = dest.p1.x;
+  float y1 = dest.p1.y;
+  float x2 = dest.p2.x;
+  float y2 = dest.p2.y;
 
   // test with all tiles in this rectangle
   int starttilex = int(x1-1) / 32;
@@ -695,37 +687,109 @@ void
 Sector::collision_object(MovingObject* object1, MovingObject* object2) const
 {
   CollisionHit hit;
-  Rect dest1 = object1->get_bbox();
-  dest1.move(object1->get_movement());
-  Rect dest2 = object2->get_bbox();
-  dest2.move(object2->get_movement());
 
   Vector movement = object1->get_movement() - object2->get_movement();
-  if(Collision::rectangle_rectangle(hit, dest1, movement, dest2)) {
+  if(Collision::rectangle_rectangle(hit, object1->dest, movement, object2->dest)) {
     HitResponse response1 = object1->collision(*object2, hit);
     hit.normal *= -1;
     HitResponse response2 = object2->collision(*object1, hit);
 
     if(response1 != CONTINUE) {
       if(response1 == ABORT_MOVE)
-        object1->movement = Vector(0, 0);
+        object1->dest = object1->get_bbox();
       if(response2 == CONTINUE)
-        object2->movement += hit.normal * (hit.depth + DELTA);
+        object2->dest.move(hit.normal * (hit.depth + DELTA));
     } else if(response2 != CONTINUE) {
       if(response2 == ABORT_MOVE)
-        object2->movement = Vector(0, 0);
+        object2->dest = object2->get_bbox();
       if(response1 == CONTINUE)
-        object1->movement += -hit.normal * (hit.depth + DELTA);
+        object1->dest.move(-hit.normal * (hit.depth + DELTA));
     } else {
-      object1->movement += -hit.normal * (hit.depth/2 + DELTA);
-      object2->movement += hit.normal * (hit.depth/2 + DELTA);
+      object1->dest.move(-hit.normal * (hit.depth/2 + DELTA));
+      object2->dest.move(hit.normal * (hit.depth/2 + DELTA));
+    }
+  }
+}
+
+bool
+Sector::collision_static(MovingObject* object, const Vector& movement)
+{
+  GameObject* collided_with = solids;
+  CollisionHit hit;
+  hit.time = -1;
+
+  collision_tilemap(object->dest, movement, hit);
+
+  // collision with other (static) objects
+  CollisionHit temphit;
+  for(MovingObjects::iterator i2 = moving_objects.begin();
+      i2 != moving_objects.end(); ++i2) {
+    MovingObject* moving_object_2 = *i2;
+    if(moving_object_2->get_group() != COLGROUP_STATIC
+        || !moving_object_2->is_valid())
+      continue;
+        
+    Rect dest2 = moving_object_2->get_bbox();
+    // We're using the old position of the object here,
+    // this might seem a bit wrong but improves some situations
+    // like stacked boxes and badguys alot 
+    //
+    dest2.move(moving_object_2->get_movement());
+    Vector rel_movement 
+      = movement - moving_object_2->get_movement();
+    //Vector movement = 
+        
+    if(Collision::rectangle_rectangle(temphit, object->dest, rel_movement, dest2)
+        && temphit.time > hit.time) {
+      hit = temphit;
+      collided_with = moving_object_2;
+    }
+  }
+
+  if(hit.time < 0)
+    return true;
+
+  HitResponse response = object->collision(*collided_with, hit);
+  hit.normal *= -1;
+  if(collided_with != solids) {
+    MovingObject* moving_object = (MovingObject*) collided_with;
+    HitResponse other_response = moving_object->collision(*object, hit);
+    if(other_response == ABORT_MOVE) {
+      moving_object->dest = moving_object->get_bbox();
+    } else if(other_response == FORCE_MOVE) {
+      // the static object "wins" move tux out of the collision
+      object->dest.move(-hit.normal * (hit.depth + DELTA));
+      return false;
+    } else if(other_response == TEST) {
+      object->dest.move(moving_object->get_movement());
+      //object->movement += moving_object->get_movement();
     }
   }
+
+  if(response == CONTINUE) {
+    object->dest.move(-hit.normal * (hit.depth + DELTA));
+    return false;
+  } else if(response == ABORT_MOVE) {
+    object->dest = object->get_bbox();
+    return true;
+  }
+  
+  // force move
+  return false;
 }
 
 void
 Sector::handle_collisions()
 {
+  // calculate destination positions of the objects
+  for(MovingObjects::iterator i = moving_objects.begin();
+      i != moving_objects.end(); ++i) {
+    MovingObject* moving_object = *i;
+
+    moving_object->dest = moving_object->get_bbox();
+    moving_object->dest.move(moving_object->get_movement());
+  }
+    
   // part1: COLGROUP_MOVING vs COLGROUP_STATIC and tilemap
   //   we do this up to 4 times and have to sort all results for the smallest
   //   one before we can continue here
@@ -737,59 +801,39 @@ Sector::handle_collisions()
         || !moving_object->is_valid())
       continue;
 
-    // up to 4 tries
-    for(int t = 0; t < 4; ++t) {
-      CollisionHit hit;
-      hit.time = -1;
-      MovingObject* collided_with = NULL; 
-      
-      // collision with tilemap
-      collision_tilemap(moving_object, hit);
-    
-      // collision with other objects
-      Rect dest1 = moving_object->get_bbox();
-      dest1.move(moving_object->get_movement());
-      CollisionHit temphit;
-      
-      for(MovingObjects::iterator i2 = moving_objects.begin();
-          i2 != moving_objects.end(); ++i2) {
-        MovingObject* moving_object_2 = *i2;
-        if(moving_object_2->get_group() != COLGROUP_STATIC
-           || !moving_object_2->is_valid())
-          continue;
-        
-        Rect dest2 = moving_object_2->get_bbox();
-        dest2.move(moving_object_2->get_movement());
-        Vector movement 
-          = moving_object->get_movement() - moving_object_2->get_movement();
-        if(Collision::rectangle_rectangle(temphit, dest1, movement, dest2)
-            && temphit.time > hit.time) {
-          hit = temphit;
-          collided_with = moving_object_2;
-        }
-      }
+    Vector movement = moving_object->get_movement();
 
-      if(hit.time < 0)
-        break;
+    // test if x or y movement is dominant
+    if(fabsf(moving_object->get_movement().x) > fabsf(moving_object->get_movement().y)) {
 
-      // call collision callbacks
-      HitResponse response;
-      if(collided_with != 0) {
-        response = moving_object->collision(*collided_with, hit);
-        hit.normal *= -1;
-        collided_with->collision(*moving_object, hit);
-      } else {
-        response = moving_object->collision(*solids, hit);
-        hit.normal *= -1;
+      // test in x direction first, then y direction
+      moving_object->dest.move(Vector(0, -movement.y));
+      for(int i = 0; i < 2; ++i) {
+        bool res = collision_static(moving_object, /*Vector(movement.x, 0)*/ movement);
+        if(res)
+          break;
+      }
+      moving_object->dest.move(Vector(0, movement.y));
+      for(int i = 0; i < 2; ++i) {
+        bool res = collision_static(moving_object, /*Vector(0, movement.y)*/ movement);
+        if(res)
+          break;
+      }
+      
+    } else {
+
+      // test in y direction first, then x direction
+      moving_object->dest.move(Vector(-movement.x, 0));
+      for(int i = 0; i < 2; ++i) {
+        bool res = collision_static(moving_object, movement/*Vector(0, movement.y)*/);
+        if(res)
+          break;
       }
-   
-      if(response == CONTINUE) {
-        moving_object->movement += -hit.normal * (hit.depth + DELTA);
-      } else if(response == ABORT_MOVE) {
-        moving_object->movement = Vector(0, 0);
-        break;
-      } else { // force move
-        break;
+      moving_object->dest.move(Vector(movement.x, 0)); 
+      for(int i = 0; i < 2; ++i) {
+        bool res = collision_static(moving_object, movement /*Vector(movement.x, 0)*/);
+        if(res)
+          break;
       }
     }
   }
@@ -803,7 +847,7 @@ Sector::handle_collisions()
         || !moving_object->is_valid())
       continue;
 
-    uint32_t tile_attributes = collision_tile_attributes(moving_object);
+    uint32_t tile_attributes = collision_tile_attributes(moving_object->dest);
     if(tile_attributes > Tile::FIRST_INTERESTING_FLAG) {
       moving_object->collision_tile(tile_attributes);
     }
@@ -853,7 +897,7 @@ Sector::handle_collisions()
       i != moving_objects.end(); ++i) {
     MovingObject* moving_object = *i;
 
-    moving_object->bbox.move(moving_object->get_movement());
+    moving_object->bbox = moving_object->dest;
     moving_object->movement = Vector(0, 0);
   }
 }
index 05d5af4..5bf497a 100644 (file)
@@ -87,11 +87,6 @@ public:
   void play_music(MusicType musictype);
   MusicType get_music_type();
   
-  /** Checks for all possible collisions. And calls the
-      collision_handlers, which the collision_objects provide for this
-      case (or not). */
-  void handle_collisions();
-
   bool add_bullet(const Vector& pos, float xm, Direction dir);
   bool add_smoke_cloud(const Vector& pos);
   void add_floating_text(const Vector& pos, const std::string& text);
@@ -103,8 +98,7 @@ public:
   /** Get total number of badguys */
   int get_total_badguys();
 
-  void collision_tilemap(MovingObject* object, CollisionHit& hit) const;
-  uint32_t collision_tile_attributes(MovingObject* object) const;
+  void collision_tilemap(const Rect& dest, const Vector& movement, CollisionHit& hit) const;
 
   /** Checks if at the specified rectangle are gameobjects with STATIC flag set
    * (or solid tiles from the tilemap)
@@ -119,6 +113,15 @@ public:
   }
 
 private:
+  uint32_t collision_tile_attributes(const Rect& dest) const;
+  
+  bool collision_static(MovingObject* object, const Vector& movement);
+  
+  /** Checks for all possible collisions. And calls the
+      collision_handlers, which the collision_objects provide for this
+      case (or not). */
+  void handle_collisions();
+  
   void collision_object(MovingObject* object1, MovingObject* object2) const;
   GameObject* parse_object(const std::string& name, const lisp::Lisp& lisp);
   
index b9577e2..f4d6ea0 100644 (file)
@@ -381,16 +381,6 @@ void title()
                   // Contrib Menu
                   generate_contrib_menu();
                   break;
-#if 0
-                case MNID_LEVELEDITOR: {
-                  LevelEdtiro* leveleditor = new LevelEditor();
-                  leveleditor->run();
-                  delete leveleditor;
-                  Menu::set_current(main_menu);
-                  resume_demo();
-                  break;
-                }
-#endif
                 case MNID_CREDITS:
                   sound_manager->stop_music();
                   fadeout(500);