fixed background drawing problems introduced with my last commit
[supertux.git] / src / sector.cpp
index 37a9e39..f9b5ede 100644 (file)
 #include "app/globals.h"
 #include "sector.h"
 #include "utils/lispreader.h"
-#include "badguy.h"
-#include "gameobjs.h"
-#include "camera.h"
-#include "background.h"
-#include "particlesystem.h"
+#include "object/gameobjs.h"
+#include "object/camera.h"
+#include "object/background.h"
+#include "object/particlesystem.h"
+#include "object/tilemap.h"
 #include "tile.h"
-#include "tilemap.h"
 #include "audio/sound_manager.h"
 #include "gameloop.h"
 #include "resources.h"
@@ -45,8 +44,8 @@
 #include "math/aatriangle.h"
 #include "object/coin.h"
 #include "object/block.h"
+#include "object/invisible_block.h"
 #include "object/platform.h"
-#include "trigger/door.h"
 #include "object/bullet.h"
 #include "badguy/jumpy.h"
 #include "badguy/snowball.h"
 #include "badguy/flame.h"
 #include "badguy/mriceblock.h"
 #include "badguy/mrbomb.h"
+#include "badguy/dispenser.h"
+#include "badguy/spike.h"
+#include "badguy/nolok_01.h"
+#include "trigger/door.h"
 #include "trigger/sequence_trigger.h"
+#include "trigger/secretarea_trigger.h"
 
 Sector* Sector::_current = 0;
 
 Sector::Sector()
