-Added new object system and converted some GameObjects to it.
authorMatthias Braun <matze@braunis.de>
Thu, 20 May 2004 12:12:39 +0000 (12:12 +0000)
committerMatthias Braun <matze@braunis.de>
Thu, 20 May 2004 12:12:39 +0000 (12:12 +0000)
 Please take a look if you like this new interface and complain as soon as
 possible :)
-Currently you experience some stuff is drawn at the wrong layer, that is
 because not everything is using the new displaymanager yet.

SVN-Revision: 1276

20 files changed:
src/Makefile.am
src/badguy.cpp
src/display_manager.cpp [new file with mode: 0644]
src/display_manager.h [new file with mode: 0644]
src/drawable.h [new file with mode: 0644]
src/game_object.cpp [new file with mode: 0644]
src/game_object.h [new file with mode: 0644]
src/gameloop.cpp
src/gameobjs.cpp
src/gameobjs.h
src/particlesystem.cpp
src/particlesystem.h
src/text.h
src/texture.h
src/tile.h
src/vector.h [new file with mode: 0644]
src/viewport.cpp [new file with mode: 0644]
src/viewport.h [new file with mode: 0644]
src/world.cpp
src/world.h

index f94c1c5..b4784a3 100644 (file)
@@ -74,6 +74,14 @@ sprite_manager.h \
 music_manager.cpp \
 music_manager.h \
 musicref.cpp \
-musicref.h
+musicref.h \
+viewport.cpp \
+viewport.h \
+game_object.cpp \
+game_object.h \
+display_manager.h \
+display_manager.cpp \
+background.h \
+background.cpp
 
 # EOF #
index c74e677..9804627 100644 (file)
@@ -864,8 +864,8 @@ BadGuy::squish_me(Player* player)
 {
   make_player_jump(player);
     
-  World::current()->add_score(base.x,
-                              base.y, 50 * player_status.score_multiplier);
+  World::current()->add_score(Vector(base.x, base.y),
+                              50 * player_status.score_multiplier);
   play_sound(sounds[SND_SQUISH], SOUND_CENTER_SPEAKER);
   player_status.score_multiplier++;
 
@@ -884,7 +884,8 @@ BadGuy::squish(Player* player)
     World::current()->add_bad_guy(base.x, base.y, BAD_BOMB);
     
     make_player_jump(player);
-    World::current()->add_score(base.x, base.y, 50 * player_status.score_multiplier);
+    World::current()->add_score(Vector(base.x, base.y),
+                                50 * player_status.score_multiplier);
     play_sound(sounds[SND_SQUISH], SOUND_CENTER_SPEAKER);
     player_status.score_multiplier++;
     remove_me();
@@ -936,7 +937,8 @@ BadGuy::squish(Player* player)
       
     make_player_jump(player);
              
-    World::current()->add_score(base.x, base.y, 25 * player_status.score_multiplier);
+    World::current()->add_score(Vector(base.x, base.y),
+                                25 * player_status.score_multiplier);
     player_status.score_multiplier++;
      
     // simply remove the fish...
@@ -977,8 +979,8 @@ BadGuy::kill_me(int score)
 
   /* Gain some points: */
   if (score != 0)
-    World::current()->add_score(base.x, base.y,
-                    score * player_status.score_multiplier);
+    World::current()->add_score(Vector(base.x, base.y),
+                                score * player_status.score_multiplier);
 
   /* Play death sound: */
   play_sound(sounds[SND_FALL], SOUND_CENTER_SPEAKER);
diff --git a/src/display_manager.cpp b/src/display_manager.cpp
new file mode 100644 (file)
index 0000000..4559d59
--- /dev/null
@@ -0,0 +1,43 @@
+#include "display_manager.h"
+
+#include <algorithm>
+
+DisplayManager::DisplayManager()
+{
+}
+
+DisplayManager::~DisplayManager()
+{
+}
+
+void
+DisplayManager::add_drawable(Drawable* drawable, int layer)
+{
+  DisplayList::iterator i 
+    = std::lower_bound(displaylist.begin(), displaylist.end(), layer);
+  if(i == displaylist.end())
+    displaylist.push_back(DrawingQueueEntry(drawable, layer));
+  else
+    displaylist.insert(i, DrawingQueueEntry(drawable, layer));
+}
+
+void
+DisplayManager::remove_drawable(Drawable* drawable)
+{
+  for(DisplayList::iterator i = displaylist.begin();
+      i != displaylist.end(); ) {
+    if(i->object == drawable)
+      i = displaylist.erase(i);
+    else
+      ++i;
+  }
+}
+
+void
+DisplayManager::draw()
+{
+  for(DisplayList::iterator i = displaylist.begin(); i != displaylist.end();
+      ++i)
+    i->object->draw(viewport, i->layer);
+}
+
diff --git a/src/display_manager.h b/src/display_manager.h
new file mode 100644 (file)
index 0000000..cbc4740
--- /dev/null
@@ -0,0 +1,62 @@
+#ifndef __DISPLAY_MANAGER_H__
+#define __DISPLAY_MANAGER_H__
+
+#include <vector>
+
+#include "drawable.h"
+#include "viewport.h"
+
+// some constants for predefined layer values
+enum {
+  LAYER_BACKGROUND0 = -300,
+  LAYER_BACKGROUND1 = -200,
+  LAYER_BACKGROUNDTILES = -100,
+  LAYER_TILES = 0,
+  LAYER_OBJECTS = 100,
+  LAYER_FOREGROUND0 = 200,
+  LAYER_FOREGROUND1 = 300
+};
+
+/** This class holds a list of all things that should be drawn to screen
+ */
+class DisplayManager
+{
+public:
+  DisplayManager();
+  ~DisplayManager();
+  
+  /** adds an object to the list of stuff that should be drawn each frame.
+   * The layer argument specifies how early an object is drawn.
+   */
+  void add_drawable(Drawable* object, int layer);
+
+  void remove_drawable(Drawable* object);
+
+  void draw();
+
+  ViewPort& get_viewport()
+  { return viewport; }
+
+private:
+  class DrawingQueueEntry {
+  public:
+    DrawingQueueEntry(Drawable* newobject, int newlayer)
+      : object(newobject), layer(newlayer)
+    { }
+
+    bool operator <(int olayer) const
+    {
+      return layer < olayer;
+    }
+
+    Drawable* object;
+    int layer;
+  };
+
+  typedef std::vector<DrawingQueueEntry> DisplayList;
+  DisplayList displaylist;
+  ViewPort viewport;
+};
+
+#endif
+
diff --git a/src/drawable.h b/src/drawable.h
new file mode 100644 (file)
index 0000000..605c336
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef __DRAWABLE_H__
+#define __DRAWABLE_H__
+
+class ViewPort;
+
+/** interface for all game objects that can be drawn on screen.
+ */
+class Drawable
+{
+public:
+  /** This function draws the object on screen.
+   */                                              
+  virtual void draw(ViewPort& viewport, int layer) = 0;
+};
+
+#endif
+
diff --git a/src/game_object.cpp b/src/game_object.cpp
new file mode 100644 (file)
index 0000000..a354784
--- /dev/null
@@ -0,0 +1,11 @@
+#include "game_object.h"
+
+_GameObject::_GameObject()
+  : wants_to_die(false)
+{
+}
+
+_GameObject::~_GameObject()
+{
+}
+
diff --git a/src/game_object.h b/src/game_object.h
new file mode 100644 (file)
index 0000000..e5df93b
--- /dev/null
@@ -0,0 +1,52 @@
+#ifndef __GAMEOBJECT_HPP__
+#define __GAMEOBJECT_HPP__
+
+#include <string>
+
+class DisplayManager;
+
+/**
+ * Base class for all game objects. This contains functions for:
+ *  -querying the actual type of the object
+ *  -a flag that indicates if the object wants to be removed. Objects with this
+ *   flag will be removed at the end of each frame. This is alot safer than
+ *   having some uncontrollable "delete this" in the code.
+ *  -an action function that is called once per frame and allows the object to
+ *   update it's state.
+ * 
+ * Most GameObjects will also implement the DrawableObject interface so that
+ * they can actually be drawn on screen.
+ */
+class _GameObject // TODO rename this once the game has been converted
+{
+public:
+  _GameObject();
+  virtual ~_GameObject();
+
+  /** returns the name of the objecttype, this is mainly usefull for the editor.
+   * For the coding part you should use C++ RTTI (ie. typeid and dynamic_cast)
+   * instead.
+   */
+  virtual std::string type() const = 0;
+  /** This function is called once per frame and allows the object to update
+   * it's state. The elapsed_time is the time since the last frame and should be
+   * the base for all timed things.
+   */
+  virtual void action(float elapsed_time) = 0;
+
+  /** returns true if the object is not scheduled to be removed yet */
+  bool is_valid() const
+  { return !wants_to_die; }
+  /** schedules this object to be removed at the end of the frame */
+  void remove_me()
+  { wants_to_die = true; }
+  
+private:
+  /** this flag indicates if the object should be removed at the end of the
+   * frame
+   */
+  bool wants_to_die;
+};
+
+#endif
+
index 190700c..698e02b 100644 (file)
@@ -657,8 +657,8 @@ GameSession::run()
 /* Bounce a brick: */
 void bumpbrick(float x, float y)
 {
-  World::current()->add_bouncy_brick(((int)(x + 1) / 32) * 32,
-                         (int)(y / 32) * 32);
+  World::current()->add_bouncy_brick(Vector(((int)(x + 1) / 32) * 32,
+                         (int)(y / 32) * 32));
 
   play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER);
 }
