New object weak_block: a block that can be destroyed by bullet hits
[supertux.git] / src / collision_grid.cpp
index c3b2769..e53d18a 100644 (file)
@@ -1,10 +1,30 @@
+//  $Id$
+//
+//  SuperTux
+//  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
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  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 <iostream>
-#include "collision_grid.h"
-#include "special/collision.h"
-#include "sector.h"
-#include "collision_grid_iterator.h"
+#include "collision_grid.hpp"
+#include "log.hpp"
+#include "collision.hpp"
+#include "sector.hpp"
+#include "collision_grid_iterator.hpp"
 
 static const float DELTA = .001;
 
@@ -49,14 +69,14 @@ CollisionGrid::add_object(MovingObject* object)
   objects.push_back(wrapper);
   wrapper->id = objects.size()-1;
   
-  const Rectangle& bbox = object->bbox;
+  const Rect& bbox = object->bbox;
   for(float y = bbox.p1.y; y < bbox.p2.y; y += cell_height) {
     for(float x = bbox.p1.x; x < bbox.p2.x; x += cell_width) {
       int gridx = int(x / cell_width);
       int gridy = int(y / cell_height);
       if(gridx < 0 || gridy < 0 
           || gridx >= int(cells_x) || gridy >= int(cells_y)) {
-        std::cerr << "Object out of range: " << gridx << ", " << gridy << "\n";
+        log_warning << "Object out of range: " << gridx << ", " << gridy << std::endl;
         continue;
       }
       GridEntry* entry = new GridEntry;
@@ -78,19 +98,26 @@ CollisionGrid::remove_object(MovingObject* object)
       break;
     }
   }
+#ifdef DEBUG
   assert(wrapper != 0);
+#else
+  if(wrapper == 0) {
+    log_warning << "Tried to remove nonexistant object" << std::endl;
+    return;
+  }
+#endif
   
-  const Rectangle& bbox = wrapper->dest;
+  const Rect& bbox = wrapper->dest;
   for(float y = bbox.p1.y; y < bbox.p2.y; y += cell_height) {
     for(float x = bbox.p1.x; x < bbox.p2.x; x += cell_width) {
       int gridx = int(x / cell_width);
       int gridy = int(y / cell_height);
       if(gridx < 0 || gridy < 0 
           || gridx >= int(cells_x) || gridy >= int(cells_y)) {
-        std::cerr << "Object out of range: " << gridx << ", " << gridy << "\n";
+        log_warning << "Object out of range: " << gridx << ", " << gridy << std::endl;
         continue;
       }
-      remove_object_from_gridcell(gridy*cells_x + gridx, object);
+      remove_object_from_gridcell(gridy*cells_x + gridx, wrapper);
     }
   }
 
@@ -98,34 +125,60 @@ CollisionGrid::remove_object(MovingObject* object)
 }
 
 void