-  : gravity(10), player(0), solids(0), background(0), camera(0),
+  : gravity(10), player(0), solids(0), camera(0),
     currentmusic(LEVEL_MUSIC)
 {
   song_title = "Mortimers_chipdisko.mod";
@@ -69,6 +73,9 @@ Sector::Sector()
 
 Sector::~Sector()
 {
+  update_game_objects();
+  assert(gameobjects_new.size() == 0);
+
   for(GameObjects::iterator i = gameobjects.begin(); i != gameobjects.end();
       ++i) {
     delete *i;
@@ -82,48 +89,17 @@ Sector::~Sector()
     _current = 0;
 }
 
-Sector *Sector::create(const std::string& name, size_t width, size_t height)
-{
-  Sector *sector = new Sector;
-  sector->name = name;
-  TileMap *background = new TileMap(LAYER_BACKGROUNDTILES, false, width, height);
-  TileMap *interactive = new TileMap(LAYER_TILES, true, width, height);
-  TileMap *foreground = new TileMap(LAYER_FOREGROUNDTILES, false, width, height);
-  sector->add_object(background);
-  sector->add_object(interactive);
-  sector->add_object(foreground);
-  sector->solids = interactive;
-  sector->camera = new Camera(sector);
-  sector->add_object(sector->camera);
-  sector->update_game_objects();
-  return sector;
-}
-
 GameObject*
-Sector::parseObject(const std::string& name, LispReader& reader)
+Sector::parse_object(const std::string& name, LispReader& reader)
 {
   if(name == "background") {
-    background = new Background(reader);
-    return background;
+    return new Background(reader);
   } else if(name == "camera") {
-    if(camera) {
-      std::cerr << "Warning: More than 1 camera defined in sector.\n";
-      return 0;
-    }
-    camera = new Camera(this);
-    camera->read(reader);
+    Camera* camera = new Camera(this);
+    camera->parse(reader);
     return camera;
   } else if(name == "tilemap") {
-    TileMap* tilemap = new TileMap(reader);
-
-    if(tilemap->is_solid()) {
-      if(solids) {
-        std::cerr << "Warning multiple solid tilemaps in sector.\n";
-        return 0;
-      }
-      solids = tilemap;
-    }
-    return tilemap;
+    return  new TileMap(reader);
   } else if(name == "particles-snow") {
     SnowParticleSystem* partsys = new SnowParticleSystem();
     partsys->parse(reader);
@@ -134,6 +110,8 @@ Sector::parseObject(const std::string& name, LispReader& reader)
     return partsys;
   } else if(name == "door") {
     return new Door(reader);
+  } else if(name == "secretarea") {
+    return new SecretAreaTrigger(reader);
   } else if(name == "platform") {
     return new Platform(reader);
   } else if(name == "jumpy" || name == "money") {
@@ -148,18 +126,16 @@ Sector::parseObject(const std::string& name, LispReader& reader)
     return new MrIceBlock(reader);
   } else if(name == "mrbomb") {
     return new MrBomb(reader);
+  } else if(name == "dispenser") {
+    return new Dispenser(reader);
+  } else if(name == "spike") {
+    return new Spike(reader);
+  } else if(name == "nolok_01") {
+    return new Nolok_01(reader);
   }
-#if 0
-    else if(badguykind_from_string(name) != BAD_INVALID) {
-      return new BadGuy(badguykind_from_string(name), reader);
-    } else if(name == "trampoline") {
-      return new Trampoline(reader);
-    } else if(name == "flying-platform") {
-      return new FlyingPlatform(reader);
-#endif
 
-   std::cerr << "Unknown object type '" << name << "'.\n";
-   return 0;
+  std::cerr << "Unknown object type '" << name << "'.\n";
+  return 0;
 }
 
 void
@@ -188,13 +164,16 @@ Sector::parse(LispReader& lispreader)
       reader.read_float("y", sp->pos.y);
       spawnpoints.push_back(sp);
     } else {
-      GameObject* object = parseObject(token, reader);
+      GameObject* object = parse_object(token, reader);
       if(object) {
         add_object(object);
       }
     }
   }
 
+  update_game_objects();
+  fix_old_tiles();
+  update_game_objects();
   if(!camera) {
     std::cerr << "sector '" << name << "' does not contain a camera.\n";
     camera = new Camera(this);
@@ -235,11 +214,11 @@ Sector::parse_old_format(LispReader& reader)
   bkgd_bottom.blue = b;
   
   if(backgroundimage != "") {
-    background = new Background;
+    Background* background = new Background;
     background->set_image(backgroundimage, bgspeed);
     add_object(background);
   } else {
-    background = new Background;
+    Background* background = new Background;
     background->set_gradient(bkgd_top, bkgd_bottom);
     add_object(background);
   }
@@ -273,34 +252,7 @@ Sector::parse_old_format(LispReader& reader)
       || reader.read_int_vector("tilemap", tiles)) {
     TileMap* tilemap = new TileMap();
     tilemap->set(width, height, tiles, LAYER_TILES, true);
-    solids = tilemap;
     add_object(tilemap);
-
-    // hack for now...
-    for(size_t x=0; x < solids->get_width(); ++x) {
-      for(size_t y=0; y < solids->get_height(); ++y) {
-        const Tile* tile = solids->get_tile(x, y);
-
-        if(tile->attributes & Tile::COIN) {
-          Coin* coin = new Coin(Vector(x*32, y*32));
-          add_object(coin);
-          solids->change(x, y, 0);
-        } else if(tile->attributes & Tile::FULLBOX) {
-          BonusBlock* block = new BonusBlock(Vector(x*32, y*32), tile->data);
-          add_object(block);
-          solids->change(x, y, 0);
-        } else if(tile->attributes & Tile::BRICK) {
-          Brick* brick = new Brick(Vector(x*32, y*32), tile->data);
-          add_object(brick);
-          solids->change(x, y, 0);
-        } else if(tile->attributes & Tile::GOAL) {
-          SequenceTrigger* trigger = new SequenceTrigger(Vector(x*32, y*32),
-              "endsequence");
-          add_object(trigger);
-          solids->change(x, y, 0);
-        }
-      }                                                   
-    }
   }
 
   if(reader.read_int_vector("background-tm", tiles)) {
@@ -347,7 +299,7 @@ Sector::parse_old_format(LispReader& reader)
                                                                                 
         LispReader reader(lisp_cdr(data));
 
-        GameObject* object = parseObject(object_type, reader);
+        GameObject* object = parse_object(object_type, reader);
         if(object) {
           add_object(object);
         } else {
@@ -360,8 +312,55 @@ Sector::parse_old_format(LispReader& reader)
   }
 
   // add a camera
-  camera = new Camera(this);
+  Camera* camera = new Camera(this);
   add_object(camera);
+
+  update_game_objects();
+  fix_old_tiles();
+  update_game_objects();
+  if(solids == 0)
+    throw std::runtime_error("sector does not contain a solid tile layer.");  
+}
+
+void
+Sector::fix_old_tiles()
+{
+  // hack for now...
+  for(size_t x=0; x < solids->get_width(); ++x) {
+    for(size_t y=0; y < solids->get_height(); ++y) {
+      const Tile* tile = solids->get_tile(x, y);
+      Vector pos(x*32, y*32);
+      
+      if(tile->id == 112) {
+        add_object(new InvisibleBlock(pos));
+        solids->change(x, y, 0);
+      } else if(tile->id == 295) {
+        add_object(new Spike(pos, Spike::NORTH));
+        solids->change(x, y, 0);
+      } else if(tile->id == 296) {
+        add_object(new Spike(pos, Spike::EAST));
+        solids->change(x, y, 0);
+      } else if(tile->id == 297) {
+        add_object(new Spike(pos, Spike::SOUTH));
+        solids->change(x, y, 0);
+      } else if(tile->id == 298) {
+        add_object(new Spike(pos, Spike::WEST));
+        solids->change(x, y, 0);
+      } else if(tile->attributes & Tile::COIN) {
+        add_object(new Coin(pos));
+        solids->change(x, y, 0);
+      } else if(tile->attributes & Tile::FULLBOX) {
+        add_object(new BonusBlock(pos, tile->data));
+        solids->change(x, y, 0);
+      } else if(tile->attributes & Tile::BRICK) {
+        add_object(new Brick(pos, tile->data));
+        solids->change(x, y, 0);
+      } else if(tile->attributes & Tile::GOAL) {
+        add_object(new SequenceTrigger(pos, "endsequence"));
+        solids->change(x, y, 0);
+      }
+    }                                                   
+  }
 }
 
 void
@@ -392,41 +391,6 @@ Sector::write(LispWriter& writer)
 }
 
 void