index 8787bb9..f85c89d 100644 (file)
 #include "sprite_manager.h"
 #include "resources.h"
 #include "level.h"
+#include "display_manager.h"
 
-void
-BouncyDistro::init(float x, float y)
+BouncyDistro::BouncyDistro(DisplayManager& displaymanager, const Vector& pos)
+  : position(pos)
 {
-  base.x = x;
-  base.y = y;
-  base.ym = -2;
+  ym = -2;
+  displaymanager.add_drawable(this, LAYER_OBJECTS);
 }
 
 void
-BouncyDistro::action(double frame_ratio)
+BouncyDistro::action(float elapsed_time)
 {
-  base.y = base.y + base.ym * frame_ratio;
-
-  base.ym += 0.1 * frame_ratio;
+  position.y += ym * elapsed_time;
 
-  if (base.ym >= 0)
-    {
-      std::vector<BouncyDistro*>::iterator i
-        = std::find(World::current()->bouncy_distros.begin(), 
-                    World::current()->bouncy_distros.end(), 
-                    this);
-      if (i != World::current()->bouncy_distros.end())
-        World::current()->bouncy_distros.erase(i);
-    }
+  ym += 0.1 * elapsed_time; // not framerate independent... but who really cares
+  if(ym >= 0)
+    remove_me();
 }
 
 void
-BouncyDistro::draw()
+BouncyDistro::draw(ViewPort& viewport, int )
 {
-  img_distro[0]->draw(base.x - scroll_x,
-                      base.y - scroll_y);
+  img_distro[0]->draw(viewport.world2screen(position));
 }
 
 
