From: Matthias Braun Date: Thu, 20 May 2004 12:12:39 +0000 (+0000) Subject: -Added new object system and converted some GameObjects to it. X-Git-Url: https://git.verplant.org/?a=commitdiff_plain;h=b145cb4377ee4ba2b2c725e3e1f36a27bcae2ff5;p=supertux.git -Added new object system and converted some GameObjects to it. 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 --- diff --git a/src/Makefile.am b/src/Makefile.am index f94c1c56d..b4784a3a6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 # diff --git a/src/badguy.cpp b/src/badguy.cpp index c74e6779b..980462776 100644 --- a/src/badguy.cpp +++ b/src/badguy.cpp @@ -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 index 000000000..4559d59c1 --- /dev/null +++ b/src/display_manager.cpp @@ -0,0 +1,43 @@ +#include "display_manager.h" + +#include + +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 index 000000000..cbc47409b --- /dev/null +++ b/src/display_manager.h @@ -0,0 +1,62 @@ +#ifndef __DISPLAY_MANAGER_H__ +#define __DISPLAY_MANAGER_H__ + +#include + +#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 DisplayList; + DisplayList displaylist; + ViewPort viewport; +}; + +#endif + diff --git a/src/drawable.h b/src/drawable.h new file mode 100644 index 000000000..605c3368e --- /dev/null +++ b/src/drawable.h @@ -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 index 000000000..a3547849e --- /dev/null +++ b/src/game_object.cpp @@ -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 index 000000000..e5df93bef --- /dev/null +++ b/src/game_object.h @@ -0,0 +1,52 @@ +#ifndef __GAMEOBJECT_HPP__ +#define __GAMEOBJECT_HPP__ + +#include + +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 + diff --git a/src/gameloop.cpp b/src/gameloop.cpp index 190700c50..698e02bde 100644 --- a/src/gameloop.cpp +++ b/src/gameloop.cpp @@ -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); } diff --git a/src/gameobjs.cpp b/src/gameobjs.cpp index 8787bb91c..f85c89da9 100644 --- a/src/gameobjs.cpp +++ b/src/gameobjs.cpp @@ -27,73 +27,51 @@ #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::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::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::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::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); diff --git a/src/gameobjs.h b/src/gameobjs.h index b6cec5541..cc1a1daf6 100644 --- a/src/gameobjs.h +++ b/src/gameobjs.h @@ -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; }; diff --git a/src/particlesystem.cpp b/src/particlesystem.cpp index e303838d3..83705363e 100644 --- a/src/particlesystem.cpp +++ b/src/particlesystem.cpp @@ -25,11 +25,15 @@ #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::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::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::iterator i; for(i = particles.begin(); i != particles.end(); ++i) { diff --git a/src/particlesystem.h b/src/particlesystem.h index 01ba5862d..f0b86b751 100644 --- a/src/particlesystem.h +++ b/src/particlesystem.h @@ -22,6 +22,10 @@ #include #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 @@ -38,15 +42,13 @@ * 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 diff --git a/src/text.h b/src/text.h index 87066d7e2..0e6d7ce30 100644 --- a/src/text.h +++ b/src/text.h @@ -23,6 +23,7 @@ #include #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*/ diff --git a/src/texture.h b/src/texture.h index 3ba0e58a0..00ad954b8 100644 --- a/src/texture.h +++ b/src/texture.h @@ -29,6 +29,7 @@ #include #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 diff --git a/src/tile.h b/src/tile.h index 4d4f0082b..a94a19c36 100644 --- a/src/tile.h +++ b/src/tile.h @@ -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 index 000000000..6436c3f5a --- /dev/null +++ b/src/vector.h @@ -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 index 000000000..bd5da54b6 --- /dev/null +++ b/src/viewport.cpp @@ -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 index 000000000..64ef79213 --- /dev/null +++ b/src/viewport.h @@ -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 + diff --git a/src/world.cpp b/src/world.cpp index 5c0b0cbb4..ed049b03d 100644 --- a/src/world.cpp +++ b/src/world.cpp @@ -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::iterator i = bouncy_distros.begin(); - i != bouncy_distros.end(); ++i) - delete *i; - - for (std::vector::iterator i = broken_bricks.begin(); - i != broken_bricks.end(); ++i) - delete *i; - - for (std::vector::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 (*i); + if(drawable) + displaymanager.remove_drawable(drawable); delete *i; + } - for (std::vector::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::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::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 (*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; diff --git a/src/world.h b/src/world.h index 168059a64..05f54d23d 100644 --- a/src/world.h +++ b/src/world.h @@ -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 bouncy_distros; - std::vector broken_bricks; - std::vector bouncy_bricks; - std::vector floating_scores; std::vector upgrades; std::vector bullets; - typedef std::vector 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 T* add_object(U data);