-Sector::do_vertical_flip()
-{
-  // remove or fix later
-#if 0
-  for(GameObjects::iterator i = gameobjects_new.begin(); i != gameobjects_new.end(); ++i)
-    {
-    TileMap* tilemap = dynamic_cast<TileMap*> (*i);
-    if(tilemap)
-      {
-      tilemap->do_vertical_flip();
-      }
-
-    BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
-    if(badguy)
-      badguy->start_position.y = solids->get_height()*32 - badguy->start_position.y - 32;
-    Trampoline* trampoline = dynamic_cast<Trampoline*> (*i);
-    if(trampoline)
-      trampoline->base.y = solids->get_height()*32 - trampoline->base.y - 32;
-    FlyingPlatform* flying_platform = dynamic_cast<FlyingPlatform*> (*i);
-    if(flying_platform)
-      flying_platform->base.y = solids->get_height()*32 - flying_platform->base.y - 32;
-    Door* door = dynamic_cast<Door*> (*i);
-    if(door)
-      door->set_area(door->get_area().x, solids->get_height()*32 - door->get_area().y - 32);
-    }
-
-  for(SpawnPoints::iterator i = spawnpoints.begin(); i != spawnpoints.end();
-      ++i) {
-    SpawnPoint* spawn = *i;
-    spawn->pos.y = solids->get_height()*32 - spawn->pos.y - 32;
-  }
-#endif
-}
-
-void
 Sector::add_object(GameObject* object)
 {
   // make sure the object isn't already in the list
@@ -517,8 +481,7 @@ Sector::action(float elapsed_time)
   }
                                                                                 
   /* Handle all possible collisions. */
-  collision_handler();
-                                                                                
+  collision_handler();                                                                              
   update_game_objects();
 }
 
@@ -535,15 +498,6 @@ Sector::update_game_objects()
             std::remove(bullets.begin(), bullets.end(), bullet),
             bullets.end());
       }