-void
-BrokenBrick::init(Tile* tile_, float x, float y, float xm, float ym)
+BrokenBrick::BrokenBrick(DisplayManager& displaymanager, Tile* ntile,
+    const Vector& pos, const Vector& nmovement)
+  : tile(ntile), position(pos), movement(nmovement)
 {
-  tile    = tile_;
-  base.x  = x;
-  base.y  = y;
-  base.xm = xm;
-  base.ym = ym;
-
-  timer.init(true);
+  displaymanager.add_drawable(this, LAYER_OBJECTS);
   timer.start(200);
 }
 
 void
-BrokenBrick::action(double frame_ratio)
+BrokenBrick::action(float elapsed_time)
 {
-  base.x = base.x + base.xm * frame_ratio;
-  base.y = base.y + base.ym * frame_ratio;
+  position += movement * elapsed_time;
 
   if (!timer.check())
-    {
-      std::vector<BrokenBrick*>::iterator i
-        = std::find(World::current()->broken_bricks.begin(), 
-                    World::current()->broken_bricks.end(), 
-                    this);
-      if (i != World::current()->broken_bricks.end())
-        World::current()->broken_bricks.erase(i);
-    }
+    remove_me();
 }
 
 void
-BrokenBrick::draw()
+BrokenBrick::draw(ViewPort& viewport, int )
 {
   SDL_Rect src, dest;
   src.x = rand() % 16;
@@ -101,8 +79,8 @@ BrokenBrick::draw()
   src.w = 16;
   src.h = 16;
 
-  dest.x = (int)(base.x - scroll_x);
-  dest.y = (int)(base.y  - scroll_y);
+  dest.x = (int)(position.x - viewport.get_translation().x);
+  dest.y = (int)(position.y - viewport.get_translation().y);
   dest.w = 16;
   dest.h = 16;
   
@@ -110,107 +88,56 @@ BrokenBrick::draw()
     tile->images[0]->draw_part(src.x,src.y,dest.x,dest.y,dest.w,dest.h);
 }
 
-void
-BouncyBrick::init(float x, float y)
+BouncyBrick::BouncyBrick(DisplayManager& displaymanager, const Vector& pos)
+  : position(pos), offset(0), offset_m(-BOUNCY_BRICK_SPEED)
 {
-  base.x   = x;
-  base.y   = y;
-  offset   = 0;
-  offset_m = -BOUNCY_BRICK_SPEED;
-  shape    = World::current()->get_level()->gettileid(x, y);
+  displaymanager.add_drawable(this, LAYER_OBJECTS);
+  shape    = World::current()->get_level()->gettileid(pos.x, pos.y);
 }
 
 void
-BouncyBrick::action(double frame_ratio)
+BouncyBrick::action(float elapsed_time)
 {
-  offset = (offset + offset_m * frame_ratio);
+  offset += offset_m * elapsed_time;
 
   /* Go back down? */
   if (offset < -BOUNCY_BRICK_MAX_OFFSET)
     offset_m = BOUNCY_BRICK_SPEED;
 
-
   /* Stop bouncing? */
   if (offset >= 0)
-    {
-      std::vector<BouncyBrick*>::iterator i
-        = std::find(World::current()->bouncy_bricks.begin(), 
-                    World::current()->bouncy_bricks.end(), 
-                    this);
-      if (i != World::current()->bouncy_bricks.end())
-        World::current()->bouncy_bricks.erase(i);
-    }
+    remove_me();
 }
 
 void
-BouncyBrick::draw()
+BouncyBrick::draw(ViewPort& viewport, int)
 {
-  SDL_Rect dest;
-  
-  if (base.x >= scroll_x - 32 &&
-      base.x <= scroll_x + screen->w)
-    {
-      dest.x = (int)(base.x - scroll_x);
-      dest.y = (int)(base.y - scroll_y);
-      dest.w = 32;
-      dest.h = 32;
-
-      Level* plevel = World::current()->get_level();
-
-      // FIXME: overdrawing hack to clean the tile from the screen to
-      // paint it later at on offseted position
-      if(plevel->img_bkgd)
-        {
-          fillrect(base.x - scroll_x, base.y - scroll_y,
-                   32,32, 
-                   plevel->bkgd_top.red, plevel->bkgd_top.green, plevel->bkgd_top.blue, 0);
-// FIXME: doesn't respect the gradient, futhermore is this necessary at all??
-        }
-      else
-        {
-          int s = ((int)scroll_x / 2)%640;
-          plevel->img_bkgd->draw_part(dest.x + s, dest.y, 
-                                      dest.x, dest.y,dest.w,dest.h);
-        }
-
-      Tile::draw(base.x - scroll_x,
-                 base.y - scroll_y + offset,
-                 shape);
-    }
+  Tile::draw(viewport.world2screen(position + Vector(0, offset)), shape);
 }
 
-void
-FloatingScore::init(float x, float y, int s)
+FloatingScore::FloatingScore(DisplayManager& displaymanager, 
+    const Vector& pos, int score)
+  : position(pos)
 {
-  base.x = x;
-  base.y = y - 16;
-  timer.init(true);
+  displaymanager.add_drawable(this, LAYER_OBJECTS+1);
   timer.start(1000);
-  value = s;
+  snprintf(str, 10, "%d", score);
+  position.x += - strlen(str) * 8;
 }
 
 void
-FloatingScore::action(double frame_ratio)
+FloatingScore::action(float elapsed_time)
 {
-  base.y = base.y - 2 * frame_ratio;
+  position.y -= 2 * elapsed_time;
 
   if(!timer.check())
-    {
-      std::vector<FloatingScore*>::iterator i
-        = std::find(World::current()->floating_scores.begin(), 
-                    World::current()->floating_scores.end(), 
-                    this);
-      if (i != World::current()->floating_scores.end())
-        World::current()->floating_scores.erase(i);
-    }
+    remove_me();
 }
 
 void