-CollisionGrid::move_object(MovingObject* object)
+CollisionGrid::move_object(ObjectWrapper* wrapper)
 {
-  const Rectangle& bbox = object->bbox;
-  for(float y = bbox.p1.y; y < bbox.p2.y; y += cell_height) {
-    for(float x = bbox.p1.x; x < bbox.p2.x; x += cell_width) {
+  // FIXME not optimal yet... should leave the gridcells untouched that don't
+  // need to be changed.
+  const Rect& obbox = wrapper->dest;
+  for(float y = obbox.p1.y; y < obbox.p2.y; y += cell_height) {
+    for(float x = obbox.p1.x; x < obbox.p2.x; x += cell_width) {
+      int gridx = int(x / cell_width);
+      int gridy = int(y / cell_height);
+      if(gridx < 0 || gridy < 0  ||
+         gridx >= int(cells_x) || gridy >= int(cells_y)) {
+        log_warning << "Object out of range: " << gridx << ", " << gridy << std::endl;
+        continue;
+      }
+      remove_object_from_gridcell(gridy*cells_x + gridx, wrapper);
+    }
+  }
+
+  const Rect& nbbox = wrapper->object->bbox;
+  for(float y = nbbox.p1.y; y < nbbox.p2.y; y += cell_height) {
+    for(float x = nbbox.p1.x; x < nbbox.p2.x; x += cell_width) {
       int gridx = int(x / cell_width);
       int gridy = int(y / cell_height);
       if(gridx < 0 || gridy < 0 
           || gridx >= int(cells_x) || gridy >= int(cells_y)) {
-        std::cerr << "Object out of range: " << gridx << ", " << gridy << "\n";
+        log_warning << "Object out of range: " << gridx << ", " << gridy << std::endl;
         continue;
       }
-      // TODO
+
+      GridEntry* entry = new GridEntry;
+      entry->object_wrapper = wrapper;
+      entry->next = grid[gridy*cells_x + gridx];
+      grid[gridy*cells_x + gridx] = entry;
     }
   }
+
+  wrapper->dest = nbbox;
 }
 
 void
 CollisionGrid::check_collisions()
 {
+  std::vector<ObjectWrapper*> moved_objects;
+#if 0
   CollisionGridIterator iter(*this, Sector::current()->get_active_region());
   while(ObjectWrapper* wrapper = iter.next_wrapper()) {
     MovingObject* object = wrapper->object;
     if(!object->is_valid())
       continue;
-    if(object->get_flags() & GameObject::FLAG_NO_COLLDET) {
+    if(object->get_group() == COLGROUP_DISABLED) {
       object->bbox.move(object->movement);
       object->movement = Vector(0, 0);
+      moved_objects.push_back(wrapper);
       continue;
     }
 
@@ -134,8 +187,17 @@ CollisionGrid::check_collisions()
     
     collide_object(wrapper);
 
-    object->bbox.move(object->get_movement());
-    object->movement = Vector(0, 0);
+    if(object->movement != Vector(0, 0)) {
+      object->bbox.move(object->movement);
+      object->movement = Vector(0, 0);
+      moved_objects.push_back(wrapper);
+    }
+  }
+#endif
+
+  for(std::vector<ObjectWrapper*>::iterator i = moved_objects.begin();
+      i != moved_objects.end(); ++i) {
+    move_object(*i);
   }
 }
 
@@ -144,14 +206,14 @@ CollisionGrid::collide_object(ObjectWrapper* wrapper)
 {
   iterator_timestamp++;
 
-  const Rectangle& bbox = wrapper->object->bbox;
-  for(float y = bbox.p1.y; y < bbox.p2.y; y += cell_height) {
-    for(float x = bbox.p1.x; x < bbox.p2.x; x += cell_width) {
+  const Rect& bbox = wrapper->object->bbox;
+  for(float y = bbox.p1.y - cell_height; y < bbox.p2.y + cell_height; y += cell_height) {
+    for(float x = bbox.p1.x - cell_width; x < bbox.p2.x + cell_width; x += cell_width) {
       int gridx = int(x / cell_width);
       int gridy = int(y / cell_height);
       if(gridx < 0 || gridy < 0 
           || gridx >= int(cells_x) || gridy >= int(cells_y)) {
-        std::cerr << "Object out of range: " << gridx << ", " << gridy << "\n";
+        //log_warning << "Object out of range: " << gridx << ", " << gridy << std::endl;
         continue;
       }
   
@@ -180,9 +242,9 @@ CollisionGrid::collide_object_object(ObjectWrapper* wrapper,
   MovingObject* object1 = wrapper->object;
   MovingObject* object2 = wrapper2->object;
   
-  Rectangle dest1 = object1->get_bbox();
+  Rect dest1 = object1->get_bbox();
   dest1.move(object1->get_movement());
-  Rectangle dest2 = object2->get_bbox();
+  Rect dest2 = object2->get_bbox();
   dest2.move(object2->get_movement());
 
   Vector movement = object1->get_movement() - object2->get_movement();
@@ -209,13 +271,13 @@ CollisionGrid::collide_object_object(ObjectWrapper* wrapper,
 }
 
 void
-CollisionGrid::remove_object_from_gridcell(int gridcell, MovingObject* object)
+CollisionGrid::remove_object_from_gridcell(int gridcell, ObjectWrapper* wrapper)
 {
   GridEntry* lastentry = 0;
   GridEntry* entry = grid[gridcell];
 
   while(entry) {
-    if(entry->object_wrapper->object == object) {
+    if(entry->object_wrapper == wrapper) {
       if(lastentry == 0) {
         grid[gridcell] = entry->next;
       } else {
@@ -229,6 +291,6 @@ CollisionGrid::remove_object_from_gridcell(int gridcell, MovingObject* object)
     entry = entry->next;
   };
 
-  std::cerr << "Couldn't find object in cell.\n";
+  log_warning << "Couldn't find object in cell" << std::endl;
 }