-#if 0
-      InteractiveObject* interactive_object =
-          dynamic_cast<InteractiveObject*> (*i);
-      if(interactive_object) {
-        interactive_objects.erase(
-            std::remove(interactive_objects.begin(), interactive_objects.end(),
-                interactive_object), interactive_objects.end());
-      }
-#endif
       delete *i;
       i = gameobjects.erase(i);
     } else {
@@ -555,17 +509,29 @@ Sector::update_game_objects()
   for(std::vector<GameObject*>::iterator i = gameobjects_new.begin();
       i != gameobjects_new.end(); ++i)
   {
-          Bullet* bullet = dynamic_cast<Bullet*> (*i);
-          if(bullet)
-            bullets.push_back(bullet);
-#if 0
-          InteractiveObject* interactive_object 
-              = dynamic_cast<InteractiveObject*> (*i);
-          if(interactive_object)
-            interactive_objects.push_back(interactive_object);
-#endif
+    Bullet* bullet = dynamic_cast<Bullet*> (*i);
+    if(bullet)
+      bullets.push_back(bullet);
+
+    TileMap* tilemap = dynamic_cast<TileMap*> (*i);
+    if(tilemap && tilemap->is_solid()) {
+      if(solids == 0) {
+        solids = tilemap;
+      } else {
+        std::cerr << "Another solid tilemaps added. Ignoring.";
+      }
+    }
+
+    Camera* camera = dynamic_cast<Camera*> (*i);
+    if(camera) {
+      if(this->camera != 0) {
+        std::cerr << "Warning: Multiple cameras added. Ignoring.";
+        continue;
+      }
+      this->camera = camera;
+    }
 
-          gameobjects.push_back(*i);
+    gameobjects.push_back(*i);
   }
   gameobjects_new.clear();
 }
@@ -588,6 +554,8 @@ Sector::draw(DrawingContext& context)
   context.pop_transform();
 }
 
+static const float DELTA = .001;
+
 void
 Sector::collision_tilemap(MovingObject* object, int depth)
 {
@@ -626,7 +594,7 @@ Sector::collision_tilemap(MovingObject* object, int depth)
   CollisionHit temphit, hit;
   Rectangle dest = object->get_bbox();
   dest.move(object->movement);
-  hit.depth = -1;
+  hit.time = -1; // represents an invalid value
   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);
@@ -641,32 +609,18 @@ Sector::collision_tilemap(MovingObject* object, int depth)
         AATriangle triangle;
         Vector p1(x*32, y*32);
         Vector p2((x+1)*32, (y+1)*32);
-        switch(tile->data) {
-          case 0:
-            triangle = AATriangle(p1, p2, AATriangle::SOUTHWEST);
-            break;
-          case 1:
-            triangle = AATriangle(p1, p2, AATriangle::NORTHEAST);
-            break;
-          case 2:
-            triangle = AATriangle(p1, p2, AATriangle::SOUTHEAST);
-            break;
-          case 3:
-            triangle = AATriangle(p1, p2, AATriangle::NORTHWEST);
-            break;
-          default:
-            printf("Invalid slope angle in tile %d !\n", tile->id);
-            break;
-        }
+        triangle = AATriangle(p1, p2, tile->data);
 
-        if(Collision::rectangle_aatriangle(temphit, dest, triangle)) {
-          if(temphit.depth > hit.depth)
+        if(Collision::rectangle_aatriangle(temphit, dest, object->movement,
+              triangle)) {
+          if(temphit.time > hit.time)
             hit = temphit;
         }
       } else { // normal rectangular tile
         Rectangle rect(x*32, y*32, (x+1)*32, (y+1)*32);
-        if(Collision::rectangle_rectangle(temphit, dest, rect)) {
-          if(temphit.depth > hit.depth)
+        if(Collision::rectangle_rectangle(temphit, dest,
+              object->movement, rect)) {
+          if(temphit.time > hit.time)
             hit = temphit;
         }
       }
@@ -674,7 +628,7 @@ Sector::collision_tilemap(MovingObject* object, int depth)
   }
 
   // did we collide at all?
-  if(hit.depth == -1)
+  if(hit.time < 0)
     return;
  
   // call collision function
@@ -687,7 +641,7 @@ Sector::collision_tilemap(MovingObject* object, int depth)
       return;
   }
   // move out of collision and try again