-FloatingScore::draw()
+FloatingScore::draw(ViewPort& viewport, int )
 {
-  char str[10];
-  sprintf(str, "%d", value);
-  gold_text->draw(str, (int)base.x + 16 - strlen(str) * 8, (int)base.y, 1);
+  gold_text->draw(str, viewport.world2screen(position));
 }
 
 /* Trampoline */
@@ -364,6 +291,7 @@ ObjectManager::~ObjectManager()
 
 void ObjectManager::load_badguys(std::string filename)
 {
+  (void) filename;
 /*
   lisp_object_t* root_obj = lisp_read_from_file(filename);
 
index b6cec55..cc1a1da 100644 (file)
@@ -28,6 +28,8 @@
 #include "scene.h"
 #include "physic.h"
 #include "collision.h"
+#include "game_object.h"
+#include "drawable.h"
 
 enum ObjectType { OBJ_NONE, OBJ_BADGUY, OBJ_TRAMPOLINE };
 
@@ -52,14 +54,18 @@ struct ObjectData
 #define NO_BOUNCE 0
 #define BOUNCE 1
 
-class BouncyDistro : public GameObject
+class BouncyDistro : public _GameObject, public Drawable
 {
- public:
-  
-  void init(float x, float y);
-  void action(double frame_ratio);
-  void draw(); 
-  std::string type() { return "BouncyDistro"; };
+public:
+  BouncyDistro(DisplayManager& displaymanager, const Vector& pos);
+  virtual void action(float elapsed_time);
+  virtual void draw(ViewPort& viewport, int layer);
+  virtual std::string type() const
+  { return "BouncyDistro"; };
+
+private:
+  Vector position;
+  float ym;
 };
 
 extern Surface* img_distro[4];
@@ -69,41 +75,56 @@ extern Surface* img_distro[4];
 
 class Tile;
 
-class BrokenBrick : public GameObject
+class BrokenBrick : public _GameObject, public Drawable
 {
- public:
+public:
+  BrokenBrick(DisplayManager& displaymanager, Tile* tile,
+      const Vector& pos, const Vector& movement);
+
+  virtual void action(float elapsed_time);
+  virtual void draw(ViewPort& viewport, int layer);
+
+  virtual std::string type() const
+  { return "BrokenBrick"; };
+
+private:
   Timer timer;
   Tile* tile;
-
-  void init(Tile* tile, float x, float y, float xm, float ym);
-  void action(double frame_ratio);
-  void draw();
-  std::string type() { return "BrokenBrick"; };
+  Vector position;
+  Vector movement;
 };
 
-class BouncyBrick : public GameObject
+class BouncyBrick : public _GameObject, public Drawable
 {
- public:
-  float offset;
-  float offset_m;
-  int shape;
+public:
+  BouncyBrick(DisplayManager& displaymanager, const Vector& pos);
+  virtual void action(float elapsed_time);
+  virtual void draw(ViewPort& viewport, int layer);
+  
+  virtual std::string type() const
+  { return "BouncyBrick"; };
 
-  void init(float x, float y);
-  void action(double frame_ratio);
-  void draw();
-  std::string type() { return "BouncyBrick"; };
+private:
+  Vector position;
+  float offset;   
+  float offset_m;
+  int shape;      
 };
 
-class FloatingScore : public GameObject
+class FloatingScore : public _GameObject, public Drawable
 {
- public:
-  int value;
-  Timer timer;
+public:
+  FloatingScore(DisplayManager& displaymanager, const Vector& pos, int s);
   
-  void init(float x, float y, int s);
-  void action(double frame_ratio);
-  void draw();
-  std::string type() { return "FloatingScore"; };
+  virtual void action(float elapsed_time);
+  virtual void draw(ViewPort& viewport, int layer);
+  virtual std::string type() const
+  { return "FloatingScore"; };
+
+private:
+  Vector position;
+  char str[10];
+  Timer timer;  
 };
 
 
index e303838..8370536 100644 (file)
 #include "world.h"
 #include "level.h"
 #include "scene.h"
+#include "viewport.h"
+#include "display_manager.h"
 
-ParticleSystem::ParticleSystem()
+ParticleSystem::ParticleSystem(DisplayManager& displaymanager)
 {
     virtual_width = screen->w;
     virtual_height = screen->h;
+
+    displaymanager.add_drawable(this, LAYER_BACKGROUND1);
 }
 
 ParticleSystem::~ParticleSystem()
@@ -40,13 +44,16 @@ ParticleSystem::~ParticleSystem()
     }
 }
 
-void ParticleSystem::draw(float scrollx, float scrolly, int layer)
+void ParticleSystem::draw(ViewPort& viewport, int layer)
 {
     std::vector<Particle*>::iterator i;
     for(i = particles.begin(); i != particles.end(); ++i) {
         Particle* particle = *i;
         if(particle->layer != layer)
             continue;
+
+        float scrollx = viewport.get_translation().x;
+        float scrolly = viewport.get_translation().y;
         
         // remap x,y coordinates onto screencoordinates
         float x = fmodf(particle->x - scrollx, virtual_width);
@@ -68,7 +75,8 @@ void ParticleSystem::draw(float scrollx, float scrolly, int layer)
     }
 }
 
-SnowParticleSystem::SnowParticleSystem()
+SnowParticleSystem::SnowParticleSystem(DisplayManager& displaymanager)
+  : ParticleSystem(displaymanager)
 {
     snowimages[0] = new Surface(datadir+"/images/shared/snow0.png", USE_ALPHA);
     snowimages[1] = new Surface(datadir+"/images/shared/snow1.png", USE_ALPHA);
@@ -82,7 +90,7 @@ SnowParticleSystem::SnowParticleSystem()
         SnowParticle* particle = new SnowParticle;
         particle->x = rand() % int(virtual_width);
         particle->y = rand() % screen->h;
-        particle->layer = i % 2;
+        particle->layer = LAYER_BACKGROUND1;
         int snowsize = rand() % 3;
         particle->texture = snowimages[snowsize];
         do {
@@ -100,7 +108,7 @@ SnowParticleSystem::~SnowParticleSystem()
     delete snowimages[i];
 }
 
-void SnowParticleSystem::simulate(float elapsed_time)
+void SnowParticleSystem::action(float elapsed_time)
 {
     std::vector<Particle*>::iterator i;
     for(i = particles.begin(); i != particles.end(); ++i) {
@@ -113,7 +121,8 @@ void SnowParticleSystem::simulate(float elapsed_time)
     }
 }
 
-CloudParticleSystem::CloudParticleSystem()
+CloudParticleSystem::CloudParticleSystem(DisplayManager& displaymanager)
+  : ParticleSystem(displaymanager)
 {
     cloudimage = new Surface(datadir + "/images/shared/cloud.png", USE_ALPHA);
 
@@ -124,7 +133,7 @@ CloudParticleSystem::CloudParticleSystem()
         CloudParticle* particle = new CloudParticle;
         particle->x = rand() % int(virtual_width);
         particle->y = rand() % int(virtual_height);
-        particle->layer = 0;
+        particle->layer = LAYER_BACKGROUND1;
         particle->texture = cloudimage;
         particle->speed = -float(250 + rand() % 200) / 1000.0;
 
@@ -137,7 +146,7 @@ CloudParticleSystem::~CloudParticleSystem()
   delete cloudimage;
 }
 
-void CloudParticleSystem::simulate(float elapsed_time)
+void CloudParticleSystem::action(float elapsed_time)
 {
     std::vector<Particle*>::iterator i;
     for(i = particles.begin(); i != particles.end(); ++i) {
index 01ba586..f0b86b7 100644 (file)
 
 #include <vector>
 #include "texture.h"
+#include "drawable.h"
+#include "game_object.h"
+
+class DisplayManager;
 
 /**
  * This is the base class for particle systems. It is responsible for storing a
  * initialize particles in the constructor and move them in the simulate
  * function.
  */
-class ParticleSystem
+class ParticleSystem : public _GameObject, public Drawable
 {
 public:
-    ParticleSystem();
+    ParticleSystem(DisplayManager& displaymanager);
     virtual ~ParticleSystem();
     
-    void draw(float scrollx, float scrolly, int layer);
-
-    virtual void simulate(float elapsed_time) = 0;
+    virtual void draw(ViewPort& view, int layer);
 
 protected:
     class Particle
@@ -67,10 +69,13 @@ protected:
 class SnowParticleSystem : public ParticleSystem
 {
 public:
-    SnowParticleSystem();
+    SnowParticleSystem(DisplayManager& displaymanager);
     virtual ~SnowParticleSystem();
 
-    virtual void simulate(float elapsed_time);
+    virtual void action(float elapsed_time);
+
+    std::string type() const
+    { return "SnowParticleSystem"; }
     
 private:
     class SnowParticle : public Particle
@@ -85,10 +90,13 @@ private:
 class CloudParticleSystem : public ParticleSystem
 {
 public:
-    CloudParticleSystem();
+    CloudParticleSystem(DisplayManager& displaymanager);
     virtual ~CloudParticleSystem();
 
-    virtual void simulate(float elapsed_time);
+    virtual void action(float elapsed_time);
+
+    std::string type() const
+    { return "SnowParticleSystem"; }    
     
 private:
     class CloudParticle : public Particle
index 87066d7..0e6d7ce 100644 (file)
@@ -23,6 +23,7 @@
 
 #include <string>
 #include "texture.h"
+#include "vector.h"
 
 void display_text_file(const std::string& file, const std::string& surface, float scroll_speed);
 void display_text_file(const std::string& file, Surface* surface, float scroll_speed);
@@ -64,6 +65,13 @@ class Text
   void draw_align(const char* text, int x, int y, TextHAlign halign, TextVAlign valign, int shadowsize = 1, int update = NO_UPDATE);
   void erasetext(const char * text, int x, int y, Surface* surf, int update, int shadowsize);
   void erasecenteredtext(const char * text, int y, Surface* surf, int update, int shadowsize);
+
+  /// conveniance function
+  void draw(const char* text, const Vector& pos, int shadowsize = 1, int update
+      = NO_UPDATE)
+  {
+    draw(text, int(pos.x), int(pos.y), shadowsize, update);
+  }
 };
 
 #endif /*SUPERTUX_TEXT_H*/
index 3ba0e58..00ad954 100644 (file)
@@ -29,6 +29,7 @@
 
 #include <list>
 #include "screen.h"
+#include "vector.h"
 
 SDL_Surface* sdl_surface_from_sdl_surface(SDL_Surface* sdl_surf, int use_alpha);
 
@@ -92,6 +93,12 @@ public:
   void draw_part(float sx, float sy, float x, float y, float w, float h,  Uint8 alpha = 255, bool update = false);
   void draw_stretched(float x, float y, int w, int h, Uint8 alpha, bool update = false);
   void resize(int w_, int h_);
+
+  /// conveniance function
+  void draw(const Vector& pos, Uint8 alpha = 255, bool update = false)
+  {
+    draw(pos.x, pos.y, alpha, update);
+  }
 };
 
 /** Surface implementation, all implementation have to inherit from
index 4d4f008..a94a19c 100644 (file)
@@ -28,6 +28,7 @@
 #include "globals.h"
 #include "lispreader.h"
 #include "setup.h"
+#include "vector.h"
 
 /**
 Tile Class
@@ -86,6 +87,11 @@ public:
   /** Draw a tile on the screen: */
   static void draw(float x, float y, unsigned int c, Uint8 alpha = 255);
   static void draw_stretched(float x, float y, int w, int h, unsigned int c, Uint8 alpha = 255);
+
+  static void draw(const Vector& pos, unsigned int c, Uint8 alpha = 255)
+  {
+    draw(pos.x, pos.y, c, alpha);
+  }
 };
 
 struct TileGroup
diff --git a/src/vector.h b/src/vector.h
new file mode 100644 (file)
index 0000000..6436c3f
--- /dev/null
@@ -0,0 +1,58 @@
+#ifndef __VECTOR_HPP__
+#define __VECTOR_HPP__
+
+class Vector
+{
+public:
+  Vector(float nx, float ny)
+    : x(nx), y(ny)
+  { }
+  Vector(const Vector& other)
+    : x(other.x), y(other.y)
+  { }
+  Vector()
+    : x(0), y(0)
+  { }
+
+  bool operator ==(const Vector& other) const
+  {
+    return x == other.x && y == other.y;
+  }
+
+  const Vector& operator=(const Vector& other)
+  {
+    x = other.x;
+    y = other.y;
+    return *this;
+  }
+
+  Vector operator+(const Vector& other) const
+  {
+    return Vector(x + other.x, y + other.y);
+  }
+
+  Vector operator-(const Vector& other) const
+  {
+    return Vector(x - other.x, y - other.y);
+  }
+
+  Vector operator*(float s) const
+  {
+    return Vector(x * s, y * s);
+  }
+
+  const Vector& operator +=(const Vector& other)
+  {
+    x += other.x;
+    y += other.y;
+    return *this;
+  }
+
+  // ... add the other operators as needed, I'm too lazy now ...
+
+  float x, y; // leave this public, get/set methods just give me headaches
+              // for such simple stuff :)
+};
+
+#endif
+
diff --git a/src/viewport.cpp b/src/viewport.cpp
new file mode 100644 (file)
index 0000000..bd5da54
--- /dev/null
@@ -0,0 +1,16 @@
+#include "viewport.h"
+
+ViewPort::ViewPort()
+{
+}
+
+ViewPort::~ViewPort()
+{
+}
+
+void
+ViewPort::set_translation(const Vector& newtranslation)
+{
+  translation = newtranslation;
+}
+
diff --git a/src/viewport.h b/src/viewport.h
new file mode 100644 (file)
index 0000000..64ef792
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef __VIEWPORT_H__
+#define __VIEWPORT_H__
+
+#include "vector.h"
+#include "rectangle.h"
+
+class ViewPort
+{
+public:
+  ViewPort();
+  ~ViewPort();
+
+  Vector world2screen(const Vector& worldpos) const
+  {
+    return worldpos - translation;
+  }
+    
+  /** returns the current translation (=scroll) vector of the viewport */
+  const Vector& get_translation() const
+  { return translation; }
+
+  void set_translation(const Vector& translation);
+
+private:
+  Vector translation;
+};
+
+#endif
+
index 5c0b0cb..ed049b0 100644 (file)
@@ -33,6 +33,9 @@
 #include "tile.h"
 #include "resources.h"
 #include "gameobjs.h"
+#include "viewport.h"
+#include "display_manager.h"
+#include "background.h"
 
 Surface* img_distro[4];
 
@@ -51,8 +54,16 @@ World::World(const std::string& filename)
 
   get_level()->load_gfx();
   activate_bad_guys();
-  activate_objects();
+  // add background
   activate_particle_systems();
+  Background* bg = new Background(displaymanager);
+  if(level->img_bkgd) {
+    bg->set_image(level->img_bkgd, level->bkgd_speed);
+  } else {
+    bg->set_gradient(level->bkgd_top, level->bkgd_bottom);
+  }
+  gameobjects.push_back(bg);
+  activate_objects();
   get_level()->load_song();
 
   apply_bonuses();
@@ -75,6 +86,13 @@ World::World(const std::string& subset, int level_nr)
   activate_bad_guys();
   activate_objects();
   activate_particle_systems();
+  Background* bg = new Background(displaymanager);
+  if(level->img_bkgd) {
+    bg->set_image(level->img_bkgd, level->bkgd_speed);
+  } else {
+    bg->set_gradient(level->bkgd_top, level->bkgd_bottom);
+  }
+  gameobjects.push_back(bg);
   get_level()->load_song();
 
   apply_bonuses();
@@ -112,26 +130,14 @@ World::~World()
   for (Trampolines::iterator i = trampolines.begin(); i != trampolines.end(); ++i)
     delete *i;
 
-  for (ParticleSystems::iterator i = particle_systems.begin();
-          i != particle_systems.end(); ++i)
-    delete *i;
-
-  for (std::vector<BouncyDistro*>::iterator i = bouncy_distros.begin();
-       i != bouncy_distros.end(); ++i)
-    delete *i;
-  
-  for (std::vector<BrokenBrick*>::iterator i = broken_bricks.begin();
-       i != broken_bricks.end(); ++i)
-    delete *i;
-  
-  for (std::vector<BouncyBrick*>::iterator i = bouncy_bricks.begin();
-       i != bouncy_bricks.end(); ++i)
+  for (std::vector<_GameObject*>::iterator i = gameobjects.begin();
+          i != gameobjects.end(); ++i) {
+    Drawable* drawable = dynamic_cast<Drawable*> (*i);
+    if(drawable)
+      displaymanager.remove_drawable(drawable);
     delete *i;
+  }
 
-  for (std::vector<FloatingScore*>::iterator i = floating_scores.begin();
-       i != floating_scores.end(); ++i)
-    delete *i;
-  
   delete level;
 }
 
@@ -177,11 +183,11 @@ World::activate_particle_systems()
 {
   if (level->particle_system == "clouds")
     {
-      particle_systems.push_back(new CloudParticleSystem);
+      gameobjects.push_back(new CloudParticleSystem(displaymanager));
     }
   else if (level->particle_system == "snow")
     {
-      particle_systems.push_back(new SnowParticleSystem);
+      gameobjects.push_back(new SnowParticleSystem(displaymanager));
     }
   else if (level->particle_system != "")
     {
@@ -195,18 +201,16 @@ World::draw()
   int y,x;
 
   /* Draw the real background */
+#if 0
   drawgradient(level->bkgd_top, level->bkgd_bottom);
   if(level->img_bkgd)
       level->draw_bg();
-
+#endif
     
   /* Draw particle systems (background) */
-  std::vector<ParticleSystem*>::iterator p;
-  for(p = particle_systems.begin(); p != particle_systems.end(); ++p)
-    {
-      (*p)->draw(scroll_x, 0, 0);
-    }
-
+  displaymanager.get_viewport().set_translation(Vector(scroll_x, scroll_y));
+  displaymanager.draw();
+  
   /* Draw background: */
   for (y = 0; y < VISIBLE_TILES_Y && y < level->height; ++y)
     {
@@ -227,10 +231,6 @@ World::draw()
         }
     }
 
-  /* (Bouncy bricks): */
-  for (unsigned int i = 0; i < bouncy_bricks.size(); ++i)
-    bouncy_bricks[i]->draw();
-
   for (BadGuys::iterator i = bad_guys.begin(); i != bad_guys.end(); ++i)
     (*i)->draw();
 
@@ -242,18 +242,9 @@ World::draw()
   for (unsigned int i = 0; i < bullets.size(); ++i)
     bullets[i].draw();
 
-  for (unsigned int i = 0; i < floating_scores.size(); ++i)
-    floating_scores[i]->draw();
-
   for (unsigned int i = 0; i < upgrades.size(); ++i)
     upgrades[i].draw();
 
-  for (unsigned int i = 0; i < bouncy_distros.size(); ++i)
-    bouncy_distros[i]->draw();
-
-  for (unsigned int i = 0; i < broken_bricks.size(); ++i)
-    broken_bricks[i]->draw();
-
   /* Draw foreground: */
   for (y = 0; y < VISIBLE_TILES_Y && y < level->height; ++y)
     {
@@ -263,12 +254,6 @@ World::draw()
                      level->fg_tiles[(int)y + (int)(scroll_y / 32)][(int)x + (int)(scroll_x / 32)]);
         }
     }