-  object->movement += hit.normal * (hit.depth + .001);
+  object->movement += hit.normal * (hit.depth + DELTA);
   collision_tilemap(object, depth+1);
 }
 
@@ -699,19 +653,26 @@ Sector::collision_object(MovingObject* object1, MovingObject* object2)
   dest1.move(object1->get_movement());
   Rectangle dest2 = object2->get_bbox();
   dest2.move(object2->get_movement());
-  if(Collision::rectangle_rectangle(hit, dest1, dest2)) {
-    HitResponse response = object1->collision(*object2, hit);
-    if(response == ABORT_MOVE) {
-      object1->movement = Vector(0, 0);
-    } else if(response == CONTINUE) {
-      object1->movement += hit.normal * (hit.depth/2 + .001);
-    }
+
+  Vector movement = object1->get_movement() - object2->get_movement();
+  if(Collision::rectangle_rectangle(hit, dest1, movement, dest2)) {
+    HitResponse response1 = object1->collision(*object2, hit);
     hit.normal *= -1;
-    response = object2->collision(*object1, hit);
-    if(response == ABORT_MOVE) {
-      object2->movement = Vector(0, 0);
-    } else if(response == CONTINUE) {
-      object2->movement += hit.normal * (hit.depth/2 + .001);
+    HitResponse response2 = object2->collision(*object1, hit);
+
+    if(response1 != CONTINUE) {
+      if(response1 == ABORT_MOVE)
+        object1->movement = Vector(0, 0);
+      if(response2 == CONTINUE)
+        object2->movement += hit.normal * (hit.depth + DELTA);
+    } else if(response2 != CONTINUE) {
+      if(response2 == ABORT_MOVE)
+        object2->movement = Vector(0, 0);
+      if(response1 == CONTINUE)
+        object1->movement += -hit.normal * (hit.depth + DELTA);
+    } else {
+      object1->movement += -hit.normal * (hit.depth/2 + DELTA);
+      object2->movement += hit.normal * (hit.depth/2 + DELTA);
     }
   }
 }
@@ -722,13 +683,17 @@ Sector::collision_handler()
   for(std::vector<GameObject*>::iterator i = gameobjects.begin();
       i != gameobjects.end(); ++i) {
     GameObject* gameobject = *i;
-    if(!gameobject->is_valid() 
-        || gameobject->get_flags() & GameObject::FLAG_NO_COLLDET)
+    if(!gameobject->is_valid())
       continue;
     MovingObject* movingobject = dynamic_cast<MovingObject*> (gameobject);
     if(!movingobject)
       continue;
-  
+    if(movingobject->get_flags() & GameObject::FLAG_NO_COLLDET) {
+      movingobject->bbox.move(movingobject->movement);
+      movingobject->movement = Vector(0, 0);
+      continue;
+    }
+
     // collision with tilemap
     if(! (movingobject->movement == Vector(0, 0)))
       collision_tilemap(movingobject, 0);
@@ -746,20 +711,12 @@ Sector::collision_handler()
 
       collision_object(movingobject, movingobject2);
     }
-    
+
     movingobject->bbox.move(movingobject->get_movement());
     movingobject->movement = Vector(0, 0);
   }
 }
 
-void
-Sector::add_score(const Vector& pos, int s)
-{
-  global_stats.add_points(SCORE_STAT, s);
-                                                                                
-  add_object(new FloatingText(pos, s));
-}
-                                                                                
 bool
 Sector::add_bullet(const Vector& pos, float xm, Direction dir)
 {
@@ -792,13 +749,6 @@ Sector::add_smoke_cloud(const Vector& pos)
   return true;
 }
 
-bool
-Sector::add_particles(const Vector& epicenter, int min_angle, int max_angle, const Vector& initial_velocity, const Vector& acceleration, int number, Color color, int size, int life_time, int drawing_layer)
-{
-  add_object(new Particles(epicenter, min_angle, max_angle, initial_velocity, acceleration, number, color, size, life_time, drawing_layer));
-  return true;
-}
-
 void
 Sector::add_floating_text(const Vector& pos, const std::string& text)
 {