-
-  /* Draw particle systems (foreground) */
-  for(p = particle_systems.begin(); p != particle_systems.end(); ++p)
-    {
-      (*p)->draw(scroll_x, 0, 1);
-    }
 }
 
 void
@@ -278,21 +263,6 @@ World::action(double frame_ratio)
   tux.check_bounds(level->back_scrolling, (bool)level->hor_autoscroll_speed);
   scrolling(frame_ratio);
 
-  /* Handle bouncy distros: */
-  for (unsigned int i = 0; i < bouncy_distros.size(); i++)
-    bouncy_distros[i]->action(frame_ratio);
-
-  /* Handle broken bricks: */
-  for (unsigned int i = 0; i < broken_bricks.size(); i++)
-    broken_bricks[i]->action(frame_ratio);
-
-  // Handle all kinds of game objects
-  for (unsigned int i = 0; i < bouncy_bricks.size(); i++)
-    bouncy_bricks[i]->action(frame_ratio);
-  
-  for (unsigned int i = 0; i < floating_scores.size(); i++)
-    floating_scores[i]->action(frame_ratio);
-
   for (unsigned int i = 0; i < bullets.size(); ++i)
     bullets[i].action(frame_ratio);
   
@@ -306,11 +276,9 @@ World::action(double frame_ratio)
      (*i)->action(frame_ratio);
 
   /* update particle systems */
-  std::vector<ParticleSystem*>::iterator p;
-  for(p = particle_systems.begin(); p != particle_systems.end(); ++p)
-    {
-      (*p)->simulate(frame_ratio);
-    }
+  for(std::vector<_GameObject*>::iterator i = gameobjects.begin();
+      i != gameobjects.end(); ++i)
+    (*i)->action(frame_ratio);
 
   /* Handle all possible collisions. */
   collision_handler();
@@ -325,6 +293,20 @@ World::action(double frame_ratio)
       ++i;
     }
   }
+
+  for(std::vector<_GameObject*>::iterator i = gameobjects.begin();
+      i != gameobjects.end(); /* nothing */) {
+    if((*i)->is_valid() == false) {
+      Drawable* drawable = dynamic_cast<Drawable*> (*i);
+      if(drawable)
+        displaymanager.remove_drawable(drawable);
+      
+      delete *i;
+      i = gameobjects.erase(i);
+    } else {
+      ++i;
+    }
+  }
 }
 
 /* the space that it takes for the screen to start scrolling, regarding */
@@ -541,47 +523,40 @@ World::collision_handler()
 }
 
 void
-World::add_score(float x, float y, int s)
+World::add_score(const Vector& pos, int s)
 {
   player_status.score += s;
 
-  FloatingScore* new_floating_score = new FloatingScore();
-  new_floating_score->init(x-scroll_x, y-scroll_y, s);
-  floating_scores.push_back(new_floating_score);
+  gameobjects.push_back(new FloatingScore(displaymanager, pos, s));
 }
 
 void
-World::add_bouncy_distro(float x, float y)
+World::add_bouncy_distro(const Vector& pos)
 {
-  BouncyDistro* new_bouncy_distro = new BouncyDistro();
-  new_bouncy_distro->init(x, y);
-  bouncy_distros.push_back(new_bouncy_distro);
+  gameobjects.push_back(new BouncyDistro(displaymanager, pos));
 }
 
 void
-World::add_broken_brick(Tile* tile, float x, float y)
+World::add_broken_brick(const Vector& pos, Tile* tile)
 {
-  add_broken_brick_piece(tile, x, y, -1, -4);
-  add_broken_brick_piece(tile, x, y + 16, -1.5, -3);
+  add_broken_brick_piece(pos, Vector(-1, -4), tile);
+  add_broken_brick_piece(pos + Vector(0, 16), Vector(-1.5, -3), tile);
 
-  add_broken_brick_piece(tile, x + 16, y, 1, -4);
-  add_broken_brick_piece(tile, x + 16, y + 16, 1.5, -3);
+  add_broken_brick_piece(pos + Vector(16, 0), Vector(1, -4), tile);
+  add_broken_brick_piece(pos + Vector(16, 16), Vector(1.5, -3), tile);
 }
 
 void
-World::add_broken_brick_piece(Tile* tile, float x, float y, float xm, float ym)
+World::add_broken_brick_piece(const Vector& pos, const Vector& movement,
+    Tile* tile)
 {
-  BrokenBrick* new_broken_brick = new BrokenBrick();
-  new_broken_brick->init(tile, x, y, xm, ym);
-  broken_bricks.push_back(new_broken_brick);
+  gameobjects.push_back(new BrokenBrick(displaymanager, tile, pos, movement));
 }
 
 void
-World::add_bouncy_brick(float x, float y)
+World::add_bouncy_brick(const Vector& pos)
 {
-  BouncyBrick* new_bouncy_brick = new BouncyBrick();
-  new_bouncy_brick->init(x,y);
-  bouncy_bricks.push_back(new_bouncy_brick);
+  gameobjects.push_back(new BouncyBrick(displaymanager, pos));
 }
 
 BadGuy*
@@ -674,8 +649,8 @@ World::trybreakbrick(float x, float y, bool small)
       if (tile->data > 0)
         {
           /* Get a distro from it: */
-          add_bouncy_distro(((int)(x + 1) / 32) * 32,
-                                  (int)(y / 32) * 32);
+          add_bouncy_distro(
+              Vector(((int)(x + 1) / 32) * 32, (int)(y / 32) * 32));
 
           // TODO: don't handle this in a global way but per-tile...
           if (!counting_distros)
@@ -705,9 +680,9 @@ World::trybreakbrick(float x, float y, bool small)
           plevel->change(x, y, TM_IA, tile->next_tile);
           
           /* Replace it with broken bits: */
-          add_broken_brick(tile, 
+          add_broken_brick(Vector(
                                  ((int)(x + 1) / 32) * 32,
-                                 (int)(y / 32) * 32);
+                                 (int)(y / 32) * 32), tile);
           
           /* Get some score: */
           play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER);
@@ -739,7 +714,7 @@ World::tryemptybox(float x, float y, Direction col_side)
   switch(tile->data)
     {
     case 1: // Box with a distro!
-      add_bouncy_distro(posx, posy);
+      add_bouncy_distro(Vector(posx, posy));
       play_sound(sounds[SND_DISTRO], SOUND_CENTER_SPEAKER);
       player_status.score = player_status.score + SCORE_DISTRO;
       player_status.distros++;
@@ -788,8 +763,8 @@ World::trygrabdistro(float x, float y, int bounciness)
 
       if (bounciness == BOUNCE)
         {
-          add_bouncy_distro(((int)(x + 1) / 32) * 32,
-                                  (int)(y / 32) * 32);
+          add_bouncy_distro(Vector(((int)(x + 1) / 32) * 32,
+                                  (int)(y / 32) * 32));
         }
 
       player_status.score = player_status.score + SCORE_DISTRO;
index 168059a..05f54d2 100644 (file)
@@ -30,6 +30,7 @@
 #include "badguy.h"
 #include "particlesystem.h"
 #include "gameobjs.h"
+#include "display_manager.h"
 
 class Level;
 
@@ -54,15 +55,12 @@ private:
   static World* current_;
 public:
   BadGuys bad_guys;
-  std::vector<BouncyDistro*> bouncy_distros;
-  std::vector<BrokenBrick*>  broken_bricks;
-  std::vector<BouncyBrick*>  bouncy_bricks;
-  std::vector<FloatingScore*> floating_scores;
 
   std::vector<Upgrade> upgrades;
   std::vector<Bullet> bullets;
-  typedef std::vector<ParticleSystem*> ParticleSystems;
-  ParticleSystems particle_systems;
+  std::vector<_GameObject*> gameobjects;
+
+  DisplayManager displaymanager;
 
 public:
   static World* current() { return current_; }
@@ -95,11 +93,12 @@ public:
   void activate_bad_guys();
   void activate_objects();
 
-  void add_score(float x, float y, int s);
-  void add_bouncy_distro(float x, float y);
-  void add_broken_brick(Tile* tile, float x, float y);
-  void add_broken_brick_piece(Tile* tile, float x, float y, float xm, float ym);
-  void add_bouncy_brick(float x, float y);
+  void add_score(const Vector& pos, int s);
+  void add_bouncy_distro(const Vector& pos);
+  void add_broken_brick(const Vector& pos, Tile* tile);
+  void add_broken_brick_piece(const Vector& pos,
+      const Vector& movement, Tile* tile);
+  void add_bouncy_brick(const Vector& pos);
 
   BadGuy* add_bad_guy(float x, float y, BadGuyKind kind, bool stay_on_platform = false);
   template <class T, class U> T* add_object(U data);