From: Matthias Braun Date: Mon, 31 May 2004 02:40:30 +0000 (+0000) Subject: big refactoring of level and world class. A level is now basically a set of X-Git-Url: https://git.verplant.org/?a=commitdiff_plain;h=2074a5e3f8167dec24989c008ddadda14687a3a6;p=supertux.git big refactoring of level and world class. A level is now basically a set of sectors (or sublevels). The Sector class has been merged with the old world class. Also I've rewritten some parts of the load/save code and changed the fileformat a bit to support several sectors. On the way I fixed some bugs (and maybe introduce new ones...) SVN-Revision: 1371 --- diff --git a/data/levels/misc/menu.stl b/data/levels/misc/menu.stl index 6e5bdce61..1914f7343 100644 --- a/data/levels/misc/menu.stl +++ b/data/levels/misc/menu.stl @@ -6,7 +6,7 @@ (music "theme.mod") (background "arctis.jpg") (particle_system "") - (bkgd_speed "2") + (bkgd_speed 0.5) (bkgd_red_top 0) (bkgd_green_top 0) (bkgd_blue_top 0) diff --git a/data/levels/test/level10.stl b/data/levels/test/level10.stl index 2948f6ecf..ae0b31d25 100644 --- a/data/levels/test/level10.stl +++ b/data/levels/test/level10.stl @@ -6,7 +6,7 @@ (music "forest.mod") (background "cave2.jpg") (particle_system "") - (bkgd_speed 50) + (bkgd_speed 0.5) (bkgd_red_top 150) (bkgd_green_top 200) (bkgd_blue_top 255) diff --git a/data/levels/test/level11.stl b/data/levels/test/level11.stl new file mode 100644 index 000000000..90c04b12d --- /dev/null +++ b/data/levels/test/level11.stl @@ -0,0 +1,54 @@ +(supertux-level + (version 2) + (name "Sector Test") + (author "Matthias Braun") + (time 500) + (sector + (name "main") + (gravity 10) + (camera + (mode "normal") + ) + (playerspawn + (name "main") + (x 100) + (y 170) + ) + (tilemap + (layer "interactive") + (solid #t) + (speed 1.) + (width 50) + (height 15) + (tiles + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 48 0 0 0 0 0 0 0 48 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 27 28 28 28 28 28 28 28 29 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 48 0 0 48 0 0 48 0 0 0 0 0 0 48 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 48 48 48 48 48 48 48 48 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 48 0 0 0 0 0 0 0 48 0 0 0 48 0 0 0 0 0 0 48 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 7 8 8 8 8 8 48 0 0 0 0 7 8 8 48 76 76 76 48 8 8 8 8 8 8 48 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 + 28 28 28 28 28 28 28 28 28 28 28 28 28 28 48 75 75 75 48 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14 14 + 10 11 11 11 11 11 11 11 11 11 11 11 11 11 48 48 48 48 48 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 + 10 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 + ) + ) + (background + (image "arctis.jpg") + (speed 0.5) + ) + (fish (x 509) (y 181)) + (flyingsnowball (x 941) (y 222)) + (spiky (x 656) (y 306)) + (snowball (x 259) (y 303)) + (stalactite (x 1159) (y 288)) + (stalactite (x 1235) (y 288)) + (laptop (x 1198) (y 186)) + (mriceblock (x 323) (y 74)) + ) +) diff --git a/data/levels/test/level4.stl b/data/levels/test/level4.stl index 33fc08a21..625040c15 100644 --- a/data/levels/test/level4.stl +++ b/data/levels/test/level4.stl @@ -6,7 +6,7 @@ (music "Mortimers_chipdisko.mod") (background "") (particle_system "") - (bkgd_speed 50) + (bkgd_speed 0.5) (bkgd_red_top 0) (bkgd_green_top 0) (bkgd_blue_top 255) diff --git a/data/levels/test/level5.stl b/data/levels/test/level5.stl index 21630285f..01c39bade 100644 --- a/data/levels/test/level5.stl +++ b/data/levels/test/level5.stl @@ -6,8 +6,8 @@ (music "Mortimers_chipdisko.mod") (background "") (particle_system "") - (bkgd_speed 50) - (bkgd_red_top 150) + (bkgd_speed 0.5) + (bkgd_red_top 50) (bkgd_green_top 150) (bkgd_blue_top 150) (bkgd_red_bottom 150) diff --git a/data/levels/test/level7.stl b/data/levels/test/level7.stl index 7b02be74a..5cd517ff0 100644 --- a/data/levels/test/level7.stl +++ b/data/levels/test/level7.stl @@ -6,7 +6,7 @@ (music "Mortimers_chipdisko.mod") (background "") (particle_system "") - (bkgd_speed 50) + (bkgd_speed 0.5) (bkgd_red_top 150) (bkgd_green_top 150) (bkgd_blue_top 150) diff --git a/data/levels/world1/level10.stl b/data/levels/world1/level10.stl index 32623abb0..1944e44b8 100644 --- a/data/levels/world1/level10.stl +++ b/data/levels/world1/level10.stl @@ -89,7 +89,10 @@ (point (x 7889) (y 327)) ) (objects - (mriceblock (x 613) (y 367)) + (mriceblock + (x 613) + (y 367) + ) (mrbomb (x 5833) (y 353)) (mrbomb (x 6091) (y 334)) (money (x 6640) (y 288)) diff --git a/src/Makefile.am b/src/Makefile.am index bbe31307f..023536d6f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -59,8 +59,6 @@ title.cpp \ title.h \ type.cpp \ type.h \ -world.cpp \ -world.h \ worldmap.cpp \ worldmap.h \ tile.h \ @@ -92,6 +90,8 @@ moving_object.h \ moving_object.cpp \ serializable.h \ vector.cpp \ -vector.h +vector.h \ +sector.cpp \ +sector.h # EOF # diff --git a/src/background.cpp b/src/background.cpp index 14584e967..1effc1671 100644 --- a/src/background.cpp +++ b/src/background.cpp @@ -21,18 +21,59 @@ #include "globals.h" #include "camera.h" #include "screen/drawing_context.h" +#include "lispwriter.h" Background::Background() : type(INVALID), image(0) { } +Background::Background(LispReader& reader) + : type(INVALID), image(0) +{ + if(reader.read_string("image", imagefile) + && reader.read_float("speed", speed)) { + set_image(imagefile, speed); + } + + int tr, tg, tb, br, bg, bb; + if(reader.read_int("top_red", tr) && reader.read_int("top_green", tg) + && reader.read_int("top_blue", tb) && reader.read_int("bottom_red", br) + && reader.read_int("bottom_green", br) + && reader.read_int("bottom_blue", bb)) { + set_gradient(Color(tr, tg, tb), Color(br, bg, bb)); + } +} + Background::~Background() { delete image; } void +Background::write(LispWriter& writer) +{ + if(type == INVALID) + return; + + writer.start_list("background"); + + if(type == IMAGE) { + writer.write_string("image", imagefile); + writer.write_float("speed", speed); + } else if(type == GRADIENT) { + writer.write_int("top_red", gradient_top.red); + writer.write_int("top_green", gradient_top.green); + writer.write_int("top_blue", gradient_top.blue); + writer.write_int("bottom_red", gradient_bottom.red); + writer.write_int("bottom_green", gradient_bottom.green); + writer.write_int("bottom_blue", gradient_bottom.blue); + } + + writer.end_list("background"); +} + +void Background::action(float) { } @@ -40,7 +81,8 @@ Background::action(float) void Background::set_image(const std::string& name, float speed) { - type = IMAGE; + this->type = IMAGE; + this->imagefile = name; this->speed = speed; delete image; @@ -61,9 +103,9 @@ Background::draw(DrawingContext& context) if(type == GRADIENT) { context.draw_gradient(gradient_top, gradient_bottom, LAYER_BACKGROUND0); } else if(type == IMAGE) { - int sx = int(-context.get_translation().x * float(speed/100.)) + int sx = int(-context.get_translation().x * speed) % image->w - image->w; - int sy = int(-context.get_translation().y * float(speed/100.)) + int sy = int(-context.get_translation().y * speed) % image->h - image->h; context.push_transform(); context.set_translation(Vector(0, 0)); diff --git a/src/background.h b/src/background.h index c441c50d4..07d33f1ce 100644 --- a/src/background.h +++ b/src/background.h @@ -22,15 +22,20 @@ #include "screen/texture.h" #include "screen/drawing_context.h" #include "game_object.h" +#include "lispreader.h" +#include "serializable.h" class DisplayManager; -class Background : public GameObject +class Background : public GameObject, public Serializable { public: Background(); + Background(LispReader& reader); virtual ~Background(); + virtual void write(LispWriter& writer); + void set_image(const std::string& name, float bkgd_speed); void set_gradient(Color top, Color bottom); @@ -45,6 +50,7 @@ private: }; Type type; + std::string imagefile; float speed; Surface* image; Color gradient_top, gradient_bottom; diff --git a/src/badguy.cpp b/src/badguy.cpp index 87ef1b75a..97913c736 100644 --- a/src/badguy.cpp +++ b/src/badguy.cpp @@ -29,10 +29,11 @@ #include "tile.h" #include "resources.h" #include "sprite_manager.h" -#include "world.h" #include "camera.h" #include "lispwriter.h" #include "level.h" +#include "sector.h" +#include "tilemap.h" Sprite* img_mriceblock_flat_left; Sprite* img_mriceblock_flat_right; @@ -106,8 +107,7 @@ BadGuyKind badguykind_from_string(const std::string& str) return BAD_WALKINGTREE; else { - printf("Couldn't convert badguy: '%s'\n", str.c_str()); - return BAD_SNOWBALL; + return BAD_INVALID; } } @@ -158,13 +158,13 @@ std::string badguykind_to_string(BadGuyKind kind) BadGuy::BadGuy(BadGuyKind kind_, LispReader& lispreader) : removable(false), squishcount(0) { - lispreader.read_float("x", &start_position.x); - lispreader.read_float("y", &start_position.y); + lispreader.read_float("x", start_position.x); + lispreader.read_float("y", start_position.y); kind = kind_; stay_on_platform = false; - lispreader.read_bool("stay-on-platform", &stay_on_platform); + lispreader.read_bool("stay-on-platform", stay_on_platform); init(); } @@ -214,8 +214,8 @@ BadGuy::init() --base.y; } - if(World::current()->camera) { - Vector scroll = World::current()->camera->get_translation(); + if(Sector::current() && Sector::current()->camera) { + Vector scroll = Sector::current()->camera->get_translation(); if(start_position.x > scroll.x - X_OFFSCREEN_DISTANCE && start_position.x < scroll.x + screen->w + X_OFFSCREEN_DISTANCE && @@ -223,6 +223,10 @@ BadGuy::init() start_position.y < scroll.y + screen->h + Y_OFFSCREEN_DISTANCE) { activate(LEFT); } + } else { + if(start_position.x > 0 && start_position.x <= screen->w + && start_position.y > 0 && start_position.y <= screen->h) + activate(LEFT); } } @@ -307,7 +311,7 @@ BadGuy::activate(Direction activation_dir) void BadGuy::action_mriceblock(double elapsed_time) { - Player& tux = *World::current()->get_tux(); + Player& tux = *Sector::current()->player; if(mode != HELD) fall(); @@ -362,7 +366,7 @@ BadGuy::action_mriceblock(double elapsed_time) check_horizontal_bump(); if(mode == KICK && changed != dir) { - float scroll_x = World::current()->camera->get_translation().x; + float scroll_x = Sector::current()->camera->get_translation().x; /* handle stereo sound (number 10 should be tweaked...)*/ if (base.x < scroll_x + screen->w/2 - 10) @@ -393,7 +397,7 @@ BadGuy::check_horizontal_bump(bool checkcliff) if (dir == LEFT && issolid( base.x, (int) base.y + halfheight)) { if (kind == BAD_MRICEBLOCK && mode == KICK) - World::current()->trybreakbrick(base.x, (int) base.y + halfheight, false); + Sector::current()->trybreakbrick(Vector(base.x, base.y + halfheight), false); dir = RIGHT; physic.set_velocity(-physic.get_velocity_x(), physic.get_velocity_y()); @@ -402,7 +406,8 @@ BadGuy::check_horizontal_bump(bool checkcliff) if (dir == RIGHT && issolid( base.x + base.width, (int)base.y + halfheight)) { if (kind == BAD_MRICEBLOCK && mode == KICK) - World::current()->trybreakbrick(base.x + base.width, (int) base.y + halfheight, false); + Sector::current()->trybreakbrick( + Vector(base.x + base.width, (int) base.y + halfheight), false); dir = LEFT; physic.set_velocity(-physic.get_velocity_x(), physic.get_velocity_y()); @@ -496,7 +501,7 @@ BadGuy::action_jumpy(double elapsed_time) else set_sprite(img_jumpy_left_up, img_jumpy_left_up); - Player& tux = *World::current()->get_tux(); + Player& tux = *Sector::current()->player; static const float JUMPV = 6; @@ -563,7 +568,7 @@ BadGuy::action_bomb(double elapsed_time) dying = DYING_NOT; // now the bomb hurts timer.start(EXPLODETIME); - float scroll_x = World::current()->camera->get_translation().x; + float scroll_x = Sector::current()->camera->get_translation().x; /* play explosion sound */ // FIXME: is the stereo all right? maybe we should use player cordinates... if (base.x < scroll_x + screen->w/2 - 10) @@ -587,7 +592,7 @@ BadGuy::action_bomb(double elapsed_time) void BadGuy::action_stalactite(double elapsed_time) { - Player& tux = *World::current()->get_tux(); + Player& tux = *Sector::current()->player; static const int SHAKETIME = 800; static const int RANGE = 40; @@ -798,7 +803,7 @@ BadGuy::action_wingling(double elapsed_time) physic.enable_gravity(true); else { - Player& tux = *World::current()->get_tux(); + Player& tux = *Sector::current()->player; int dirsign = physic.get_velocity_x() < 0 ? -1 : 1; if (fabsf(tux.base.x - base.x) < 150 && base.y < tux.base.y && tux.dying == DYING_NOT) @@ -827,7 +832,7 @@ BadGuy::action_wingling(double elapsed_time) void BadGuy::action_walkingtree(double elapsed_time) { - Player& tux = *World::current()->get_tux(); + Player& tux = *Sector::current()->player; Direction v_dir = physic.get_velocity_x() < 0 ? LEFT : RIGHT; if (dying == DYING_NOT) @@ -862,11 +867,11 @@ BadGuy::action_walkingtree(double elapsed_time) void BadGuy::action(float elapsed_time) { - float scroll_x = World::current()->camera->get_translation().x; - float scroll_y = World::current()->camera->get_translation().y; + float scroll_x = Sector::current()->camera->get_translation().x; + float scroll_y = Sector::current()->camera->get_translation().y; // BadGuy fall below the ground - if (base.y > World::current()->get_level()->height * 32) { + if (base.y > Sector::current()->solids->get_height() * 32) { remove_me(); return; } @@ -1052,7 +1057,7 @@ BadGuy::squish_me(Player* player) { make_player_jump(player); - World::current()->add_score(Vector(base.x, base.y), + Sector::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++; @@ -1072,7 +1077,7 @@ BadGuy::squish(Player* player) explode(false); make_player_jump(player); - World::current()->add_score(Vector(base.x, base.y), + Sector::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++; @@ -1124,7 +1129,7 @@ BadGuy::squish(Player* player) make_player_jump(player); - World::current()->add_score(Vector(base.x, base.y), + Sector::current()->add_score(Vector(base.x, base.y), 25 * player_status.score_multiplier); player_status.score_multiplier++; @@ -1156,7 +1161,7 @@ BadGuy::squish(Player* player) make_player_jump(player); base.y += 66 - base.height; - World::current()->add_score(Vector(base.x, base.y), + Sector::current()->add_score(Vector(base.x, base.y), 25 * player_status.score_multiplier); player_status.score_multiplier++; @@ -1178,7 +1183,7 @@ BadGuy::kill_me(int score) set_sprite(img_mriceblock_falling_left, img_mriceblock_falling_right); if(mode == HELD) { mode = NORMAL; - Player& tux = *World::current()->get_tux(); + Player& tux = *Sector::current()->player; tux.holding_something = false; } } @@ -1187,7 +1192,7 @@ BadGuy::kill_me(int score) /* Gain some points: */ if (score != 0) - World::current()->add_score(Vector(base.x, base.y), + Sector::current()->add_score(Vector(base.x, base.y), score * player_status.score_multiplier); /* Play death sound: */ @@ -1197,7 +1202,7 @@ BadGuy::kill_me(int score) void BadGuy::explode(bool right_way) { - BadGuy *badguy = World::current()->add_bad_guy(base.x, base.y, BAD_BOMB); + BadGuy *badguy = Sector::current()->add_bad_guy(base.x, base.y, BAD_BOMB); if(right_way) { badguy->timer.start(0); diff --git a/src/badguy.h b/src/badguy.h index d1a6b843d..c6b6eeca7 100644 --- a/src/badguy.h +++ b/src/badguy.h @@ -28,6 +28,7 @@ #include "screen/texture.h" #include "physic.h" #include "sprite.h" +#include "defines.h" #include "moving_object.h" #include "collision.h" #include "serializable.h" @@ -47,7 +48,9 @@ enum BadGuyKind { BAD_SNOWBALL, BAD_WINGLING, BAD_WALKINGTREE, - NUM_BadGuyKinds + NUM_BadGuyKinds, + + BAD_INVALID }; BadGuyKind badguykind_from_string(const std::string& str); diff --git a/src/camera.cpp b/src/camera.cpp index e8cd4f5cd..6985fd7c9 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -23,18 +23,16 @@ #include #include "lispwriter.h" #include "player.h" -#include "level.h" +#include "tilemap.h" +#include "gameloop.h" #include "globals.h" -#include "world.h" +#include "sector.h" -Camera::Camera(Player* newplayer, Level* newlevel) - : player(newplayer), level(newlevel), do_backscrolling(true), - scrollchange(NONE), auto_idx(0), auto_t(0) +Camera::Camera(Sector* newsector) + : sector(newsector), do_backscrolling(true), scrollchange(NONE), + auto_idx(0), auto_t(0) { - if(!player || !level) - mode = MANUAL; - else - mode = NORMAL; + mode = NORMAL; } Camera::~Camera() @@ -44,7 +42,7 @@ Camera::~Camera() const Vector& Camera::get_translation() const { - return World::current()->context.get_translation(); + return translation; } void @@ -52,17 +50,17 @@ Camera::read(LispReader& reader) { std::string modename; - reader.read_string("mode", &modename); + reader.read_string("mode", modename); if(modename == "normal") { mode = NORMAL; do_backscrolling = true; - reader.read_bool("backscrolling", &do_backscrolling); + reader.read_bool("backscrolling", do_backscrolling); } else if(modename == "autoscroll") { mode = AUTOSCROLL; lisp_object_t* cur = 0; - reader.read_lisp("path", &cur); + reader.read_lisp("path", cur); if(cur == 0) { throw std::runtime_error("No path specified in autoscroll camera."); } @@ -76,11 +74,11 @@ Camera::read(LispReader& reader) LispReader reader(lisp_cdr(lisp_car(cur))); ScrollPoint point; - if(!reader.read_float("x", &point.position.x) || - !reader.read_float("y", &point.position.y)) { + if(!reader.read_float("x", point.position.x) || + !reader.read_float("y", point.position.y)) { throw std::runtime_error("x and y missing in point of camerapath"); } - reader.read_float("speed", &speed); + reader.read_float("speed", speed); point.speed = speed; scrollpoints.push_back(point); @@ -123,30 +121,39 @@ Camera::write(LispWriter& writer) writer.end_list("camera"); } +void +Camera::reset(const Vector& tuxpos) +{ + translation.x = tuxpos.x - screen->w/3 * 2; + translation.y = tuxpos.y - screen->h/2; + keep_in_bounds(); +} + static const float EPSILON = .00001; static const float max_speed_y = 1.4; void Camera::action(float elapsed_time) { - translation = World::current()->context.get_translation(); if(mode == NORMAL) scroll_normal(elapsed_time); else if(mode == AUTOSCROLL) scroll_autoscroll(elapsed_time); - World::current()->context.set_translation(translation); } void Camera::keep_in_bounds() { + float width = sector->solids->get_width() * 32; + float height = sector->solids->get_height() * 32; + // don't scroll before the start or after the level's end - if(translation.y > level->height * 32 - screen->h) - translation.y = level->height * 32 - screen->h; + if(translation.y > height - screen->h) + translation.y = height - screen->h; if(translation.y < 0) translation.y = 0; - if(translation.x > level->width * 32 - screen->w) - translation.x = level->width * 32 - screen->w; + if(translation.x > width - screen->w) + translation.x = width - screen->w; if(translation.x < 0) translation.x = 0; } @@ -154,7 +161,8 @@ Camera::keep_in_bounds() void Camera::scroll_normal(float elapsed_time) { - assert(level != 0 && player != 0); + assert(sector != 0); + Player* player = sector->player; // check that we don't have division by zero later if(elapsed_time < EPSILON) @@ -163,7 +171,7 @@ Camera::scroll_normal(float elapsed_time) /****** Vertical Scrolling part ******/ bool do_y_scrolling = true; - if(player->dying || level->height == 19) + if(player->dying || sector->solids->get_height() == 19) do_y_scrolling = false; if(do_y_scrolling) { @@ -241,6 +249,8 @@ Camera::scroll_normal(float elapsed_time) void Camera::scroll_autoscroll(float elapsed_time) { + Player* player = sector->player; + if(player->dying) return; diff --git a/src/camera.h b/src/camera.h index b64e71d89..648084ee3 100644 --- a/src/camera.h +++ b/src/camera.h @@ -26,13 +26,12 @@ #include class LispReader; -class Player; -class Level; +class Sector; class Camera : public GameObject, public Serializable { public: - Camera(Player* player = 0, Level* level = 0); + Camera(Sector* sector); virtual ~Camera(); /// parse camera mode from lisp file @@ -40,6 +39,9 @@ public: /// write camera mode to a lisp file virtual void write(LispWriter& writer); + /// reset camera postion + virtual void reset(const Vector& tuxpos); + /** @deprecated@ */ const Vector& get_translation() const; @@ -68,8 +70,7 @@ private: Vector translation; - Player* player; - Level* level; + Sector* sector; // normal mode bool do_backscrolling; diff --git a/src/collision.cpp b/src/collision.cpp index d431f40a2..e36327f5b 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -22,8 +22,8 @@ #include "collision.h" #include "bitmask.h" #include "scene.h" -#include "world.h" -#include "level.h" +#include "sector.h" +#include "tilemap.h" #include "tile.h" bool rectcollision(const base_type& one, const base_type& two) @@ -44,11 +44,7 @@ bool rectcollision_offset(const base_type& one, const base_type& two, float off_ bool collision_object_map(const base_type& base) { - if(!World::current()) - return false; - - const Level& level = *World::current()->get_level(); - TileManager& tilemanager = *TileManager::instance(); + const TileMap& tilemap = *Sector::current()->solids; // we make the collision rectangle 1 pixel smaller int starttilex = int(base.x+1) / 32; @@ -58,8 +54,8 @@ bool collision_object_map(const base_type& base) for(int x = starttilex; x*32 < max_x; ++x) { for(int y = starttiley; y*32 < max_y; ++y) { - Tile* tile = tilemanager.get(level.get_tile_at(x, y)); - if(tile && (tile->attributes & Tile::SOLID)) + Tile* tile = tilemap.get_tile(x, y); + if(tile->attributes & Tile::SOLID) return true; } } @@ -69,8 +65,7 @@ bool collision_object_map(const base_type& base) void* collision_func(const base_type& base, tiletestfunction function) { - const Level& level = *World::current()->get_level(); - TileManager& tilemanager = *TileManager::instance(); + const TileMap& tilemap = *Sector::current()->solids; int starttilex = int(base.x) / 32; int starttiley = int(base.y) / 32; @@ -79,7 +74,7 @@ void* collision_func(const base_type& base, tiletestfunction function) for(int x = starttilex; x*32 < max_x; ++x) { for(int y = starttiley; y*32 < max_y; ++y) { - Tile* tile = tilemanager.get(level.get_tile_at(x, y)); + Tile* tile = tilemap.get_tile(x, y); void* result = function(tile); if(result != 0) return result; @@ -242,7 +237,8 @@ void collision_swept_object_map(base_type* old, base_type* current) Tile* gettile(float x, float y) { - return TileManager::instance()->get(World::current()->get_level()->gettileid(x, y)); + const TileMap& tilemap = *Sector::current()->solids; + return tilemap.get_tile_at(Vector(x, y)); } bool issolid(float x, float y) diff --git a/src/collision.h b/src/collision.h index b6c243e2e..399366544 100644 --- a/src/collision.h +++ b/src/collision.h @@ -24,7 +24,6 @@ #include "type.h" class Tile; -class World; /* Collision objects */ enum diff --git a/src/configfile.cpp b/src/configfile.cpp index 5be9d1ae3..33447a10e 100644 --- a/src/configfile.cpp +++ b/src/configfile.cpp @@ -74,36 +74,36 @@ void loadconfig(void) LispReader reader(lisp_cdr(root_obj)); - reader.read_bool("fullscreen", &use_fullscreen); - reader.read_bool("sound", &use_sound); - reader.read_bool("music", &use_music); - reader.read_bool("show_fps", &show_fps); + reader.read_bool("fullscreen", use_fullscreen); + reader.read_bool("sound", use_sound); + reader.read_bool("music", use_music); + reader.read_bool("show_fps", show_fps); std::string video; - reader.read_string ("video", &video); + reader.read_string ("video", video); if (video == "opengl") use_gl = true; else use_gl = false; - reader.read_int ("joystick", &joystick_num); + reader.read_int ("joystick", joystick_num); if (!(joystick_num >= 0)) use_joystick = false; else use_joystick = true; - reader.read_int ("joystick-x", &joystick_keymap.x_axis); - reader.read_int ("joystick-y", &joystick_keymap.y_axis); - reader.read_int ("joystick-a", &joystick_keymap.a_button); - reader.read_int ("joystick-b", &joystick_keymap.b_button); - reader.read_int ("joystick-start", &joystick_keymap.start_button); - reader.read_int ("joystick-deadzone", &joystick_keymap.dead_zone); + reader.read_int ("joystick-x", joystick_keymap.x_axis); + reader.read_int ("joystick-y", joystick_keymap.y_axis); + reader.read_int ("joystick-a", joystick_keymap.a_button); + reader.read_int ("joystick-b", joystick_keymap.b_button); + reader.read_int ("joystick-start", joystick_keymap.start_button); + reader.read_int ("joystick-deadzone", joystick_keymap.dead_zone); - reader.read_int ("keyboard-jump", &keymap.jump); - reader.read_int ("keyboard-duck", &keymap.duck); - reader.read_int ("keyboard-left", &keymap.left); - reader.read_int ("keyboard-right", &keymap.right); - reader.read_int ("keyboard-fire", &keymap.fire); + reader.read_int ("keyboard-jump", keymap.jump); + reader.read_int ("keyboard-duck", keymap.duck); + reader.read_int ("keyboard-left", keymap.left); + reader.read_int ("keyboard-right", keymap.right); + reader.read_int ("keyboard-fire", keymap.fire); lisp_free(root_obj); } @@ -111,7 +111,6 @@ void loadconfig(void) void saveconfig (void) { /* write settings to config file */ - FILE * config = opendata(config_filename, "w"); if(config) diff --git a/src/gameloop.cpp b/src/gameloop.cpp index ac9ad58e5..7b7afc9d4 100644 --- a/src/gameloop.cpp +++ b/src/gameloop.cpp @@ -44,7 +44,7 @@ #include "high_scores.h" #include "menu.h" #include "badguy.h" -#include "world.h" +#include "sector.h" #include "special.h" #include "player.h" #include "level.h" @@ -54,13 +54,14 @@ #include "particlesystem.h" #include "resources.h" #include "background.h" +#include "tilemap.h" #include "music_manager.h" GameSession* GameSession::current_ = 0; -GameSession::GameSession(const std::string& subset_, int levelnb_, int mode) - : world(0), st_gl_mode(mode), levelnb(levelnb_), end_sequence(NO_ENDSEQUENCE), - subset(subset_) +GameSession::GameSession(const std::string& levelname_, int mode) + : level(0), currentsector(0), st_gl_mode(mode), + end_sequence(NO_ENDSEQUENCE), levelname(levelname_) { current_ = this; @@ -71,6 +72,8 @@ GameSession::GameSession(const std::string& subset_, int levelnb_, int mode) fps_timer.init(true); frame_timer.init(true); + context = new DrawingContext(); + restart_level(); } @@ -84,28 +87,25 @@ GameSession::restart_level() fps_timer.init(true); frame_timer.init(true); +#if 0 float old_x_pos = -1; - if (world) { // Tux has lost a life, so we try to respawn him at the nearest reset point old_x_pos = world->get_tux()->base.x; } +#endif - delete world; + delete level; + currentsector = 0; - if (st_gl_mode == ST_GL_LOAD_LEVEL_FILE) - { - world = new World(subset); - } - else if (st_gl_mode == ST_GL_DEMO_GAME) - { - world = new World(subset); - } - else - { - world = new World(subset, levelnb); - } + level = new Level; + level->load(levelname); + currentsector = level->get_sector("main"); + if(!currentsector) + st_abort("Level has no main sector.", ""); + currentsector->activate("main"); +#if 0 // TODO // Set Tux to the nearest reset point if (old_x_pos != -1) { @@ -123,6 +123,7 @@ GameSession::restart_level() world->get_tux()->base.y = best_reset_point.y; } } +#endif if (st_gl_mode != ST_GL_DEMO_GAME) { @@ -132,12 +133,13 @@ GameSession::restart_level() time_left.init(true); start_timers(); - world->play_music(LEVEL_MUSIC); + currentsector->play_music(LEVEL_MUSIC); } GameSession::~GameSession() { - delete world; + delete level; + delete context; } void @@ -148,16 +150,18 @@ GameSession::levelintro(void) char str[60]; DrawingContext context; - world->background->draw(context); + currentsector->background->draw(context); - sprintf(str, "%s", world->get_level()->name.c_str()); - context.draw_text_center(gold_text, str, Vector(0, 220), 0); + context.draw_text_center(gold_text, level->get_name(), Vector(0, 220), + LAYER_FOREGROUND1); sprintf(str, "TUX x %d", player_status.lives); - context.draw_text_center(white_text, str, Vector(0, 240), 0); + context.draw_text_center(white_text, str, Vector(0, 240), + LAYER_FOREGROUND1); - sprintf(str, "by %s", world->get_level()->author.c_str()); - context.draw_text_center(white_small_text, str, Vector(0, 400), 0); + context.draw_text_center(white_small_text, + std::string("by ") + level->get_author(), + Vector(0, 400), LAYER_FOREGROUND1); context.do_drawing(); @@ -169,7 +173,7 @@ GameSession::levelintro(void) void GameSession::start_timers() { - time_left.start(world->get_level()->time_left*1000); + time_left.start(level->time_left*1000); st_pause_ticks_init(); update_time = st_get_ticks(); } @@ -177,8 +181,9 @@ GameSession::start_timers() void GameSession::on_escape_press() { - if(world->get_tux()->dying || end_sequence != NO_ENDSEQUENCE) + if(currentsector->player->dying || end_sequence != NO_ENDSEQUENCE) return; // don't let the player open the menu, when he is dying + if(game_pause) return; @@ -191,7 +196,7 @@ GameSession::on_escape_press() /* Tell Tux that the keys are all down, otherwise it could have nasty bugs, like going allways to the right or whatever that key does */ - Player& tux = *world->get_tux(); + Player& tux = *(currentsector->player); tux.key_event((SDLKey)keymap.jump, UP); tux.key_event((SDLKey)keymap.duck, UP); tux.key_event((SDLKey)keymap.left, UP); @@ -208,7 +213,7 @@ GameSession::process_events() { if (end_sequence != NO_ENDSEQUENCE) { - Player& tux = *world->get_tux(); + Player& tux = *currentsector->player; tux.input.fire = UP; tux.input.left = UP; @@ -277,7 +282,7 @@ GameSession::process_events() } else { - Player& tux = *world->get_tux(); + Player& tux = *currentsector->player; switch(event.type) { @@ -429,10 +434,10 @@ GameSession::process_events() void GameSession::check_end_conditions() { - Player* tux = world->get_tux(); + Player* tux = currentsector->player; /* End of level? */ - int endpos = (World::current()->get_level()->width-5) * 32; + int endpos = (currentsector->solids->get_width() - 5) * 32; Tile* endtile = collision_goal(tux->base); // fallback in case the other endpositions don't trigger @@ -481,52 +486,45 @@ GameSession::check_end_conditions() void GameSession::action(double frame_ratio) { - if (exit_status == ES_NONE && !world->get_tux()->growing_timer.check()) + if (exit_status == ES_NONE && !currentsector->player->growing_timer.check()) { // Update Tux and the World - world->action(frame_ratio); + currentsector->action(frame_ratio); } } void GameSession::draw() { - DrawingContext& context = world->context; - - world->draw(); - drawstatus(context); + currentsector->draw(*context); + drawstatus(*context); if(game_pause) { - context.push_transform(); - context.set_translation(Vector(0, 0)); - int x = screen->h / 20; for(int i = 0; i < x; ++i) { - context.draw_filled_rect( + context->draw_filled_rect( Vector(i % 2 ? (pause_menu_frame * i)%screen->w : -((pause_menu_frame * i)%screen->w) ,(i*20+pause_menu_frame)%screen->h), Vector(screen->w,10), Color(20,20,20, rand() % 20 + 1), LAYER_FOREGROUND1+1); } - context.draw_filled_rect( + context->draw_filled_rect( Vector(0,0), Vector(screen->w, screen->h), Color(rand() % 50, rand() % 50, rand() % 50, 128), LAYER_FOREGROUND1); - world->context.draw_text_center(blue_text, "PAUSE - Press 'P' To Play", + context->draw_text_center(blue_text, "PAUSE - Press 'P' To Play", Vector(0, 230), LAYER_FOREGROUND1+2); - - context.pop_transform(); } if(Menu::current()) { - Menu::current()->draw(context); - mouse_cursor->draw(context); + Menu::current()->draw(*context); + mouse_cursor->draw(*context); } - context.do_drawing(); + context->do_drawing(); } void @@ -589,7 +587,8 @@ GameSession::run() } /* Handle events: */ - world->get_tux()->input.old_fire = world->get_tux()->input.fire; + currentsector->player->input.old_fire + = currentsector->player->input.fire; process_events(); process_menu(); @@ -634,24 +633,24 @@ GameSession::run() } /* Handle time: */ - if (!time_left.check() && world->get_tux()->dying == DYING_NOT + if (!time_left.check() && currentsector->player->dying == DYING_NOT && !end_sequence) - world->get_tux()->kill(Player::KILL); + currentsector->player->kill(Player::KILL); /* Handle music: */ - if(world->get_tux()->invincible_timer.check() && !end_sequence) + if(currentsector->player->invincible_timer.check() && !end_sequence) { - world->play_music(HERRING_MUSIC); + currentsector->play_music(HERRING_MUSIC); } /* are we low on time ? */ else if (time_left.get_left() < TIME_WARNING && !end_sequence) { - world->play_music(HURRYUP_MUSIC); + currentsector->play_music(HURRYUP_MUSIC); } /* or just normal music? */ - else if(world->get_music_type() != LEVEL_MUSIC && !end_sequence) + else if(currentsector->get_music_type() != LEVEL_MUSIC && !end_sequence) { - world->play_music(LEVEL_MUSIC); + currentsector->play_music(LEVEL_MUSIC); } /* Calculate frames per second */ @@ -674,7 +673,7 @@ GameSession::run() /* Bounce a brick: */ void bumpbrick(float x, float y) { - World::current()->add_bouncy_brick(Vector(((int)(x + 1) / 32) * 32, + Sector::current()->add_bouncy_brick(Vector(((int)(x + 1) / 32) * 32, (int)(y / 32) * 32)); play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER); @@ -684,9 +683,6 @@ void bumpbrick(float x, float y) void GameSession::drawstatus(DrawingContext& context) { - context.push_transform(); - context.set_translation(Vector(0, 0)); - char str[60]; snprintf(str, 60, "%d", player_status.score); @@ -742,8 +738,6 @@ GameSession::drawstatus(DrawingContext& context) context.draw_text(gold_text, str, Vector(screen->w-4*16, 40), LAYER_FOREGROUND1); } - - context.pop_transform(); } void @@ -752,7 +746,7 @@ GameSession::drawresultscreen(void) char str[80]; DrawingContext context; - world->background->draw(context); + currentsector->background->draw(context); context.draw_text_center(blue_text, "Result:", Vector(0, 200), LAYER_FOREGROUND1); @@ -780,7 +774,7 @@ std::string slotinfo(int slot) if (savegame) { LispReader reader(lisp_cdr(savegame)); - reader.read_string("title", &title); + reader.read_string("title", title); lisp_free(savegame); } diff --git a/src/gameloop.h b/src/gameloop.h index a2eb7c722..4e990f367 100644 --- a/src/gameloop.h +++ b/src/gameloop.h @@ -24,8 +24,6 @@ #include "sound.h" #include "type.h" -#include "level.h" -#include "world.h" /* GameLoop modes */ @@ -37,18 +35,22 @@ extern int game_started; -class World; +class Level; +class Sector; +class DrawingContext; /** The GameSession class controlls the controll flow of a World, ie. present the menu on specifc keypresses, render and update it while keeping the speed and framerate sane, etc. */ class GameSession { - private: +private: Timer fps_timer; Timer frame_timer; Timer endsequence_timer; - World* world; + Level* level; + Sector* currentsector; + int st_gl_mode; int levelnb; float fps_fps; @@ -69,18 +71,16 @@ class GameSession bool game_pause; - // FIXME: Hack for restarting the level - std::string subset; - - public: + std::string levelname; +public: enum ExitStatus { ES_NONE, ES_LEVEL_FINISHED, ES_GAME_OVER, ES_LEVEL_ABORT }; - private: +private: ExitStatus exit_status; - public: - +public: + DrawingContext* context; Timer time_left; - GameSession(const std::string& subset, int levelnb, int mode); + GameSession(const std::string& level, int mode); ~GameSession(); /** Enter the busy loop */ @@ -89,11 +89,14 @@ class GameSession void draw(); void action(double frame_ratio); - Level* get_level() { return world->get_level(); } - World* get_world() { return world; } - + void set_current() + { current_ = this; } static GameSession* current() { return current_; } - private: + + Sector* get_current_sector() + { return currentsector; } + +private: static GameSession* current_; void restart_level(); @@ -107,7 +110,7 @@ class GameSession void drawendscreen(); void drawresultscreen(void); - private: +private: void on_escape_press(); void process_menu(); }; diff --git a/src/gameobjs.cpp b/src/gameobjs.cpp index 9c8e79cfc..9075f2c73 100644 --- a/src/gameobjs.cpp +++ b/src/gameobjs.cpp @@ -21,13 +21,13 @@ #include #include #include -#include "world.h" #include "tile.h" #include "gameloop.h" #include "gameobjs.h" #include "sprite_manager.h" #include "resources.h" -#include "level.h" +#include "sector.h" +#include "tilemap.h" BouncyDistro::BouncyDistro(const Vector& pos) : position(pos) @@ -80,7 +80,7 @@ BrokenBrick::draw(DrawingContext& context) BouncyBrick::BouncyBrick(const Vector& pos) : position(pos), offset(0), offset_m(-BOUNCY_BRICK_SPEED) { - shape = World::current()->get_level()->gettileid(pos.x, pos.y); + shape = Sector::current()->solids->get_tile_id_at(pos); } void @@ -134,12 +134,12 @@ Sprite *img_trampoline[TRAMPOLINE_FRAMES]; Trampoline::Trampoline(LispReader& reader) { - reader.read_float("x", &base.x); - reader.read_float("y", &base.y); + reader.read_float("x", base.x); + reader.read_float("y", base.y); base.width = 32; base.height = 32; power = 7.5; - reader.read_float("power", &power); + reader.read_float("power", power); frame = 0; mode = M_NORMAL; @@ -191,7 +191,7 @@ Trampoline::action(float frame_ratio) { /* FIXME: The trampoline object shouldn't know about pplayer objects. */ /* If we're holding the iceblock */ - Player& tux = *World::current()->get_tux(); + Player& tux = *Sector::current()->player; Direction dir = tux.dir; if(dir == RIGHT) @@ -271,11 +271,11 @@ Sprite *img_flying_platform; FlyingPlatform::FlyingPlatform(LispReader& reader) { - reader.read_int_vector("x", &pos_x); - reader.read_int_vector("y", &pos_y); + reader.read_int_vector("x", pos_x); + reader.read_int_vector("y", pos_y); velocity = 2.0; - reader.read_float("velocity", &velocity); + reader.read_float("velocity", velocity); base.x = pos_x[0]; base.y = pos_y[0]; diff --git a/src/gameobjs.h b/src/gameobjs.h index c4ce9ffd2..d9459f491 100644 --- a/src/gameobjs.h +++ b/src/gameobjs.h @@ -30,6 +30,7 @@ #include "collision.h" #include "game_object.h" #include "moving_object.h" +#include "serializable.h" #include "lispwriter.h" /* Bounciness of distros: */ diff --git a/src/high_scores.cpp b/src/high_scores.cpp index 044c882f2..af4f754a6 100644 --- a/src/high_scores.cpp +++ b/src/high_scores.cpp @@ -70,8 +70,8 @@ void load_hs(void) if (strcmp(lisp_symbol(lisp_car(root_obj)), "supertux-highscore") == 0) { LispReader reader(lisp_cdr(root_obj)); - reader.read_int("score", &hs_score); - reader.read_string("name", &hs_name); + reader.read_int("score", hs_score); + reader.read_string("name", hs_name); } fclose(fi); diff --git a/src/level.cpp b/src/level.cpp index ab90191c5..cc30db43f 100644 --- a/src/level.cpp +++ b/src/level.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include "globals.h" #include "setup.h" #include "camera.h" @@ -31,13 +32,12 @@ #include "level.h" #include "physic.h" #include "scene.h" +#include "sector.h" #include "tile.h" #include "lispreader.h" #include "resources.h" #include "music_manager.h" #include "gameobjs.h" -#include "world.h" -#include "background.h" #include "lispwriter.h" using namespace std; @@ -60,8 +60,7 @@ void LevelSubset::create(const std::string& subset_name) new_subset.title = "Unknown Title"; new_subset.description = "No description so far."; new_subset.save(); - new_lev.init_defaults(); - new_lev.save(subset_name, 1, 0); + //new_lev.save(subset_name, 1, 0); } void LevelSubset::parse (lisp_object_t* cursor) @@ -96,7 +95,7 @@ void LevelSubset::parse (lisp_object_t* cursor) } } -void LevelSubset::load(char *subset) +void LevelSubset::load(const char* subset) { FILE* fi; char filename[1024]; @@ -169,7 +168,8 @@ void LevelSubset::load(char *subset) levels = --i; } -void LevelSubset::save() +void +LevelSubset::save() { FILE* fi; string filename; @@ -201,408 +201,99 @@ void LevelSubset::save() fprintf( fi,")"); fclose(fi); - } } -Level::Level() - : img_bkgd(0) -{ - init_defaults(); -} - -Level::~Level() -{ - delete img_bkgd; -} - -void -Level::init_defaults() -{ - name = "UnNamed"; - author = "UnNamed"; - song_title = "Mortimers_chipdisko.mod"; - width = 0; - height = 0; - start_pos.x = 100; - start_pos.y = 170; - time_left = 100; - gravity = 10.; - - resize(21, 19); -} - -int -Level::load(const std::string& subset, int level, World* world) +std::string +LevelSubset::get_level_filename(unsigned int num) { char filename[1024]; - + // Load data file: - snprintf(filename, 1024, "%s/levels/%s/level%d.stl", st_dir, subset.c_str(), level); + snprintf(filename, 1024, "%s/levels/%s/level%d.stl", st_dir, + name.c_str(), num); if(!faccessible(filename)) - snprintf(filename, 1024, "%s/levels/%s/level%d.stl", datadir.c_str(), subset.c_str(), level); - - return load(filename, world); -} + snprintf(filename, 1024, "%s/levels/%s/level%d.stl", datadir.c_str(), + name.c_str(), num); -int -Level::load(const std::string& filename, World* world) -{ - lisp_object_t* root_obj = lisp_read_from_file(filename); - if (!root_obj) - { - std::cout << "Level: Couldn't load file: " << filename << std::endl; - return -1; - } - - if (root_obj->type == LISP_TYPE_EOF || root_obj->type == LISP_TYPE_PARSE_ERROR) - { - lisp_free(root_obj); - std::cout << "World: Parse Error in file '" << filename - << "'.\n"; - return -1; - } - - int version = 0; - if (strcmp(lisp_symbol(lisp_car(root_obj)), "supertux-level") == 0) - { - LispReader reader(lisp_cdr(root_obj)); - version = 0; - reader.read_int("version", &version); - if(!reader.read_int("width", &width)) - st_abort("No width specified for level.", ""); - if (!reader.read_float("start_pos_x", &start_pos.x)) start_pos.x = 100; - if (!reader.read_float("start_pos_y", &start_pos.y)) start_pos.y = 170; - time_left = 500; - if(!reader.read_int("time", &time_left)) { - printf("Warning: no time specified for level.\n"); - } - - height = 15; - if(!reader.read_int("height", &height)) { - printf("Warning: no height specified for level.\n"); - } - - - // read old background stuff - int bkgd_speed = 50; - reader.read_int("bkgd_speed", &bkgd_speed); - - Color bkgd_top, bkgd_bottom; - int r, g, b; - reader.read_int("bkgd_red_top", &r); - reader.read_int("bkgd_green_top", &g); - reader.read_int("bkgd_blue_top", &b); - bkgd_top.red = r; - bkgd_top.green = g; - bkgd_top.blue = b; - - reader.read_int("bkgd_red_bottom", &r); - reader.read_int("bkgd_green_bottom", &g); - reader.read_int("bkgd_blue_bottom", &b); - bkgd_bottom.red = r; - bkgd_bottom.green = g; - bkgd_bottom.blue = b; - - std::string bkgd_image; - reader.read_string("background", &bkgd_image); - - if(world) { - Background* background = new Background(); - if(bkgd_image != "") - background->set_image(bkgd_image, bkgd_speed); - else - background->set_gradient(bkgd_top, bkgd_bottom); - - world->add_object(background); - } - - gravity = 10; - reader.read_float("gravity", &gravity); - name = "Noname"; - reader.read_string("name", &name); - author = "unknown author"; - reader.read_string("author", &author); - song_title = ""; - reader.read_string("music", &song_title); - particle_system = ""; - reader.read_string("particle_system", &particle_system); - - reader.read_int_vector("background-tm", &bg_tiles); - if(int(bg_tiles.size()) != width * height) - st_abort("Wrong size of backgroundtilemap", ""); - - if (!reader.read_int_vector("interactive-tm", &ia_tiles)) - reader.read_int_vector("tilemap", &ia_tiles); - if(int(ia_tiles.size()) != width * height) - st_abort("Wrong size of interactivetilemap", ""); - - reader.read_int_vector("foreground-tm", &fg_tiles); - if(int(fg_tiles.size()) != width * height) - st_abort("Wrong size of foregroundtilemap", ""); - - { // Read ResetPoints - lisp_object_t* cur = 0; - if (reader.read_lisp("reset-points", &cur)) - { - while (!lisp_nil_p(cur)) - { - lisp_object_t* data = lisp_car(cur); - - ResetPoint pos; - - LispReader reader(lisp_cdr(data)); - if (reader.read_int("x", &pos.x) - && reader.read_int("y", &pos.y)) - { - reset_points.push_back(pos); - } - - cur = lisp_cdr(cur); - } - } - } - - { // Read Objects - lisp_object_t* cur = 0; - if (reader.read_lisp("objects", &cur)) - { - if(world) - world->parse_objects(cur); - } - } - - { // Read Camera - lisp_object_t* cur = 0; - if (reader.read_lisp("camera", &cur)) - { - LispReader reader(cur); - if(world) { - world->camera->read(reader); - } - } - } - } - - lisp_free(root_obj); - return 0; + return std::string(filename); } -/* Save data for level: */ +//--------------------------------------------------------------------------- -void -Level::save(const std::string& subset, int level, World* world) +Level::Level() + : name("noname"), author("mr. x"), time_left(500) { - char filename[1024]; - char str[80]; - - /* Save data file: */ - snprintf(str, sizeof(str), "/levels/%s/", subset.c_str()); - fcreatedir(str); - snprintf(filename, sizeof(filename), - "%s/levels/%s/level%d.stl", st_dir, subset.c_str(), level); - if(!fwriteable(filename)) - snprintf(filename, sizeof(filename), "%s/levels/%s/level%d.stl", - datadir.c_str(), subset.c_str(), level); - - std::ofstream out(filename); - if(!out.good()) { - st_abort("Couldn't write file.", filename); - } - LispWriter writer(out); - - /* Write header: */ - writer.write_comment("SuperTux level made using the built-in leveleditor"); - writer.start_list("supertux-level"); - - writer.write_int("version", 1); - writer.write_string("name", name); - writer.write_string("author", author); - writer.write_string("music", song_title); - writer.write_string("background", bkgd_image); - writer.write_string("particle_system", particle_system); - writer.write_int("time", time_left); - writer.write_int("width", width); - writer.write_int("height", height); - writer.write_float("gravity", gravity); - - writer.write_int_vector("background-tm", bg_tiles); - writer.write_int_vector("interactive-tm", ia_tiles); - writer.write_int_vector("foreground-tm", fg_tiles); - - writer.start_list("reset-points"); - for(std::vector::iterator i = reset_points.begin(); - i != reset_points.end(); ++i) { - writer.start_list("point"); - writer.write_int("x", i->x); - writer.write_int("y", i->y); - writer.end_list("point"); - } - writer.end_list("reset-points"); - - // write objects - writer.start_list("objects"); - // pick all objects that can be written into a levelfile - for(std::vector::iterator it = world->gameobjects.begin(); - it != world->gameobjects.end(); ++it) { - Serializable* serializable = dynamic_cast (*it); - if(serializable) - serializable->write(writer); - } - writer.end_list("objects"); - - writer.end_list("supertux-level"); - out.close(); } -/* Unload data for this level: */ void -Level::cleanup() +Level::load(const std::string& filename) { - bg_tiles.clear(); - ia_tiles.clear(); - fg_tiles.clear(); - - reset_points.clear(); - name = ""; - author = ""; - song_title = ""; -} - -/* Load a level-specific graphic... */ -void Level::load_image(Surface** ptexture, string theme,const char * file, int use_alpha) -{ - char fname[1024]; - - snprintf(fname, 1024, "%s/themes/%s/%s", st_dir, theme.c_str(), file); - if(!faccessible(fname)) - snprintf(fname, 1024, "%s/images/themes/%s/%s", datadir.c_str(), theme.c_str(), file); - - *ptexture = new Surface(fname, use_alpha); -} + LispReader* level = LispReader::load(filename, "supertux-level"); -/* Change the size of a level */ -void -Level::resize(int new_width, int new_height) -{ - if(new_width < width) { - // remap tiles for new width - for(int y = 0; y < height && y < new_height; ++y) { - for(int x = 0; x < new_width; ++x) { - ia_tiles[y * new_width + x] = ia_tiles[y * width + x]; - bg_tiles[y * new_width + x] = bg_tiles[y * width + x]; - fg_tiles[y * new_width + x] = fg_tiles[y * width + x]; - } - } + int version = 1; + level->read_int("version", version); + if(version == 1) { + load_old_format(*level); + return; } - ia_tiles.resize(new_width * new_height); - bg_tiles.resize(new_width * new_height); - fg_tiles.resize(new_width * new_height); - - if(new_width > width) { - // remap tiles - for(int y = std::min(height, new_height)-1; y >= 0; --y) { - for(int x = new_width-1; x >= 0; --x) { - if(x >= width) { - ia_tiles[y * new_width + x] = 0; - bg_tiles[y * new_width + x] = 0; - fg_tiles[y * new_width + x] = 0; - } else { - ia_tiles[y * new_width + x] = ia_tiles[y * width + x]; - bg_tiles[y * new_width + x] = bg_tiles[y * width + x]; - fg_tiles[y * new_width + x] = fg_tiles[y * width + x]; - } - } + for(lisp_object_t* cur = level->get_lisp(); !lisp_nil_p(cur); + cur = lisp_cdr(cur)) { + std::string token = lisp_symbol(lisp_car(lisp_car(cur))); + lisp_object_t* data = lisp_car(lisp_cdr(lisp_car(cur))); + LispReader reader(lisp_cdr(lisp_car(cur))); + + if(token == "name") { + name = lisp_string(data); + } else if(token == "author") { + author = lisp_string(data); + } else if(token == "time") { + time_left = lisp_integer(data); + } else if(token == "sector") { + Sector* sector = new Sector; + sector->parse(reader); + add_sector(sector); + } else { + std::cerr << "Unknown token '" << token << "' in level file.\n"; + continue; } } - - height = new_height; - width = new_width; -} - -void -Level::change(float x, float y, int tm, unsigned int c) -{ - int yy = ((int)y / 32); - int xx = ((int)x / 32); - - if (yy >= 0 && yy < height && xx >= 0 && xx <= width) - { - switch(tm) - { - case TM_BG: - bg_tiles[yy * width + xx] = c; - break; - case TM_IA: - ia_tiles[yy * width + xx] = c; - break; - case TM_FG: - fg_tiles[yy * width + xx] = c; - break; - } - } + + delete level; } void -Level::load_song() +Level::load_old_format(LispReader& reader) { - char* song_path; - char* song_subtitle; - - level_song = music_manager->load_music(datadir + "/music/" + song_title); - - song_path = (char *) malloc(sizeof(char) * datadir.length() + - strlen(song_title.c_str()) + 8 + 5); - song_subtitle = strdup(song_title.c_str()); - strcpy(strstr(song_subtitle, "."), "\0"); - sprintf(song_path, "%s/music/%s-fast%s", datadir.c_str(), - song_subtitle, strstr(song_title.c_str(), ".")); - if(!music_manager->exists_music(song_path)) { - level_song_fast = level_song; - } else { - level_song_fast = music_manager->load_music(song_path); - } - free(song_subtitle); - free(song_path); -} + reader.read_string("name", name); + reader.read_string("author", author); + reader.read_int("time", time_left); -MusicRef -Level::get_level_music() -{ - return level_song; + Sector* sector = new Sector; + sector->parse_old_format(reader); + add_sector(sector); } -MusicRef -Level::get_level_music_fast() +Level::~Level() { - return level_song_fast; + for(Sectors::iterator i = sectors.begin(); i != sectors.end(); ++i) + delete i->second; } -unsigned int -Level::gettileid(float x, float y) const +void +Level::add_sector(Sector* sector) { - int xx, yy; - unsigned int c; - - yy = ((int)y / 32); - xx = ((int)x / 32); - - if (yy >= 0 && yy < height && xx >= 0 && xx <= width) - c = ia_tiles[yy * width + xx]; - else - c = 0; - - return c; + sectors.insert(std::make_pair(sector->get_name(), sector)); } -unsigned int -Level::get_tile_at(int x, int y) const +Sector* +Level::get_sector(const std::string& name) { - if(x < 0 || x >= width || y < 0 || y >= height) + Sectors::iterator i = sectors.find(name); + if(i == sectors.end()) return 0; - - return ia_tiles[y * width + x]; + + return i->second; } -/* EOF */ diff --git a/src/level.h b/src/level.h index be51d5ab0..17272baf6 100644 --- a/src/level.h +++ b/src/level.h @@ -21,13 +21,13 @@ #ifndef SUPERTUX_LEVEL_H #define SUPERTUX_LEVEL_H +#include #include #include "screen/texture.h" #include "lispreader.h" #include "musicref.h" class Tile; -class World; /** This type holds meta-information about a level-subset. It could be extended to handle manipulation of subsets. */ @@ -38,9 +38,11 @@ public: ~LevelSubset(); static void create(const std::string& subset_name); - void load(char *subset); + void load(const char* subset); void save(); + std::string get_level_filename(unsigned int i); + std::string name; std::string title; std::string description; @@ -51,89 +53,36 @@ private: void parse(lisp_object_t* cursor); }; -#define LEVEL_NAME_MAX 20 - -enum TileMapType { - TM_BG, - TM_IA, - TM_FG -}; - -struct ResetPoint -{ - int x; - int y; -}; +class Sector; -class Level +class Level { public: - Surface* img_bkgd; - MusicRef level_song; - MusicRef level_song_fast; - std::string name; std::string author; - std::string song_title; - std::string bkgd_image; - std::string particle_system; - std::vector bg_tiles; /* Tiles in the background */ - std::vector ia_tiles; /* solid Tiles in the game */ - std::vector fg_tiles; /* Tiles in the foreground */ int time_left; - int width; - int height; - Vector start_pos; - float gravity; - - /** A collection of points to which Tux can be reset after a lost live */ - std::vector reset_points; - public: + typedef std::map Sectors; + Sectors sectors; + +public: Level(); - Level(const std::string& subset, int level, World* world); - Level(const std::string& filename, World* world); ~Level(); - /** Will the Level structure with default values */ - void init_defaults(); - - /** Cleanup the level struct from allocated tile data and such */ - void cleanup(); - - /** Load data for this level: - Returns -1, if the loading of the level failed. - XXX the world parameter is a temporary hack - */ - int load(const std::string& subset, int level, World* world); - - /** Load data for this level: - Returns -1, if the loading of the level failed. - XXX the world parameter is a temporary hack - */ - int load(const std::string& filename, World* world); - - void load_song(); - void free_song(); - MusicRef get_level_music(); - MusicRef get_level_music_fast(); - - // XXX the world parameter is a temporary hack - void save(const std::string& subset, int level, World* world); - - /** Edit a piece of the map! */ - void change(float x, float y, int tm, unsigned int c); - - /** Resize the level to a new width/height */ - void resize(int new_width, int new_height); - - /** Return the id of the tile at position x/y */ - unsigned int gettileid(float x, float y) const; - /** returns the id of the tile at position x,y - * (these are logical and not pixel coordinates) - */ - unsigned int get_tile_at(int x, int y) const; - - void load_image(Surface** ptexture, std::string theme, const char * file, int use_alpha); + void load(const std::string& filename); + void save(const std::string& filename); + + const std::string& get_name() const + { return name; } + + const std::string& get_author() const + { return author; } + + void add_sector(Sector* sector); + + Sector* get_sector(const std::string& name); + +private: + void load_old_format(LispReader& reader); }; #endif /*SUPERTUX_LEVEL_H*/ diff --git a/src/lispreader.cpp b/src/lispreader.cpp index 7f9f7a5fa..51ff97c03 100644 --- a/src/lispreader.cpp +++ b/src/lispreader.cpp @@ -1015,11 +1015,36 @@ lisp_dump (lisp_object_t *obj, FILE *out) using namespace std; LispReader::LispReader (lisp_object_t* l) - : lst (l) + : owner(0), lst (l) { - //std::cout << "LispReader: " << std::flush; - //lisp_dump(lst, stdout); - //std::cout << std::endl; +} + +LispReader::~LispReader() +{ + if(owner) + lisp_free(owner); +} + +LispReader* +LispReader::load(const std::string& filename, const std::string& toplevellist) +{ + lisp_object_t* obj = lisp_read_from_file(filename); + + if(obj->type == LISP_TYPE_EOF || obj->type == LISP_TYPE_PARSE_ERROR) { + lisp_free(obj); + throw LispReaderException("LispReader::load", __FILE__, __LINE__); + } + + if(toplevellist != lisp_symbol(lisp_car(obj))) { + lisp_car(obj); + throw LispReaderException("LispReader::load wrong toplevel symbol", + __FILE__, __LINE__); + } + + LispReader* reader = new LispReader(lisp_cdr(obj)); + reader->owner = obj; + + return reader; } lisp_object_t* @@ -1052,147 +1077,148 @@ LispReader::search_for(const char* name) } bool -LispReader::read_int (const char* name, int* i) +LispReader::read_int (const char* name, int& i) { lisp_object_t* obj = search_for (name); - if (obj) - { - if (!lisp_integer_p(lisp_car(obj))) - { - //st_abort("LispReader expected type integer at token: ", name); /* Instead of giving up, we return with false now. */ - return false; - } - *i = lisp_integer(lisp_car(obj)); - return true; - } - return false; + if(!obj) + return false; + + if (!lisp_integer_p(lisp_car(obj))) + return false; + + i = lisp_integer(lisp_car(obj)); + return true; } bool -LispReader::read_lisp(const char* name, lisp_object_t** b) +LispReader::read_lisp(const char* name, lisp_object_t*& b) { lisp_object_t* obj = search_for (name); - if (obj) - { - *b = obj; - return true; - } - else + if (!obj) return false; + + b = obj; + return true; +} + +LispReader* +LispReader::read_lisp(const char* name) +{ + lisp_object_t* obj = search_for(name); + if(!obj) + return 0; + + return new LispReader(obj); } bool -LispReader::read_float (const char* name, float* f) +LispReader::read_float (const char* name, float& f) { lisp_object_t* obj = search_for (name); - if (obj) - { - if (!lisp_real_p(lisp_car(obj)) && !lisp_integer_p(lisp_car(obj))) - st_abort("LispReader expected type real at token: ", name); - *f = lisp_real(lisp_car(obj)); - return true; - } - return false; + if (!obj) + return false; + + if (!lisp_real_p(lisp_car(obj)) && !lisp_integer_p(lisp_car(obj))) + st_abort("LispReader expected type real at token: ", name); + + f = lisp_real(lisp_car(obj)); + return true; } bool -LispReader::read_string_vector (const char* name, std::vector* vec) +LispReader::read_string_vector (const char* name, std::vector& vec) { lisp_object_t* obj = search_for (name); - if (obj) - { - while(!lisp_nil_p(obj)) - { - if (!lisp_string_p(lisp_car(obj))) - st_abort("LispReader expected type string at token: ", name); - vec->push_back(lisp_string(lisp_car(obj))); - obj = lisp_cdr(obj); - } - return true; - } - return false; + if (!obj) + return false; + + vec.clear(); + while(!lisp_nil_p(obj)) + { + if (!lisp_string_p(lisp_car(obj))) + st_abort("LispReader expected type string at token: ", name); + vec.push_back(lisp_string(lisp_car(obj))); + obj = lisp_cdr(obj); + } + return true; } bool -LispReader::read_int_vector (const char* name, std::vector* vec) +LispReader::read_int_vector (const char* name, std::vector& vec) { - vec->clear(); lisp_object_t* obj = search_for (name); - if (obj) - { - while(!lisp_nil_p(obj)) - { - if (!lisp_integer_p(lisp_car(obj))) - st_abort("LispReader expected type integer at token: ", name); - vec->push_back(lisp_integer(lisp_car(obj))); - obj = lisp_cdr(obj); - } - return true; - } - return false; + if (!obj) + return false; + + vec.clear(); + while(!lisp_nil_p(obj)) + { + if (!lisp_integer_p(lisp_car(obj))) + st_abort("LispReader expected type integer at token: ", name); + vec.push_back(lisp_integer(lisp_car(obj))); + obj = lisp_cdr(obj); + } + return true; } bool -LispReader::read_int_vector (const char* name, std::vector* vec) +LispReader::read_int_vector (const char* name, std::vector& vec) { - vec->clear(); lisp_object_t* obj = search_for (name); - if (obj) - { - while(!lisp_nil_p(obj)) - { - if (!lisp_integer_p(lisp_car(obj))) - st_abort("LispReader expected type integer at token: ", name); - vec->push_back(lisp_integer(lisp_car(obj))); - obj = lisp_cdr(obj); - } - return true; - } - return false; + if (!obj) + return false; + + vec.clear(); + while(!lisp_nil_p(obj)) + { + if (!lisp_integer_p(lisp_car(obj))) + st_abort("LispReader expected type integer at token: ", name); + vec.push_back(lisp_integer(lisp_car(obj))); + obj = lisp_cdr(obj); + } + return true; } bool -LispReader::read_char_vector (const char* name, std::vector* vec) +LispReader::read_char_vector (const char* name, std::vector& vec) { lisp_object_t* obj = search_for (name); - if (obj) - { - while(!lisp_nil_p(obj)) - { - vec->push_back(*lisp_string(lisp_car(obj))); - obj = lisp_cdr(obj); - } - return true; - } - return false; + if (!obj) + return false; + + vec.clear(); + while(!lisp_nil_p(obj)) + { + vec.push_back(*lisp_string(lisp_car(obj))); + obj = lisp_cdr(obj); + } + return true; } bool -LispReader::read_string (const char* name, std::string* str) +LispReader::read_string (const char* name, std::string& str) { lisp_object_t* obj = search_for (name); - if (obj) - { - if (!lisp_string_p(lisp_car(obj))) - st_abort("LispReader expected type string at token: ", name); - *str = lisp_string(lisp_car(obj)); - return true; - } - return false; + if (!obj) + return false; + + if (!lisp_string_p(lisp_car(obj))) + st_abort("LispReader expected type string at token: ", name); + str = lisp_string(lisp_car(obj)); + return true; } bool -LispReader::read_bool (const char* name, bool* b) +LispReader::read_bool (const char* name, bool& b) { lisp_object_t* obj = search_for (name); - if (obj) - { - if (!lisp_boolean_p(lisp_car(obj))) - st_abort("LispReader expected type bool at token: ", name); - *b = lisp_boolean(lisp_car(obj)); - return true; - } - return false; + if (!obj) + return false; + + if (!lisp_boolean_p(lisp_car(obj))) + st_abort("LispReader expected type bool at token: ", name); + b = lisp_boolean(lisp_car(obj)); + return true; } lisp_object_t* @@ -1201,92 +1227,19 @@ LispReader::get_lisp() return lst; } -lisp_object_t* lisp_read_from_gzfile(const char* filename) -{ - bool done = false; - lisp_object_t* root_obj = 0; - int chunk_size = 128 * 1024; - int buf_pos = 0; - int try_number = 1; - char* buf = static_cast(malloc(chunk_size)); - if (!buf) - throw LispReaderException("lisp_read_from_gzfile()", __FILE__, __LINE__); - - gzFile in = gzopen(filename, "r"); - - while (!done) - { - int ret = gzread(in, buf + buf_pos, chunk_size); - if (ret == -1) - { - free (buf); - throw LispReaderException("Error while reading from file", __FILE__, __LINE__); - } - else if (ret == chunk_size) // buffer got full, eof not yet there so resize - { - buf_pos = chunk_size * try_number; - try_number += 1; - buf = static_cast(realloc(buf, chunk_size * try_number)); - - if (!buf) - throw LispReaderException("lisp_read_from_gzfile()", __FILE__, __LINE__); - } - else - { - // everything fine, encountered EOF - done = true; - } - } - - lisp_stream_t stream; - lisp_stream_init_string (&stream, buf); - root_obj = lisp_read (&stream); - - free(buf); - gzclose(in); - - return root_obj; -} - -bool has_suffix(const char* data, const char* suffix) -{ - int suffix_len = strlen(suffix); - int data_len = strlen(data); - - const char* data_suffix = (data + data_len - suffix_len); - - if (data_suffix >= data) - { - return (strcmp(data_suffix, suffix) == 0); - } - else - { - return false; - } -} - lisp_object_t* lisp_read_from_file(const std::string& filename) { - lisp_stream_t stream; + FILE* in = fopen(filename.c_str(), "r"); - if (has_suffix(filename.c_str(), ".gz")) - { - return lisp_read_from_gzfile(filename.c_str()); - } - else - { - lisp_object_t* obj = 0; - FILE* in = fopen(filename.c_str(), "r"); + if(!in) + return 0; - if (in) - { - lisp_stream_init_file(&stream, in); - obj = lisp_read(&stream); - fclose(in); - } + lisp_stream_t stream; + lisp_stream_init_file(&stream, in); + lisp_object_t* obj = lisp_read(&stream); + fclose(in); - return obj; - } + return obj; } // EOF // diff --git a/src/lispreader.h b/src/lispreader.h index 81f2c40be..3c3ba737f 100644 --- a/src/lispreader.h +++ b/src/lispreader.h @@ -117,7 +117,6 @@ struct _lisp_object_t } v; }; -lisp_stream_t* lisp_stream_init_gzfile (lisp_stream_t *stream, gzFile file); lisp_stream_t* lisp_stream_init_file (lisp_stream_t *stream, FILE *file); lisp_stream_t* lisp_stream_init_string (lisp_stream_t *stream, char *buf); lisp_stream_t* lisp_stream_init_any (lisp_stream_t *stream, void *data, @@ -172,22 +171,29 @@ void lisp_dump (lisp_object_t *obj, FILE *out); class LispReader { private: + lisp_object_t* owner; lisp_object_t* lst; lisp_object_t* search_for(const char* name); + public: /** cur == ((pos 1 2 3) (id 12 3 4)...) */ - LispReader (lisp_object_t* l); - - bool read_int_vector (const char* name, std::vector* vec); - bool read_int_vector (const char* name, std::vector* vec); - bool read_char_vector (const char* name, std::vector* vec); - bool read_string_vector (const char* name, std::vector* vec); - bool read_string (const char* name, std::string* str); - bool read_int (const char* name, int* i); - bool read_float (const char* name, float* f); - bool read_bool (const char* name, bool* b); - bool read_lisp (const char* name, lisp_object_t** b); + LispReader(lisp_object_t* l); + ~LispReader(); + + bool read_int_vector(const char* name, std::vector& vec); + bool read_int_vector(const char* name, std::vector& vec); + bool read_char_vector(const char* name, std::vector& vec); + bool read_string_vector(const char* name, std::vector& vec); + bool read_string(const char* name, std::string& str); + bool read_int(const char* name, int& i); + bool read_float(const char* name, float& f); + bool read_bool(const char* name, bool& b); + bool read_lisp(const char* name, lisp_object_t*& b); + LispReader* read_lisp(const char* name); + + static LispReader* load(const std::string& filename, + const std::string& toplevellist); lisp_object_t* get_lisp(); }; diff --git a/src/menu.cpp b/src/menu.cpp index 34dd5297b..e79cd2c37 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -722,9 +722,6 @@ Menu::draw(DrawingContext& context) int menu_height = get_height(); int menu_width = get_width(); - context.push_transform(); - context.set_translation(Vector(0, 0)); - /* Draw a transparent background */ context.draw_filled_rect( Vector(pos_x - menu_width/2, pos_y - 24*item.size()/2 - 10), @@ -735,7 +732,6 @@ Menu::draw(DrawingContext& context) { draw_item(context, i, menu_width, menu_height); } - context.pop_transform(); } MenuItem& diff --git a/src/mousecursor.cpp b/src/mousecursor.cpp index a0735651c..d279ee2ec 100644 --- a/src/mousecursor.cpp +++ b/src/mousecursor.cpp @@ -87,9 +87,6 @@ void MouseCursor::draw(DrawingContext& context) timer.start(MC_FRAME_PERIOD); } - context.push_transform(); - context.set_translation(Vector(0, 0)); context.draw_surface_part(cursor, Vector(w*cur_frame, h*cur_state), Vector(w, h), Vector(x-mid_x, y-mid_y), LAYER_FOREGROUND1+100); - context.pop_transform(); } diff --git a/src/particlesystem.cpp b/src/particlesystem.cpp index 88b728841..c21d109b9 100644 --- a/src/particlesystem.cpp +++ b/src/particlesystem.cpp @@ -22,15 +22,15 @@ #include #include #include "globals.h" -#include "world.h" -#include "level.h" -#include "scene.h" -#include "camera.h" +#include "lispreader.h" +#include "lispwriter.h" +#include "screen/drawing_context.h" ParticleSystem::ParticleSystem() { virtual_width = screen->w; virtual_height = screen->h; + layer = LAYER_BACKGROUND1; } ParticleSystem::~ParticleSystem() @@ -62,7 +62,7 @@ void ParticleSystem::draw(DrawingContext& context) if(pos.x > screen->w) pos.x -= virtual_width; if(pos.y > screen->h) pos.y -= virtual_height; - context.draw_surface(particle->texture, pos, LAYER_BACKGROUND1); + context.draw_surface(particle->texture, pos, layer); } context.pop_transform(); @@ -87,12 +87,25 @@ SnowParticleSystem::SnowParticleSystem() do { particle->speed = snowsize/60.0 + (float(rand()%10)/300.0); } while(particle->speed < 0.01); - particle->speed *= World::current()->get_level()->gravity; particles.push_back(particle); } } +void +SnowParticleSystem::parse(LispReader& reader) +{ + reader.read_int("layer", layer); +} + +void +SnowParticleSystem::write(LispWriter& writer) +{ + writer.start_list("particles-snow"); + writer.write_int("layer", layer); + writer.end_list("particles-snow"); +} + SnowParticleSystem::~SnowParticleSystem() { for(int i=0;i<3;++i) @@ -130,6 +143,20 @@ CloudParticleSystem::CloudParticleSystem() } } +void +CloudParticleSystem::parse(LispReader& reader) +{ + reader.read_int("layer", layer); +} + +void +CloudParticleSystem::write(LispWriter& writer) +{ + writer.start_list("particles-clouds"); + writer.write_int("layer", layer); + writer.end_list("particles-clouds"); +} + CloudParticleSystem::~CloudParticleSystem() { delete cloudimage; diff --git a/src/particlesystem.h b/src/particlesystem.h index 11bff4a00..e2a7f1ab5 100644 --- a/src/particlesystem.h +++ b/src/particlesystem.h @@ -23,7 +23,9 @@ #include #include "screen/texture.h" #include "game_object.h" +#include "serializable.h" +class LispReader; class DisplayManager; /** @@ -50,6 +52,8 @@ public: virtual void draw(DrawingContext& context); protected: + int layer; + class Particle { public: @@ -64,12 +68,15 @@ protected: float virtual_width, virtual_height; }; -class SnowParticleSystem : public ParticleSystem +class SnowParticleSystem : public ParticleSystem, public Serializable { public: SnowParticleSystem(); virtual ~SnowParticleSystem(); + void parse(LispReader& reader); + void write(LispWriter& writer); + virtual void action(float elapsed_time); std::string type() const @@ -85,12 +92,15 @@ private: Surface* snowimages[3]; }; -class CloudParticleSystem : public ParticleSystem +class CloudParticleSystem : public ParticleSystem, public Serializable { public: CloudParticleSystem(); virtual ~CloudParticleSystem(); + void parse(LispReader& reader); + void write(LispWriter& writer); + virtual void action(float elapsed_time); std::string type() const diff --git a/src/physic.cpp b/src/physic.cpp index aecc01e51..ef6d94c24 100644 --- a/src/physic.cpp +++ b/src/physic.cpp @@ -24,7 +24,7 @@ #include "defines.h" #include "physic.h" #include "timer.h" -#include "world.h" +#include "sector.h" #include "level.h" Physic::Physic() @@ -124,7 +124,7 @@ Physic::enable_gravity(bool enable_gravity) void Physic::apply(float frame_ratio, float &x, float &y) { - float gravity = World::current()->get_level()->gravity; + float gravity = Sector::current()->gravity; float grav; if(gravity_enabled) grav = gravity / 100.0; diff --git a/src/player.cpp b/src/player.cpp index df8571b11..c25b39531 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -27,6 +27,9 @@ #include "scene.h" #include "tile.h" #include "sprite.h" +#include "sector.h" +#include "tilemap.h" +#include "camera.h" #include "gameobjs.h" #include "screen/screen.h" @@ -261,26 +264,30 @@ Player::action(float elapsed_time) if (isbrick(base.x, base.y) || isfullbox(base.x, base.y)) { - World::current()->trygrabdistro(base.x, base.y - 32, BOUNCE); - World::current()->trybumpbadguy(base.x, base.y - 64); + Sector::current()->trygrabdistro( + Vector(base.x, base.y - 32), BOUNCE); + Sector::current()->trybumpbadguy(Vector(base.x, base.y - 64)); - World::current()->trybreakbrick(base.x, base.y, size == SMALL); + Sector::current()->trybreakbrick( + Vector(base.x, base.y), size == SMALL); bumpbrick(base.x, base.y); - World::current()->tryemptybox(base.x, base.y, RIGHT); + Sector::current()->tryemptybox(Vector(base.x, base.y), RIGHT); } if (isbrick(base.x+ 31, base.y) || isfullbox(base.x+ 31, base.y)) { - World::current()->trygrabdistro(base.x+ 31, base.y - 32,BOUNCE); - World::current()->trybumpbadguy(base.x+ 31, base.y - 64); + Sector::current()->trygrabdistro( + Vector(base.x+ 31, base.y - 32), BOUNCE); + Sector::current()->trybumpbadguy(Vector(base.x+ 31, base.y - 64)); if(size == BIG) - World::current()->trybreakbrick(base.x+ 31, base.y, size == SMALL); + Sector::current()->trybreakbrick( + Vector(base.x+ 31, base.y), size == SMALL); bumpbrick(base.x+ 31, base.y); - World::current()->tryemptybox(base.x+ 31, base.y, LEFT); + Sector::current()->tryemptybox(Vector(base.x+ 31, base.y), LEFT); } } @@ -480,16 +487,17 @@ Player::handle_vertical_input() butt_jump = false; // Break bricks beneath Tux - if(World::current()->trybreakbrick(base.x + 1, base.y + base.height, false) - || World::current()->trybreakbrick( - base.x + base.width - 1, base.y + base.height, false)) + if(Sector::current()->trybreakbrick( + Vector(base.x + 1, base.y + base.height), false) + || Sector::current()->trybreakbrick( + Vector(base.x + base.width - 1, base.y + base.height), false)) { physic.set_velocity_y(2); butt_jump = true; } // Kill nearby badguys - std::vector gameobjects = World::current()->gameobjects; + std::vector gameobjects = Sector::current()->gameobjects; for (std::vector::iterator i = gameobjects.begin(); i != gameobjects.end(); i++) @@ -538,7 +546,7 @@ Player::handle_input() /* Shoot! */ if (input.fire == DOWN && input.old_fire == UP && got_power != NONE_POWER) { - if(World::current()->add_bullet(Vector(base.x, base.y + (base.height/2)), + if(Sector::current()->add_bullet(Vector(base.x, base.y + (base.height/2)), physic.get_velocity_x(), dir)) shooting_timer.start(SHOOTING_TIME); input.old_fire = DOWN; @@ -615,16 +623,20 @@ Player::grabdistros() /* Grab distros: */ if (!dying) { - World::current()->trygrabdistro(base.x, base.y, NO_BOUNCE); - World::current()->trygrabdistro(base.x+ 31, base.y, NO_BOUNCE); + Sector::current()->trygrabdistro(Vector(base.x, base.y), NO_BOUNCE); + Sector::current()->trygrabdistro(Vector(base.x+ 31, base.y), NO_BOUNCE); - World::current()->trygrabdistro(base.x, base.y + base.height, NO_BOUNCE); - World::current()->trygrabdistro(base.x+ 31, base.y + base.height, NO_BOUNCE); + Sector::current()->trygrabdistro( + Vector(base.x, base.y + base.height), NO_BOUNCE); + Sector::current()->trygrabdistro( + Vector(base.x+ 31, base.y + base.height), NO_BOUNCE); if(size == BIG) { - World::current()->trygrabdistro(base.x, base.y + base.height / 2, NO_BOUNCE); - World::current()->trygrabdistro(base.x+ 31, base.y + base.height / 2, NO_BOUNCE); + Sector::current()->trygrabdistro( + Vector(base.x, base.y + base.height / 2), NO_BOUNCE); + Sector::current()->trygrabdistro( + Vector(base.x+ 31, base.y + base.height / 2), NO_BOUNCE); } } @@ -924,7 +936,7 @@ Player::move(const Vector& vector) } void -Player::check_bounds(DrawingContext& viewport) +Player::check_bounds(Camera* camera) { /* Keep tux in bounds: */ if (base.x < 0) @@ -934,7 +946,7 @@ Player::check_bounds(DrawingContext& viewport) } /* Keep in-bounds, vertically: */ - if (base.y > World::current()->get_level()->height * /*TILE_HEIGHT*/ 32) + if (base.y > Sector::current()->solids->get_height() * 32) { kill(KILL); return; @@ -942,12 +954,12 @@ Player::check_bounds(DrawingContext& viewport) bool adjust = false; // can happen if back scrolling is disabled - if(base.x < viewport.get_translation().x) { - base.x = viewport.get_translation().x; + if(base.x < camera->get_translation().x) { + base.x = camera->get_translation().x; adjust = true; } - if(base.x >= viewport.get_translation().x + screen->w - base.width) { - base.x = viewport.get_translation().x + screen->w - base.width; + if(base.x >= camera->get_translation().x + screen->w - base.width) { + base.x = camera->get_translation().x + screen->w - base.width; adjust = true; } @@ -960,5 +972,3 @@ Player::check_bounds(DrawingContext& viewport) } } -// EOF // - diff --git a/src/player.h b/src/player.h index b3496ebca..5296af18b 100644 --- a/src/player.h +++ b/src/player.h @@ -76,6 +76,7 @@ struct player_input_type void player_input_init(player_input_type* pplayer_input); class Sprite; +class Camera; extern Surface* tux_life; @@ -163,7 +164,7 @@ public: void collision(void* p_c_object, int c_object); void kill(HurtMode mode); void player_remove_powerups(); - void check_bounds(DrawingContext& context); + void check_bounds(Camera* camera); bool on_ground(); bool under_solid(); bool tiles_on_air(int tiles); diff --git a/src/resources.cpp b/src/resources.cpp index fcd4d6296..62928e497 100644 --- a/src/resources.cpp +++ b/src/resources.cpp @@ -33,6 +33,7 @@ Surface* img_pole; Surface* img_poletop; Surface* img_flag[2]; Surface* img_cloud[2][4]; +Surface* img_distro[4]; MusicRef herring_song; MusicRef level_end_song; diff --git a/src/sector.cpp b/src/sector.cpp new file mode 100644 index 000000000..401e6b624 --- /dev/null +++ b/src/sector.cpp @@ -0,0 +1,788 @@ +#include "sector.h" + +#include +#include +#include +#include +#include +#include "lispreader.h" + +#include "badguy.h" +#include "special.h" +#include "gameobjs.h" +#include "camera.h" +#include "background.h" +#include "particlesystem.h" +#include "tile.h" +#include "tilemap.h" +#include "music_manager.h" +#include "gameloop.h" +#include "resources.h" + +Sector* Sector::_current = 0; + +Sector::Sector() + : gravity(10), player(0), solids(0), background(0), camera(0), + currentmusic(LEVEL_MUSIC) +{ + song_title = "Mortimers_chipdisko.mod"; + player = new Player(); + add_object(player); +} + +Sector::~Sector() +{ + for(GameObjects::iterator i = gameobjects.begin(); i != gameobjects.end(); + ++i) + delete *i; + + for(SpawnPoints::iterator i = spawnpoints.begin(); i != spawnpoints.end(); + ++i) + delete *i; + + if(_current == this) + _current = 0; +} + +void +Sector::parse(LispReader& lispreader) +{ + _current = this; + + for(lisp_object_t* cur = lispreader.get_lisp(); !lisp_nil_p(cur); + cur = lisp_cdr(cur)) { + std::string token = lisp_symbol(lisp_car(lisp_car(cur))); + lisp_object_t* data = lisp_car(lisp_cdr(lisp_car(cur))); + LispReader reader(lisp_cdr(lisp_car(cur))); + + if(token == "name") { + name = lisp_string(data); + } else if(token == "gravity") { + gravity = lisp_integer(data); + } else if(token == "music") { + song_title = lisp_string(data); + load_music(); + } else if(token == "camera") { + if(camera) { + std::cerr << "Warning: More than 1 camera defined in sector.\n"; + continue; + } + camera = new Camera(this); + camera->read(reader); + add_object(camera); + } else if(token == "background") { + background = new Background(reader); + add_object(background); + } else if(token == "playerspawn") { + SpawnPoint* sp = new SpawnPoint; + reader.read_string("name", sp->name); + reader.read_float("x", sp->pos.x); + reader.read_float("y", sp->pos.y); + } else if(token == "tilemap") { + TileMap* tilemap = new TileMap(reader); + add_object(tilemap); + + if(tilemap->is_solid()) { + if(solids) { + std::cerr << "Warning multiple solid tilemaps in sector.\n"; + continue; + } + solids = tilemap; + } + } else if(badguykind_from_string(token) != BAD_INVALID) { + add_object(new BadGuy(badguykind_from_string(token), reader)); + } else if(token == "trampoline") { + add_object(new Trampoline(reader)); + } else if(token == "flying-platform") { + add_object(new FlyingPlatform(reader)); + } else if(token == "particles-snow") { + SnowParticleSystem* partsys = new SnowParticleSystem(); + partsys->parse(reader); + add_object(partsys); + } else if(token == "particles-clouds") { + CloudParticleSystem* partsys = new CloudParticleSystem(); + partsys->parse(reader); + add_object(partsys); + } + } + + if(!camera) { + std::cerr << "sector does not contain a camera.\n"; + camera = new Camera(this); + } + if(!solids) + throw std::runtime_error("sector does not contain a solid tile layer."); +} + +void +Sector::parse_old_format(LispReader& reader) +{ + _current = this; + + name = "main"; + reader.read_float("gravity", gravity); + + std::string backgroundimage; + reader.read_string("background", backgroundimage); + float bgspeed = .5; + reader.read_float("bkgd_speed", bgspeed); + + Color bkgd_top, bkgd_bottom; + int r = 0, g = 0, b = 128; + reader.read_int("bkgd_red_top", r); + reader.read_int("bkgd_green_top", g); + reader.read_int("bkgd_blue_top", b); + bkgd_top.red = r; + bkgd_top.green = g; + bkgd_top.blue = b; + + reader.read_int("bkgd_red_bottom", r); + reader.read_int("bkgd_green_bottom", g); + reader.read_int("bkgd_blue_bottom", b); + bkgd_bottom.red = r; + bkgd_bottom.green = g; + bkgd_bottom.blue = b; + + if(backgroundimage != "") { + background = new Background; + background->set_image(backgroundimage, bgspeed); + add_object(background); + } else { + background = new Background; + background->set_gradient(bkgd_top, bkgd_bottom); + add_object(background); + } + + std::string particlesystem; + reader.read_string("particle_system", particlesystem); + if(particlesystem == "clouds") + add_object(new CloudParticleSystem()); + else if(particlesystem == "snow") + add_object(new SnowParticleSystem()); + + Vector startpos(100, 170); + reader.read_float("start_pos_x", startpos.x); + reader.read_float("start_pos_y", startpos.y); + + SpawnPoint* spawn = new SpawnPoint; + spawn->pos = startpos; + spawn->name = "main"; + spawnpoints.push_back(spawn); + + song_title = "Mortimers_chipdisko.mod"; + reader.read_string("music", song_title); + load_music(); + + int width, height = 15; + reader.read_int("width", width); + reader.read_int("height", height); + + std::vector tiles; + if(reader.read_int_vector("interactive-tm", tiles) + || reader.read_int_vector("tilemap", tiles)) { + TileMap* tilemap = new TileMap(); + tilemap->set(width, height, tiles, LAYER_TILES, true); + solids = tilemap; + add_object(tilemap); + } + + if(reader.read_int_vector("background-tm", tiles)) { + TileMap* tilemap = new TileMap(); + tilemap->set(width, height, tiles, LAYER_BACKGROUNDTILES, false); + add_object(tilemap); + } + + if(reader.read_int_vector("foreground-tm", tiles)) { + TileMap* tilemap = new TileMap(); + tilemap->set(width, height, tiles, LAYER_FOREGROUNDTILES, false); + add_object(tilemap); + } + + // TODO read resetpoints + + // read objects + { + lisp_object_t* cur = 0; + if(reader.read_lisp("objects", cur)) { + while(!lisp_nil_p(cur)) { + lisp_object_t* data = lisp_car(cur); + std::string object_type = lisp_symbol(lisp_car(data)); + + LispReader reader(lisp_cdr(data)); + + if(object_type == "trampoline") { + add_object(new Trampoline(reader)); + } + else if(object_type == "flying-platform") { + add_object(new FlyingPlatform(reader)); + } + else { + BadGuyKind kind = badguykind_from_string(object_type); + add_object(new BadGuy(kind, reader)); + } + + cur = lisp_cdr(cur); + } + } + } + + // add a camera + camera = new Camera(this); + add_object(camera); +} + +void +Sector::write(LispWriter& writer) +{ + writer.write_string("name", name); + writer.write_float("gravity", gravity); + + for(GameObjects::iterator i = gameobjects.begin(); + i != gameobjects.end(); ++i) { + Serializable* serializable = dynamic_cast (*i); + if(serializable) + serializable->write(writer); + } +} + +void +Sector::add_object(GameObject* object) +{ + // XXX a bit hackish, at least try to keep the number of these things down... + BadGuy* badguy = dynamic_cast (object); + if(badguy) + badguys.push_back(badguy); + Bullet* bullet = dynamic_cast (object); + if(bullet) + bullets.push_back(bullet); + Upgrade* upgrade = dynamic_cast (object); + if(upgrade) + upgrades.push_back(upgrade); + Trampoline* trampoline = dynamic_cast (object); + if(trampoline) + trampolines.push_back(trampoline); + FlyingPlatform* flying_platform = dynamic_cast (object); + if(flying_platform) + flying_platforms.push_back(flying_platform); + Background* background = dynamic_cast (object); + if(background) + this->background = background; + + gameobjects.push_back(object); +} + +void +Sector::activate(const std::string& spawnpoint) +{ + _current = this; + + // Apply bonuses from former levels + switch (player_status.bonus) + { + case PlayerStatus::NO_BONUS: + break; + + case PlayerStatus::FLOWER_BONUS: + player->got_power = Player::FIRE_POWER; // FIXME: add ice power to here + // fall through + + case PlayerStatus::GROWUP_BONUS: + player->grow(false); + break; + } + + SpawnPoint* sp = 0; + for(SpawnPoints::iterator i = spawnpoints.begin(); i != spawnpoints.end(); + ++i) { + if((*i)->name == spawnpoint) { + sp = *i; + break; + } + } + if(!sp) { + std::cerr << "Spawnpoint '" << spawnpoint << "' not found.\n"; + } else { + player->move(sp->pos); + } + + camera->reset(Vector(player->base.x, player->base.y)); +} + +void +Sector::action(float elapsed_time) +{ + player->check_bounds(camera); + + /* update objects (don't use iterators here, because the list might change + * during the iteration) + */ + for(size_t i = 0; i < gameobjects.size(); ++i) + if(gameobjects[i]->is_valid()) + gameobjects[i]->action(elapsed_time); + + /* Handle all possible collisions. */ + collision_handler(); + + /** cleanup marked objects */ + for(std::vector::iterator i = gameobjects.begin(); + i != gameobjects.end(); /* nothing */) { + if((*i)->is_valid() == false) { + BadGuy* badguy = dynamic_cast (*i); + if(badguy) { + badguys.erase(std::remove(badguys.begin(), badguys.end(), badguy), + badguys.end()); + } + Bullet* bullet = dynamic_cast (*i); + if(bullet) { + bullets.erase( + std::remove(bullets.begin(), bullets.end(), bullet), + bullets.end()); + } + Upgrade* upgrade = dynamic_cast (*i); + if(upgrade) { + upgrades.erase( + std::remove(upgrades.begin(), upgrades.end(), upgrade), + upgrades.end()); + } + Trampoline* trampoline = dynamic_cast (*i); + if(trampoline) { + trampolines.erase( + std::remove(trampolines.begin(), trampolines.end(), trampoline), + trampolines.end()); + } + FlyingPlatform* flying_platform= dynamic_cast (*i); + if(flying_platform) { + flying_platforms.erase( + std::remove(flying_platforms.begin(), flying_platforms.end(), flying_platform), + flying_platforms.end()); + } + + delete *i; + i = gameobjects.erase(i); + } else { + ++i; + } + } +} + +void +Sector::draw(DrawingContext& context) +{ + context.push_transform(); + context.set_translation(camera->get_translation()); + + for(GameObjects::iterator i = gameobjects.begin(); + i != gameobjects.end(); ++i) { + if( (*i)->is_valid() ) + (*i)->draw(context); + } + + context.pop_transform(); +} + +void +Sector::collision_handler() +{ + // CO_BULLET & CO_BADGUY check + for(unsigned int i = 0; i < bullets.size(); ++i) + { + for (BadGuys::iterator j = badguys.begin(); j != badguys.end(); ++j) + { + if((*j)->dying != DYING_NOT) + continue; + + if(rectcollision(bullets[i]->base, (*j)->base)) + { + // We have detected a collision and now call the + // collision functions of the collided objects. + (*j)->collision(bullets[i], CO_BULLET, COLLISION_NORMAL); + bullets[i]->collision(CO_BADGUY); + break; // bullet is invalid now, so break + } + } + } + + /* CO_BADGUY & CO_BADGUY check */ + for (BadGuys::iterator i = badguys.begin(); i != badguys.end(); ++i) + { + if((*i)->dying != DYING_NOT) + continue; + + BadGuys::iterator j = i; + ++j; + for (; j != badguys.end(); ++j) + { + if(j == i || (*j)->dying != DYING_NOT) + continue; + + if(rectcollision((*i)->base, (*j)->base)) + { + // We have detected a collision and now call the + // collision functions of the collided objects. + (*j)->collision(*i, CO_BADGUY); + (*i)->collision(*j, CO_BADGUY); + } + } + } + if(player->dying != DYING_NOT) return; + + // CO_BADGUY & CO_PLAYER check + for (BadGuys::iterator i = badguys.begin(); i != badguys.end(); ++i) + { + if((*i)->dying != DYING_NOT) + continue; + + if(rectcollision_offset((*i)->base, player->base, 0, 0)) + { + // We have detected a collision and now call the collision + // functions of the collided objects. + if (player->previous_base.y < player->base.y && + player->previous_base.y + player->previous_base.height + < (*i)->base.y + (*i)->base.height/2 + && !player->invincible_timer.started()) + { + (*i)->collision(player, CO_PLAYER, COLLISION_SQUISH); + } + else + { + player->collision(*i, CO_BADGUY); + (*i)->collision(player, CO_PLAYER, COLLISION_NORMAL); + } + } + } + + // CO_UPGRADE & CO_PLAYER check + for(unsigned int i = 0; i < upgrades.size(); ++i) + { + if(rectcollision(upgrades[i]->base, player->base)) + { + // We have detected a collision and now call the collision + // functions of the collided objects. + upgrades[i]->collision(player, CO_PLAYER, COLLISION_NORMAL); + } + } + + // CO_TRAMPOLINE & (CO_PLAYER or CO_BADGUY) + for (Trampolines::iterator i = trampolines.begin(); i != trampolines.end(); ++i) + { + if (rectcollision((*i)->base, player->base)) + { + if (player->previous_base.y < player->base.y && + player->previous_base.y + player->previous_base.height + < (*i)->base.y + (*i)->base.height/2) + { + (*i)->collision(player, CO_PLAYER, COLLISION_SQUISH); + } + else if (player->previous_base.y <= player->base.y) + { + player->collision(*i, CO_TRAMPOLINE); + (*i)->collision(player, CO_PLAYER, COLLISION_NORMAL); + } + } + } + + // CO_FLYING_PLATFORM & (CO_PLAYER or CO_BADGUY) + for (FlyingPlatforms::iterator i = flying_platforms.begin(); i != flying_platforms.end(); ++i) + { + if (rectcollision((*i)->base, player->base)) + { + if (player->previous_base.y < player->base.y && + player->previous_base.y + player->previous_base.height + < (*i)->base.y + (*i)->base.height/2) + { + (*i)->collision(player, CO_PLAYER, COLLISION_SQUISH); + player->collision(*i, CO_FLYING_PLATFORM); + } +/* else if (player->previous_base.y <= player->base.y) + { + }*/ + } + } +} + +void +Sector::add_score(const Vector& pos, int s) +{ + player_status.score += s; + + add_object(new FloatingScore(pos, s)); +} + +void +Sector::add_bouncy_distro(const Vector& pos) +{ + add_object(new BouncyDistro(pos)); +} + +void +Sector::add_broken_brick(const Vector& pos, Tile* tile) +{ + 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(pos + Vector(16, 0), Vector(1, -4), tile); + add_broken_brick_piece(pos + Vector(16, 16), Vector(1.5, -3), tile); +} + +void +Sector::add_broken_brick_piece(const Vector& pos, const Vector& movement, + Tile* tile) +{ + add_object(new BrokenBrick(tile, pos, movement)); +} + +void +Sector::add_bouncy_brick(const Vector& pos) +{ + add_object(new BouncyBrick(pos)); +} + +BadGuy* +Sector::add_bad_guy(float x, float y, BadGuyKind kind) +{ + BadGuy* badguy = new BadGuy(kind, x, y); + add_object(badguy); + return badguy; +} + +void +Sector::add_upgrade(const Vector& pos, Direction dir, UpgradeKind kind) +{ + add_object(new Upgrade(pos, dir, kind)); +} + +bool +Sector::add_bullet(const Vector& pos, float xm, Direction dir) +{ + if(player->got_power == Player::FIRE_POWER) + { + if(bullets.size() > MAX_FIRE_BULLETS-1) + return false; + } + else if(player->got_power == Player::ICE_POWER) + { + if(bullets.size() > MAX_ICE_BULLETS-1) + return false; + } + + Bullet* new_bullet = 0; + if(player->got_power == Player::FIRE_POWER) + new_bullet = new Bullet(pos, xm, dir, FIRE_BULLET); + else if(player->got_power == Player::ICE_POWER) + new_bullet = new Bullet(pos, xm, dir, ICE_BULLET); + else + throw std::runtime_error("wrong bullet type."); + add_object(new_bullet); + + play_sound(sounds[SND_SHOOT], SOUND_CENTER_SPEAKER); + + return true; +} + +/* Break a brick: */ +bool +Sector::trybreakbrick(const Vector& pos, bool small) +{ + Tile* tile = solids->get_tile_at(pos); + if (tile->attributes & Tile::BRICK) + { + if (tile->data > 0) + { + /* Get a distro from it: */ + add_bouncy_distro( + Vector(((int)(pos.x + 1) / 32) * 32, (int)(pos.y / 32) * 32)); + + // TODO: don't handle this in a global way but per-tile... + if (!counting_distros) + { + counting_distros = true; + distro_counter = 5; + } + else + { + distro_counter--; + } + + if (distro_counter <= 0) + { + counting_distros = false; + solids->change_at(pos, tile->next_tile); + } + + play_sound(sounds[SND_DISTRO], SOUND_CENTER_SPEAKER); + player_status.score = player_status.score + SCORE_DISTRO; + player_status.distros++; + return true; + } + else if (!small) + { + /* Get rid of it: */ + solids->change_at(pos, tile->next_tile); + + /* Replace it with broken bits: */ + add_broken_brick(Vector( + ((int)(pos.x + 1) / 32) * 32, + (int)(pos.y / 32) * 32), tile); + + /* Get some score: */ + play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER); + player_status.score = player_status.score + SCORE_BRICK; + + return true; + } + } + + return false; +} + +/* Empty a box: */ +void +Sector::tryemptybox(const Vector& pos, Direction col_side) +{ + Tile* tile = solids->get_tile_at(pos); + if (!(tile->attributes & Tile::FULLBOX)) + return; + + // according to the collision side, set the upgrade direction + if(col_side == LEFT) + col_side = RIGHT; + else + col_side = LEFT; + + int posx = ((int)(pos.x+1) / 32) * 32; + int posy = (int)(pos.y/32) * 32 - 32; + switch(tile->data) + { + case 1: // Box with a distro! + 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++; + break; + + case 2: // Add a fire flower upgrade! + if (player->size == SMALL) /* Tux is small, add mints! */ + add_upgrade(Vector(posx, posy), col_side, UPGRADE_GROWUP); + else /* Tux is big, add a fireflower: */ + add_upgrade(Vector(posx, posy), col_side, UPGRADE_FIREFLOWER); + play_sound(sounds[SND_UPGRADE], SOUND_CENTER_SPEAKER); + break; + + case 5: // Add an ice flower upgrade! + if (player->size == SMALL) /* Tux is small, add mints! */ + add_upgrade(Vector(posx, posy), col_side, UPGRADE_GROWUP); + else /* Tux is big, add an iceflower: */ + add_upgrade(Vector(posx, posy), col_side, UPGRADE_ICEFLOWER); + play_sound(sounds[SND_UPGRADE], SOUND_CENTER_SPEAKER); + break; + + case 3: // Add a golden herring + add_upgrade(Vector(posx, posy), col_side, UPGRADE_HERRING); + break; + + case 4: // Add a 1up extra + add_upgrade(Vector(posx, posy), col_side, UPGRADE_1UP); + break; + default: + break; + } + + /* Empty the box: */ + solids->change_at(pos, tile->next_tile); +} + +/* Try to grab a distro: */ +void +Sector::trygrabdistro(const Vector& pos, int bounciness) +{ + Tile* tile = solids->get_tile_at(pos); + if (!(tile->attributes & Tile::COIN)) + return; + + solids->change_at(pos, tile->next_tile); + play_sound(sounds[SND_DISTRO], SOUND_CENTER_SPEAKER); + + if (bounciness == BOUNCE) + { + add_bouncy_distro(Vector(((int)(pos.x + 1) / 32) * 32, + (int)(pos.y / 32) * 32)); + } + + player_status.score = player_status.score + SCORE_DISTRO; + player_status.distros++; +} + +/* Try to bump a bad guy from below: */ +void +Sector::trybumpbadguy(const Vector& pos) +{ + // Bad guys: + for (BadGuys::iterator i = badguys.begin(); i != badguys.end(); ++i) + { + if ((*i)->base.x >= pos.x - 32 && (*i)->base.x <= pos.x + 32 && + (*i)->base.y >= pos.y - 16 && (*i)->base.y <= pos.y + 16) + { + (*i)->collision(player, CO_PLAYER, COLLISION_BUMP); + } + } + + // Upgrades: + for (unsigned int i = 0; i < upgrades.size(); i++) + { + if (upgrades[i]->base.height == 32 && + upgrades[i]->base.x >= pos.x - 32 && upgrades[i]->base.x <= pos.x + 32 && + upgrades[i]->base.y >= pos.y - 16 && upgrades[i]->base.y <= pos.y + 16) + { + upgrades[i]->collision(player, CO_PLAYER, COLLISION_BUMP); + } + } +} + +void +Sector::load_music() +{ + char* song_path; + char* song_subtitle; + + level_song = music_manager->load_music(datadir + "/music/" + song_title); + + song_path = (char *) malloc(sizeof(char) * datadir.length() + + strlen(song_title.c_str()) + 8 + 5); + song_subtitle = strdup(song_title.c_str()); + strcpy(strstr(song_subtitle, "."), "\0"); + sprintf(song_path, "%s/music/%s-fast%s", datadir.c_str(), + song_subtitle, strstr(song_title.c_str(), ".")); + if(!music_manager->exists_music(song_path)) { + level_song_fast = level_song; + } else { + level_song_fast = music_manager->load_music(song_path); + } + free(song_subtitle); + free(song_path); +} + +void +Sector::play_music(int type) +{ + currentmusic = type; + switch(currentmusic) { + case HURRYUP_MUSIC: + music_manager->play_music(level_song_fast); + break; + case LEVEL_MUSIC: + music_manager->play_music(level_song); + break; + case HERRING_MUSIC: + music_manager->play_music(herring_song); + break; + default: + music_manager->halt_music(); + break; + } +} + +int +Sector::get_music_type() +{ + return currentmusic; +} diff --git a/src/sector.h b/src/sector.h new file mode 100644 index 000000000..167674978 --- /dev/null +++ b/src/sector.h @@ -0,0 +1,141 @@ +#ifndef __SECTOR_H__ +#define __SECTOR_H__ + +#include +#include +#include "vector.h" +#include "badguy.h" +#include "special.h" +#include "musicref.h" +#include "screen/drawing_context.h" + +class GameObject; +class Background; +class Player; +class Camera; +class Trampoline; +class FlyingPlatform; +class TileMap; +class Upgrade; +class Bullet; +class BadGuy; +class Vector; +class LispReader; +class Tile; + +struct SpawnPoint +{ + std::string name; + Vector pos; +}; + +/** This class holds a sector (a part of a level) and all the game objects + * (badguys, player, background, tilemap, ...) + */ +class Sector +{ +public: + Sector(); + ~Sector(); + + /// read sector from lisp file + void parse(LispReader& reader); + void parse_old_format(LispReader& reader); + /// write sector to lisp file + void write(LispWriter& writer); + + /// activates this sector (change music, intialize player class, ...) + void activate(const std::string& spawnpoint = "main"); + + void action(float elapsed_time); + void draw(DrawingContext& context); + + /// adds a gameobject + void add_object(GameObject* object); + + const std::string& get_name() const + { return name; } + + void play_music(int musictype); + int get_music_type(); + + /** Checks for all possible collisions. And calls the + collision_handlers, which the collision_objects provide for this + case (or not). */ + void collision_handler(); + + 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); + + void add_upgrade(const Vector& pos, Direction dir, UpgradeKind kind); + bool add_bullet(const Vector& pos, float xm, Direction dir); + + /** Try to grab the coin at the given coordinates */ + void trygrabdistro(const Vector& pos, int bounciness); + + /** Try to break the brick at the given coordinates */ + bool trybreakbrick(const Vector& pos, bool small); + + /** Try to get the content out of a bonus box, thus emptying it */ + void tryemptybox(const Vector& pos, Direction col_side); + + /** Try to bumb a badguy that might we walking above Tux, thus shaking + the tile which the badguy is walking on an killing him this way */ + void trybumpbadguy(const Vector& pos); + + /** @evil@ */ + static Sector* current() + { return _current; } + +private: + void load_music(); + + static Sector* _current; + + std::string name; + + std::string song_title; + MusicRef level_song; + MusicRef level_song_fast; + +public: + float gravity; + + // some special objects, where we need direct access + Player* player; + TileMap* solids; + Background* background; + Camera* camera; + +private: + typedef std::vector BadGuys; + BadGuys badguys; + typedef std::vector Trampolines; + Trampolines trampolines; + typedef std::vector FlyingPlatforms; + FlyingPlatforms flying_platforms; + + std::vector upgrades; + std::vector bullets; + +public: // ugly + typedef std::vector GameObjects; + GameObjects gameobjects; + +private: + typedef std::vector SpawnPoints; + SpawnPoints spawnpoints; + + int distro_counter; + bool counting_distros; + int currentmusic; +}; + +#endif + diff --git a/src/special.cpp b/src/special.cpp index 8dd89ae61..0c8828846 100644 --- a/src/special.cpp +++ b/src/special.cpp @@ -29,6 +29,7 @@ #include "scene.h" #include "globals.h" #include "player.h" +#include "sector.h" #include "sprite_manager.h" #include "resources.h" @@ -94,9 +95,9 @@ Bullet::action(float elapsed_time) physic.set_velocity_y(-9); float scroll_x = - World::current()->camera->get_translation().x; + Sector::current()->camera->get_translation().x; float scroll_y = - World::current()->camera->get_translation().y; + Sector::current()->camera->get_translation().y; if (base.x < scroll_x || base.x > scroll_x + screen->w || base.y < scroll_y || @@ -181,9 +182,9 @@ Upgrade::action(float elapsed_time) /* Away from the screen? Kill it! */ float scroll_x = - World::current()->camera->get_translation().x; + Sector::current()->camera->get_translation().x; float scroll_y = - World::current()->camera->get_translation().y; + Sector::current()->camera->get_translation().y; if(base.x < scroll_x - X_OFFSCREEN_DISTANCE || base.x > scroll_x + screen->w + X_OFFSCREEN_DISTANCE || @@ -332,7 +333,7 @@ Upgrade::collision(void* p_c_object, int c_object, CollisionType type) { play_sound(sounds[SND_HERRING], SOUND_CENTER_SPEAKER); pplayer->invincible_timer.start(TUX_INVINCIBLE_TIME); - World::current()->play_music(HERRING_MUSIC); + Sector::current()->play_music(HERRING_MUSIC); } else if (kind == UPGRADE_1UP) { diff --git a/src/sprite.cpp b/src/sprite.cpp index c4d552fd5..3e8a553fe 100644 --- a/src/sprite.cpp +++ b/src/sprite.cpp @@ -30,14 +30,14 @@ Sprite::Sprite(lisp_object_t* cur) LispReader reader(cur); - if(!reader.read_string("name", &name)) + if(!reader.read_string("name", name)) st_abort("Sprite wihtout name", ""); - reader.read_int("x-hotspot", &x_hotspot); - reader.read_int("y-hotspot", &y_hotspot); - reader.read_float("fps", &fps); + reader.read_int("x-hotspot", x_hotspot); + reader.read_int("y-hotspot", y_hotspot); + reader.read_float("fps", fps); std::vector images; - if(!reader.read_string_vector("images", &images)) + if(!reader.read_string_vector("images", images)) st_abort("Sprite contains no images: ", name.c_str()); for(std::vector::size_type i = 0; i < images.size(); ++i) diff --git a/src/supertux.cpp b/src/supertux.cpp index 6d17be697..62f96b8ae 100644 --- a/src/supertux.cpp +++ b/src/supertux.cpp @@ -40,7 +40,9 @@ int main(int argc, char * argv[]) { +#ifndef DEBUG try { +#endif st_directory_setup(); parseargs(argc, argv); @@ -58,7 +60,7 @@ int main(int argc, char * argv[]) } else if (level_startup_file) { - GameSession session(level_startup_file, 1, ST_GL_LOAD_LEVEL_FILE); + GameSession session(level_startup_file, ST_GL_LOAD_LEVEL_FILE); session.run(); } else @@ -76,6 +78,7 @@ int main(int argc, char * argv[]) Surface::debug_check(); #endif st_shutdown(); +#ifndef DEBUG // we want to see the backtrace in gdb when in debug mode } catch (SuperTuxException &e) { @@ -85,6 +88,7 @@ int main(int argc, char * argv[]) { std:: cerr << "Unhandled exception: " << e.what() << std::endl; } +#endif return 0; } diff --git a/src/tile.cpp b/src/tile.cpp index d38ff62ff..0955b6168 100644 --- a/src/tile.cpp +++ b/src/tile.cpp @@ -47,41 +47,41 @@ Tile::~Tile() int Tile::read(LispReader& reader) { - if(!reader.read_int("id", &id)) { + if(!reader.read_int("id", id)) { std::cerr << "Missing tile-id.\n"; return -1; } bool value; - if(reader.read_bool("solid", &value) && value) + if(reader.read_bool("solid", value) && value) attributes |= SOLID; - if(reader.read_bool("unisolid", &value) && value) + if(reader.read_bool("unisolid", value) && value) attributes |= GOAL; - if(reader.read_bool("brick", &value) && value) + if(reader.read_bool("brick", value) && value) attributes |= BRICK; - if(reader.read_bool("ice", &value) && value) + if(reader.read_bool("ice", value) && value) attributes |= ICE; - if(reader.read_bool("water", &value) && value) + if(reader.read_bool("water", value) && value) attributes |= WATER; - if(reader.read_bool("spike", &value) && value) + if(reader.read_bool("spike", value) && value) attributes |= SPIKE; - if(reader.read_bool("fullbox", &value) && value) + if(reader.read_bool("fullbox", value) && value) attributes |= FULLBOX; - if(reader.read_bool("distro", &value) && value) + if(reader.read_bool("distro", value) && value) attributes |= COIN; - if(reader.read_bool("coin", &value) && value) + if(reader.read_bool("coin", value) && value) attributes |= COIN; - if(reader.read_bool("goal", &value) && value) + if(reader.read_bool("goal", value) && value) attributes |= GOAL; - reader.read_int("data", &data); - reader.read_int("anim-speed", &anim_speed); - reader.read_int("next-tile", &next_tile); + reader.read_int("data", data); + reader.read_int("anim-speed", anim_speed); + reader.read_int("next-tile", next_tile); std::vector filenames; - reader.read_string_vector("images", &filenames); + reader.read_string_vector("images", filenames); std::vector editor_filenames; - reader.read_string_vector("editor-images", &editor_filenames); + reader.read_string_vector("editor-images", editor_filenames); // read images for(std::vector::iterator i = filenames.begin(); @@ -162,7 +162,7 @@ void TileManager::load_tileset(std::string filename) { LispReader reader(lisp_cdr(element)); std::string filename; - reader.read_string("file", &filename); + reader.read_string("file", filename); filename = datadir + "/images/tilesets/" + filename; load_tileset(filename); } @@ -170,8 +170,8 @@ void TileManager::load_tileset(std::string filename) { TileGroup new_; LispReader reader(lisp_cdr(element)); - reader.read_string("name", &new_.name); - reader.read_int_vector("tiles", &new_.tiles); + reader.read_string("name", new_.name); + reader.read_int_vector("tiles", new_.tiles); if(!tilegroups_) tilegroups_ = new std::set; tilegroups_->insert(new_).first; @@ -179,7 +179,7 @@ void TileManager::load_tileset(std::string filename) else if (strcmp(lisp_symbol(lisp_car(element)), "properties") == 0) { LispReader reader(lisp_cdr(element)); - reader.read_int("id", &tileset_id); + reader.read_int("id", tileset_id); tileset_id *= 1000; } else diff --git a/src/tilemap.cpp b/src/tilemap.cpp index 45b2bb3c4..5eb102024 100644 --- a/src/tilemap.cpp +++ b/src/tilemap.cpp @@ -20,37 +20,99 @@ #include #include +#include +#include #include #include "screen/drawing_context.h" #include "level.h" #include "tile.h" #include "globals.h" +#include "lispreader.h" +#include "lispwriter.h" -TileMap::TileMap(Level* newlevel) - : level(newlevel) +TileMap::TileMap() + : solid(false), speed(1), width(0), height(0), layer(LAYER_TILES) { tilemanager = TileManager::instance(); } +TileMap::TileMap(LispReader& reader) + : solid(false), speed(1), width(0), height(0), layer(LAYER_TILES) +{ + tilemanager = TileManager::instance(); + + std::string layer; + if(reader.read_string("layer", layer)) { + if(layer == "background") + layer = LAYER_BACKGROUNDTILES; + else if(layer == "interactive") + layer = LAYER_TILES; + else if(layer == "foreground") + layer = LAYER_FOREGROUNDTILES; + else + std::cout << "Unknown layer '" << layer << "' in tilemap.\n"; + } + + reader.read_bool("solid", solid); + reader.read_float("speed", speed); + + if(solid && speed != 1) { + std::cout << "Speed of solid tilemap is not 1. fixing.\n"; + speed = 1; + } + + if(!reader.read_int("width", width) || + !reader.read_int("height", height)) + throw std::runtime_error("No width or height specified in tilemap."); + + if(!reader.read_int_vector("tiles", tiles)) + throw std::runtime_error("No tiles in tilemap."); + if(int(tiles.size()) != width*height) + throw std::runtime_error("wrong number of tiles in tilemap."); +} + TileMap::~TileMap() { } void +TileMap::write(LispWriter& writer) +{ + writer.start_list("tilemap"); + + if(layer == LAYER_BACKGROUNDTILES) + writer.write_string("layer", "background"); + else if(layer == LAYER_TILES) + writer.write_string("layer", "interactive"); + else if(layer == LAYER_FOREGROUNDTILES) + writer.write_string("layer", "foreground"); + else { + std::cout << "Warning unknown layer in tilemap.\n"; + } + + writer.write_bool("solid", solid); + writer.write_float("speed", speed); + writer.write_int("width", width); + writer.write_int("height", height); + writer.write_int_vector("tiles", tiles); + + writer.end_list("tilemap"); +} + +void TileMap::action(float ) { } void -TileMap::draw(const std::vector& tiles, DrawingContext& context, - int layer) +TileMap::draw(DrawingContext& context) { /** if we don't round here, we'll have a 1 pixel gap on screen sometimes. * I have no idea why */ - float start_x = roundf(context.get_translation().x); - float start_y = roundf(context.get_translation().y); - float end_x = std::min(start_x + screen->w, float(level->width * 32)); - float end_y = std::min(start_y + screen->h, float(level->height * 32)); + float start_x = roundf(context.get_translation().x * speed); + float start_y = roundf(context.get_translation().y * speed); + float end_x = std::min(start_x + screen->w, float(width * 32)); + float end_y = std::min(start_y + screen->h, float(height * 32)); start_x -= int(start_x) % 32; start_y -= int(start_y) % 32; int tsx = int(start_x / 32); // tilestartindex x @@ -60,15 +122,87 @@ TileMap::draw(const std::vector& tiles, DrawingContext& context, int tx, ty; for(pos.x = start_x, tx = tsx; pos.x < end_x; pos.x += 32, ++tx) { for(pos.y = start_y, ty = tsy; pos.y < end_y; pos.y += 32, ++ty) { - tilemanager->draw_tile(context, tiles[ty*level->width + tx], pos, layer); + tilemanager->draw_tile(context, tiles[ty*width + tx], pos, layer); } } } void -TileMap::draw(DrawingContext& context) +TileMap::set(int newwidth, int newheight, const std::vector&newt, + int newlayer, bool newsolid) +{ + assert(int(newt.size()) == newwidth * newheight); + + width = newwidth; + height = newheight; + tiles = newt; + layer = newlayer; + solid = newsolid; +} + +void +TileMap::resize(int new_width, int new_height) +{ + if(new_width < width) { + // remap tiles for new width + for(int y = 0; y < height && y < new_height; ++y) { + for(int x = 0; x < new_width; ++x) { + tiles[y * new_width + x] = tiles[y * width + x]; + } + } + } + + tiles.resize(new_width * new_height); + + if(new_width > width) { + // remap tiles + for(int y = std::min(height, new_height)-1; y >= 0; --y) { + for(int x = new_width-1; x >= 0; --x) { + if(x >= width) { + tiles[y * new_width + x] = 0; + } else { + tiles[y * new_width + x] = tiles[y * width + x]; + } + } + } + } + + height = new_height; + width = new_width; +} + +Tile* +TileMap::get_tile(int x, int y) const +{ + if(x < 0 || x >= width || y < 0 || y >= height) + return tilemanager->get(0); + + return tilemanager->get(tiles[y*width + x]); +} + +Tile* +TileMap::get_tile_at(const Vector& pos) const +{ + return get_tile(int(pos.x)/32, int(pos.y)/32); +} + +unsigned int +TileMap::get_tile_id_at(const Vector& pos) const +{ + int x = int(pos.x)/32; + int y = int(pos.y)/32; + return tiles[y*width + x]; +} + +void +TileMap::change(int x, int y, unsigned int newtile) +{ + assert(x >= 0 && x < width && y >= 0 && y < height); + tiles[y*width + x] = newtile; +} + +void +TileMap::change_at(const Vector& pos, unsigned int newtile) { - draw(level->bg_tiles, context, LAYER_BACKGROUNDTILES); - draw(level->ia_tiles, context, LAYER_TILES); - draw(level->fg_tiles, context, LAYER_FOREGROUNDTILES); + change(int(pos.x)/32, int(pos.y)/32, newtile); } diff --git a/src/tilemap.h b/src/tilemap.h index 8d8f291f3..b629ccd64 100644 --- a/src/tilemap.h +++ b/src/tilemap.h @@ -22,27 +22,65 @@ #include #include "game_object.h" #include "serializable.h" +#include "vector.h" class Level; class TileManager; +class LispReader; +class Tile; /** * This class is reponsible for drawing the level tiles */ -class TileMap : public GameObject +class TileMap : public GameObject, public Serializable { public: - TileMap(Level* level); + TileMap(); + TileMap(LispReader& reader); virtual ~TileMap(); + virtual void write(LispWriter& writer); + virtual void action(float elapsed_time); - virtual void draw(const std::vector& tiles, - DrawingContext& context, int layer); virtual void draw(DrawingContext& context); + + void set(int width, int height, const std::vector& vec, + int layer, bool solid); + + /** resizes the tilemap to a new width and height (tries to not destroy the + * existing map) + */ + void resize(int newwidth, int newheight); + + size_t get_width() const + { return width; } + + size_t get_height() const + { return height; } + + bool is_solid() const + { return solid; } + + unsigned int get_tile_id_at(const Vector& pos) const; + + /// returns tile in row y and column y (of the tilemap) + Tile* get_tile(int x, int y) const; + /// returns tile at position pos (in world coordinates) + Tile* get_tile_at(const Vector& pos) const; + + void change(int x, int y, unsigned int newtile); + + void change_at(const Vector& pos, unsigned int newtile); + +public: + std::vector tiles; private: TileManager* tilemanager; - Level* level; + bool solid; + float speed; + int width, height; + int layer; }; #endif diff --git a/src/title.cpp b/src/title.cpp index 9ef6a6ddf..21cd91890 100644 --- a/src/title.cpp +++ b/src/title.cpp @@ -49,6 +49,8 @@ #include "player.h" #include "math.h" #include "tile.h" +#include "sector.h" +#include "tilemap.h" #include "resources.h" static Surface* bkg_title; @@ -62,8 +64,10 @@ static int frame; static unsigned int last_update_time; static unsigned int update_time; -std::vector contrib_subsets; -std::string current_contrib_subset; +static GameSession* titlesession; + +static std::vector contrib_subsets; +static LevelSubset* current_contrib_subset = 0; void free_contrib_menu() { @@ -115,20 +119,26 @@ void check_contrib_menu() // FIXME: This shouln't be busy looping LevelSubset& subset = * (contrib_subsets[index]); - current_contrib_subset = subset.name; + current_contrib_subset = ⊂ contrib_subset_menu->clear(); contrib_subset_menu->additem(MN_LABEL, subset.title, 0,0); contrib_subset_menu->additem(MN_HL,"",0,0); + for (int i = 1; i <= subset.levels; ++i) { - Level level; - level.load(subset.name, i, 0); - contrib_subset_menu->additem(MN_ACTION, level.name, 0, 0, i); + Level* level = new Level; + level->load(subset.get_level_filename(i)); + contrib_subset_menu->additem(MN_ACTION, level->get_name(), 0, 0, i); + delete level; } + contrib_subset_menu->additem(MN_HL,"",0,0); contrib_subset_menu->additem(MN_BACK, "Back", 0, 0); + + titlesession->get_current_sector()->activate(); + titlesession->set_current(); } } else @@ -146,20 +156,22 @@ void check_contrib_subset_menu() if (contrib_subset_menu->get_item_by_id(index).kind == MN_ACTION) { std::cout << "Starting level: " << index << std::endl; - GameSession session(current_contrib_subset, index, ST_GL_PLAY); + + GameSession session( + current_contrib_subset->get_level_filename(index), ST_GL_PLAY); session.run(); player_status.reset(); Menu::set_current(main_menu); + titlesession->get_current_sector()->activate(); + titlesession->set_current(); } } } -void draw_demo(GameSession* session, double frame_ratio) +void draw_demo(double frame_ratio) { - World* world = session->get_world(); - World::set_current(world); - Level* plevel = session->get_level(); - Player* tux = world->get_tux(); + Sector* world = titlesession->get_current_sector(); + Player* tux = world->player; world->play_music(LEVEL_MUSIC); @@ -180,7 +192,7 @@ void draw_demo(GameSession* session, double frame_ratio) } // Wrap around at the end of the level back to the beginnig - if(plevel->width * 32 - 320 < tux->base.x) + if(world->solids->get_width() * 32 - 320 < tux->base.x) { tux->level_begin(); } @@ -198,7 +210,7 @@ void draw_demo(GameSession* session, double frame_ratio) walking = false; } - world->draw(); + world->draw(*titlesession->context); } /* --- TITLE SCREEN --- */ @@ -210,7 +222,7 @@ void title(void) st_pause_ticks_init(); - GameSession session(datadir + "/levels/misc/menu.stl", 0, ST_GL_DEMO_GAME); + titlesession = new GameSession(datadir + "/levels/misc/menu.stl", ST_GL_DEMO_GAME); /* Load images: */ bkg_title = new Surface(datadir + "/images/background/arctis.jpg", IGNORE_ALPHA); @@ -224,7 +236,7 @@ void title(void) random_timer.start(rand() % 2000 + 2000); Menu::set_current(main_menu); - DrawingContext& context = World::current()->context; + DrawingContext& context = *titlesession->context; while (Menu::current()) { // if we spent to much time on a menu entry @@ -251,10 +263,8 @@ void title(void) } /* Draw the background: */ - draw_demo(&session, frame_ratio); + draw_demo(frame_ratio); - context.push_transform(); - context.set_translation(Vector(0, 0)); if (Menu::current() == main_menu) context.draw_surface(logo, Vector(screen->w/2 - logo->w/2, 30), LAYER_FOREGROUND1+1); @@ -265,7 +275,6 @@ void title(void) "This game comes with ABSOLUTELY NO WARRANTY. This is free software, and you\n" "are welcome to redistribute it under certain conditions; see the file COPYING\n" "for details.\n", Vector(0, screen->h - 70), LAYER_FOREGROUND1); - context.pop_transform(); /* Don't draw menu, if quit is true */ Menu* menu = Menu::current(); @@ -327,6 +336,8 @@ void title(void) else if (process_load_game_menu()) { // FIXME: shouldn't be needed if GameSession doesn't relay on global variables + titlesession->get_current_sector()->activate(); + titlesession->set_current(); //titletux.level_begin(); update_time = st_get_ticks(); } @@ -356,6 +367,7 @@ void title(void) /* Free surfaces: */ free_contrib_menu(); + delete titlesession; delete bkg_title; delete logo; delete img_choose_subset; diff --git a/src/world.cpp b/src/world.cpp deleted file mode 100644 index 3f63a2ed1..000000000 --- a/src/world.cpp +++ /dev/null @@ -1,642 +0,0 @@ -// $Id$ -// -// SuperTux -// Copyright (C) 2000 Bill Kendrick -// Copyright (C) 2004 Tobias Glaesser -// Copyright (C) 2004 Ingo Ruhnke -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA -// 02111-1307, USA. - -#include -#include -#include -#include -#include "globals.h" -#include "scene.h" -#include "screen/screen.h" -#include "defines.h" -#include "world.h" -#include "level.h" -#include "tile.h" -#include "resources.h" -#include "gameobjs.h" -#include "camera.h" -#include "background.h" -#include "tilemap.h" - -Surface* img_distro[4]; - -World* World::current_ = 0; - -World::World(const std::string& filename, int level_nr) - : level(0), tux(0), background(0), camera(0) -{ - // FIXME: Move this to action and draw and everywhere else where the - // world calls child functions - current_ = this; - - tux = new Player; - add_object(tux); - - level = new Level; - camera = new Camera(tux, level); - add_object(camera); - - if(level_nr >= 0) { - level->load(filename, level_nr, this); - } else { - level->load(filename, this); - } - tux->move(level->start_pos); - - set_defaults(); - - // add background - activate_particle_systems(); - - // add tilemap - add_object(new TileMap(level)); - level->load_song(); - - apply_bonuses(); -} - -void -World::apply_bonuses() -{ - // Apply bonuses from former levels - switch (player_status.bonus) - { - case PlayerStatus::NO_BONUS: - break; - - case PlayerStatus::FLOWER_BONUS: - tux->got_power = Player::FIRE_POWER; // FIXME: add ice power to here - // fall through - - case PlayerStatus::GROWUP_BONUS: - tux->grow(false); - break; - } -} - -World::~World() -{ - for (std::vector::iterator i = gameobjects.begin(); - i != gameobjects.end(); ++i) { - delete *i; - } - - delete level; - - current_ = 0; -} - -void -World::set_defaults() -{ - player_status.score_multiplier = 1; - - counting_distros = false; - distro_counter = 0; - - /* set current song/music */ - currentmusic = LEVEL_MUSIC; -} - -void -World::add_object(GameObject* object) -{ - // XXX hack for now until new collision code is ready - BadGuy* badguy = dynamic_cast (object); - if(badguy) - bad_guys.push_back(badguy); - Bullet* bullet = dynamic_cast (object); - if(bullet) - bullets.push_back(bullet); - Upgrade* upgrade = dynamic_cast (object); - if(upgrade) - upgrades.push_back(upgrade); - Trampoline* trampoline = dynamic_cast (object); - if(trampoline) - trampolines.push_back(trampoline); - FlyingPlatform* flying_platform = dynamic_cast (object); - if(flying_platform) - flying_platforms.push_back(flying_platform); - Background* background = dynamic_cast (object); - if(background) - this->background = background; - - gameobjects.push_back(object); -} - -void -World::parse_objects(lisp_object_t* cur) -{ - while(!lisp_nil_p(cur)) { - lisp_object_t* data = lisp_car(cur); - std::string object_type = lisp_symbol(lisp_car(data)); - - LispReader reader(lisp_cdr(data)); - - if(object_type == "trampoline") { - add_object(new Trampoline(reader)); - } - else if(object_type == "flying-platform") { - add_object(new FlyingPlatform(reader)); - } - else { - BadGuyKind kind = badguykind_from_string(object_type); - add_object(new BadGuy(kind, reader)); - } - - cur = lisp_cdr(cur); - } -} - -void -World::activate_particle_systems() -{ - if (level->particle_system == "clouds") - { - add_object(new CloudParticleSystem); - } - else if (level->particle_system == "snow") - { - add_object(new SnowParticleSystem); - } - else if (level->particle_system != "") - { - st_abort("unknown particle system specified in level", ""); - } -} - -void -World::draw() -{ - /* Draw objects */ - for(std::vector::iterator i = gameobjects.begin(); - i != gameobjects.end(); ++i) - if((*i)->is_valid()) - (*i)->draw(context); -} - -void -World::action(float elapsed_time) -{ - tux->check_bounds(context); - - /* update objects (don't use iterators here, because the list might change - * during the iteration) - */ - for(size_t i = 0; i < gameobjects.size(); ++i) - if(gameobjects[i]->is_valid()) - gameobjects[i]->action(elapsed_time); - - /* Handle all possible collisions. */ - collision_handler(); - - /** cleanup marked objects */ - for(std::vector::iterator i = gameobjects.begin(); - i != gameobjects.end(); /* nothing */) { - if((*i)->is_valid() == false) { - BadGuy* badguy = dynamic_cast (*i); - if(badguy) { - bad_guys.erase(std::remove(bad_guys.begin(), bad_guys.end(), badguy), - bad_guys.end()); - } - Bullet* bullet = dynamic_cast (*i); - if(bullet) { - bullets.erase( - std::remove(bullets.begin(), bullets.end(), bullet), - bullets.end()); - } - Upgrade* upgrade = dynamic_cast (*i); - if(upgrade) { - upgrades.erase( - std::remove(upgrades.begin(), upgrades.end(), upgrade), - upgrades.end()); - } - Trampoline* trampoline = dynamic_cast (*i); - if(trampoline) { - trampolines.erase( - std::remove(trampolines.begin(), trampolines.end(), trampoline), - trampolines.end()); - } - FlyingPlatform* flying_platform= dynamic_cast (*i); - if(flying_platform) { - flying_platforms.erase( - std::remove(flying_platforms.begin(), flying_platforms.end(), flying_platform), - flying_platforms.end()); - } - - delete *i; - i = gameobjects.erase(i); - } else { - ++i; - } - } -} - -void -World::collision_handler() -{ - // CO_BULLET & CO_BADGUY check - for(unsigned int i = 0; i < bullets.size(); ++i) - { - for (BadGuys::iterator j = bad_guys.begin(); j != bad_guys.end(); ++j) - { - if((*j)->dying != DYING_NOT) - continue; - - if(rectcollision(bullets[i]->base, (*j)->base)) - { - // We have detected a collision and now call the - // collision functions of the collided objects. - (*j)->collision(bullets[i], CO_BULLET, COLLISION_NORMAL); - bullets[i]->collision(CO_BADGUY); - break; // bullet is invalid now, so break - } - } - } - - /* CO_BADGUY & CO_BADGUY check */ - for (BadGuys::iterator i = bad_guys.begin(); i != bad_guys.end(); ++i) - { - if((*i)->dying != DYING_NOT) - continue; - - BadGuys::iterator j = i; - ++j; - for (; j != bad_guys.end(); ++j) - { - if(j == i || (*j)->dying != DYING_NOT) - continue; - - if(rectcollision((*i)->base, (*j)->base)) - { - // We have detected a collision and now call the - // collision functions of the collided objects. - (*j)->collision(*i, CO_BADGUY); - (*i)->collision(*j, CO_BADGUY); - } - } - } - - if(tux->dying != DYING_NOT) return; - - // CO_BADGUY & CO_PLAYER check - for (BadGuys::iterator i = bad_guys.begin(); i != bad_guys.end(); ++i) - { - if((*i)->dying != DYING_NOT) - continue; - - if(rectcollision_offset((*i)->base, tux->base, 0, 0)) - { - // We have detected a collision and now call the collision - // functions of the collided objects. - if (tux->previous_base.y < tux->base.y && - tux->previous_base.y + tux->previous_base.height - < (*i)->base.y + (*i)->base.height/2 - && !tux->invincible_timer.started()) - { - (*i)->collision(tux, CO_PLAYER, COLLISION_SQUISH); - } - else - { - tux->collision(*i, CO_BADGUY); - (*i)->collision(tux, CO_PLAYER, COLLISION_NORMAL); - } - } - } - - // CO_UPGRADE & CO_PLAYER check - for(unsigned int i = 0; i < upgrades.size(); ++i) - { - if(rectcollision(upgrades[i]->base, tux->base)) - { - // We have detected a collision and now call the collision - // functions of the collided objects. - upgrades[i]->collision(tux, CO_PLAYER, COLLISION_NORMAL); - } - } - - // CO_TRAMPOLINE & (CO_PLAYER or CO_BADGUY) - for (Trampolines::iterator i = trampolines.begin(); i != trampolines.end(); ++i) - { - if (rectcollision((*i)->base, tux->base)) - { - if (tux->previous_base.y < tux->base.y && - tux->previous_base.y + tux->previous_base.height - < (*i)->base.y + (*i)->base.height/2) - { - (*i)->collision(tux, CO_PLAYER, COLLISION_SQUISH); - } - else if (tux->previous_base.y <= tux->base.y) - { - tux->collision(*i, CO_TRAMPOLINE); - (*i)->collision(tux, CO_PLAYER, COLLISION_NORMAL); - } - } - } - - // CO_FLYING_PLATFORM & (CO_PLAYER or CO_BADGUY) - for (FlyingPlatforms::iterator i = flying_platforms.begin(); i != flying_platforms.end(); ++i) - { - if (rectcollision((*i)->base, tux->base)) - { - if (tux->previous_base.y < tux->base.y && - tux->previous_base.y + tux->previous_base.height - < (*i)->base.y + (*i)->base.height/2) - { - (*i)->collision(tux, CO_PLAYER, COLLISION_SQUISH); - tux->collision(*i, CO_FLYING_PLATFORM); - } -/* else if (tux->previous_base.y <= tux->base.y) - { - }*/ - } - } -} - -void -World::add_score(const Vector& pos, int s) -{ - player_status.score += s; - - add_object(new FloatingScore(pos, s)); -} - -void -World::add_bouncy_distro(const Vector& pos) -{ - add_object(new BouncyDistro(pos)); -} - -void -World::add_broken_brick(const Vector& pos, Tile* tile) -{ - 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(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(const Vector& pos, const Vector& movement, - Tile* tile) -{ - add_object(new BrokenBrick(tile, pos, movement)); -} - -void -World::add_bouncy_brick(const Vector& pos) -{ - add_object(new BouncyBrick(pos)); -} - -BadGuy* -World::add_bad_guy(float x, float y, BadGuyKind kind) -{ - BadGuy* badguy = new BadGuy(kind, x, y); - add_object(badguy); - return badguy; -} - -void -World::add_upgrade(const Vector& pos, Direction dir, UpgradeKind kind) -{ - add_object(new Upgrade(pos, dir, kind)); -} - -bool -World::add_bullet(const Vector& pos, float xm, Direction dir) -{ - if(tux->got_power == Player::FIRE_POWER) - { - if(bullets.size() > MAX_FIRE_BULLETS-1) - return false; - } - else if(tux->got_power == Player::ICE_POWER) - { - if(bullets.size() > MAX_ICE_BULLETS-1) - return false; - } - - Bullet* new_bullet = 0; - if(tux->got_power == Player::FIRE_POWER) - new_bullet = new Bullet(pos, xm, dir, FIRE_BULLET); - else if(tux->got_power == Player::ICE_POWER) - new_bullet = new Bullet(pos, xm, dir, ICE_BULLET); - else - st_abort("wrong bullet type.", ""); - add_object(new_bullet); - - play_sound(sounds[SND_SHOOT], SOUND_CENTER_SPEAKER); - - return true; -} - -void -World::play_music(int musictype) -{ - currentmusic = musictype; - switch(currentmusic) { - case HURRYUP_MUSIC: - music_manager->play_music(get_level()->get_level_music_fast()); - break; - case LEVEL_MUSIC: - music_manager->play_music(get_level()->get_level_music()); - break; - case HERRING_MUSIC: - music_manager->play_music(herring_song); - break; - default: - music_manager->halt_music(); - break; - } -} - -int -World::get_music_type() -{ - return currentmusic; -} - -/* Break a brick: */ -bool -World::trybreakbrick(float x, float y, bool small) -{ - Level* plevel = get_level(); - - Tile* tile = gettile(x, y); - if (tile->attributes & Tile::BRICK) - { - if (tile->data > 0) - { - /* Get a distro from it: */ - 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) - { - counting_distros = true; - distro_counter = 5; - } - else - { - distro_counter--; - } - - if (distro_counter <= 0) - { - counting_distros = false; - plevel->change(x, y, TM_IA, tile->next_tile); - } - - play_sound(sounds[SND_DISTRO], SOUND_CENTER_SPEAKER); - player_status.score = player_status.score + SCORE_DISTRO; - player_status.distros++; - return true; - } - else if (!small) - { - /* Get rid of it: */ - plevel->change(x, y, TM_IA, tile->next_tile); - - /* Replace it with broken bits: */ - add_broken_brick(Vector( - ((int)(x + 1) / 32) * 32, - (int)(y / 32) * 32), tile); - - /* Get some score: */ - play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER); - player_status.score = player_status.score + SCORE_BRICK; - - return true; - } - } - - return false; -} - -/* Empty a box: */ -void -World::tryemptybox(float x, float y, Direction col_side) -{ - Tile* tile = gettile(x,y); - if (!(tile->attributes & Tile::FULLBOX)) - return; - - // according to the collision side, set the upgrade direction - if(col_side == LEFT) - col_side = RIGHT; - else - col_side = LEFT; - - int posx = ((int)(x+1) / 32) * 32; - int posy = (int)(y/32) * 32 - 32; - switch(tile->data) - { - case 1: // Box with a distro! - 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++; - break; - - case 2: // Add a fire flower upgrade! - if (tux->size == SMALL) /* Tux is small, add mints! */ - add_upgrade(Vector(posx, posy), col_side, UPGRADE_GROWUP); - else /* Tux is big, add a fireflower: */ - add_upgrade(Vector(posx, posy), col_side, UPGRADE_FIREFLOWER); - play_sound(sounds[SND_UPGRADE], SOUND_CENTER_SPEAKER); - break; - - case 5: // Add an ice flower upgrade! - if (tux->size == SMALL) /* Tux is small, add mints! */ - add_upgrade(Vector(posx, posy), col_side, UPGRADE_GROWUP); - else /* Tux is big, add an iceflower: */ - add_upgrade(Vector(posx, posy), col_side, UPGRADE_ICEFLOWER); - play_sound(sounds[SND_UPGRADE], SOUND_CENTER_SPEAKER); - break; - - case 3: // Add a golden herring - add_upgrade(Vector(posx, posy), col_side, UPGRADE_HERRING); - break; - - case 4: // Add a 1up extra - add_upgrade(Vector(posx, posy), col_side, UPGRADE_1UP); - break; - default: - break; - } - - /* Empty the box: */ - level->change(x, y, TM_IA, tile->next_tile); -} - -/* Try to grab a distro: */ -void -World::trygrabdistro(float x, float y, int bounciness) -{ - Tile* tile = gettile(x, y); - if (tile && (tile->attributes & Tile::COIN)) - { - level->change(x, y, TM_IA, tile->next_tile); - play_sound(sounds[SND_DISTRO], SOUND_CENTER_SPEAKER); - - if (bounciness == BOUNCE) - { - add_bouncy_distro(Vector(((int)(x + 1) / 32) * 32, - (int)(y / 32) * 32)); - } - - player_status.score = player_status.score + SCORE_DISTRO; - player_status.distros++; - } -} - -/* Try to bump a bad guy from below: */ -void -World::trybumpbadguy(float x, float y) -{ - // Bad guys: - for (BadGuys::iterator i = bad_guys.begin(); i != bad_guys.end(); ++i) - { - if ((*i)->base.x >= x - 32 && (*i)->base.x <= x + 32 && - (*i)->base.y >= y - 16 && (*i)->base.y <= y + 16) - { - (*i)->collision(tux, CO_PLAYER, COLLISION_BUMP); - } - } - - // Upgrades: - for (unsigned int i = 0; i < upgrades.size(); i++) - { - if (upgrades[i]->base.height == 32 && - upgrades[i]->base.x >= x - 32 && upgrades[i]->base.x <= x + 32 && - upgrades[i]->base.y >= y - 16 && upgrades[i]->base.y <= y + 16) - { - upgrades[i]->collision(tux, CO_PLAYER, COLLISION_BUMP); - } - } -} - -/* EOF */ - diff --git a/src/world.h b/src/world.h deleted file mode 100644 index d886cd3a4..000000000 --- a/src/world.h +++ /dev/null @@ -1,141 +0,0 @@ -// $Id$ -// -// SuperTux -// Copyright (C) 2000 Bill Kendrick -// Copyright (C) 2004 Tobias Glaesser -// Copyright (C) 2004 Ingo Ruhnke -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifndef SUPERTUX_WORLD_H -#define SUPERTUX_WORLD_H - -#include -#include -#include "type.h" -#include "scene.h" -#include "special.h" -#include "badguy.h" -#include "particlesystem.h" -#include "screen/drawing_context.h" - -class Camera; -class Level; -class Background; -class Trampoline; -class FlyingPlatform; - -/** The World class holds a level and all the game objects (badguys, - bouncy distros, etc) that are needed to run a game. */ -class World -{ -private: - typedef std::list BadGuys; - BadGuys bad_guys_to_add; - typedef std::list Trampolines; - Trampolines trampolines; - typedef std::list FlyingPlatforms; - FlyingPlatforms flying_platforms; - Level* level; - Player* tux; - - int distro_counter; - bool counting_distros; - int currentmusic; - - static World* current_; -public: - Background* background; - BadGuys bad_guys; - - std::vector upgrades; - std::vector bullets; - std::vector gameobjects; - - Camera* camera; - DrawingContext context; - -public: - static World* current() - { return current_; } - static void set_current(World* w) - { current_ = w; } - - World(const std::string& filename, int level_nr = -1); - ~World(); - - Level* get_level() - { return level; } - Player* get_tux() - { return tux; } - - void add_object(GameObject* object); - - void set_defaults(); - - void draw(); - void action(float elapsed_time); - - void play_music(int musictype); - int get_music_type(); - - /** Checks for all possible collisions. And calls the - collision_handlers, which the collision_objects provide for this - case (or not). */ - void collision_handler(); - - void parse_objects(lisp_object_t* cur); - - void activate_particle_systems(); - - 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); - - void add_upgrade(const Vector& pos, Direction dir, UpgradeKind kind); - bool add_bullet(const Vector& pos, float xm, Direction dir); - - /** Try to grab the coin at the given coordinates */ - void trygrabdistro(float x, float y, int bounciness); - - /** Try to break the brick at the given coordinates */ - bool trybreakbrick(float x, float y, bool small); - - /** Try to get the content out of a bonus box, thus emptying it */ - void tryemptybox(float x, float y, Direction col_side); - - /** Try to bumb a badguy that might we walking above Tux, thus shaking - the tile which the badguy is walking on an killing him this way */ - void trybumpbadguy(float x, float y); - - /** Apply bonuses active in the player status, used to reactivate - bonuses from former levels */ - void apply_bonuses(); -}; - -/** FIMXE: Workaround for the leveleditor mainly */ -extern World global_world; - -#endif /*SUPERTUX_WORLD_H*/ - -/* Local Variables: */ -/* mode:c++ */ -/* End: */ - diff --git a/src/worldmap.cpp b/src/worldmap.cpp index 944a39cf7..3f529e01c 100644 --- a/src/worldmap.cpp +++ b/src/worldmap.cpp @@ -29,6 +29,7 @@ #include "lispreader.h" #include "gameloop.h" #include "setup.h" +#include "sector.h" #include "worldmap.h" #include "resources.h" @@ -115,14 +116,14 @@ TileManager::TileManager() tile->auto_walk = false; LispReader reader(lisp_cdr(element)); - reader.read_int("id", &id); - reader.read_bool("north", &tile->north); - reader.read_bool("south", &tile->south); - reader.read_bool("west", &tile->west); - reader.read_bool("east", &tile->east); - reader.read_bool("stop", &tile->stop); - reader.read_bool("auto-walk", &tile->auto_walk); - reader.read_string("image", &filename); + reader.read_int("id", id); + reader.read_bool("north", tile->north); + reader.read_bool("south", tile->south); + reader.read_bool("west", tile->west); + reader.read_bool("east", tile->east); + reader.read_bool("stop", tile->stop); + reader.read_bool("auto-walk", tile->auto_walk); + reader.read_string("image", filename); tile->sprite = new Surface( datadir + "/images/worldmap/" + filename, @@ -393,15 +394,15 @@ WorldMap::load_map() if (strcmp(lisp_symbol(lisp_car(element)), "tilemap") == 0) { LispReader reader(lisp_cdr(element)); - reader.read_int("width", &width); - reader.read_int("height", &height); - reader.read_int_vector("data", &tilemap); + reader.read_int("width", width); + reader.read_int("height", height); + reader.read_int_vector("data", tilemap); } else if (strcmp(lisp_symbol(lisp_car(element)), "properties") == 0) { LispReader reader(lisp_cdr(element)); - reader.read_string("name", &name); - reader.read_string("music", &music); + reader.read_string("name", name); + reader.read_string("music", music); } else if (strcmp(lisp_symbol(lisp_car(element)), "levels") == 0) { @@ -422,10 +423,10 @@ WorldMap::load_map() level.south = true; level.west = true; - reader.read_string("extro-filename", &level.extro_filename); - reader.read_string("name", &level.name); - reader.read_int("x", &level.x); - reader.read_int("y", &level.y); + reader.read_string("extro-filename", level.extro_filename); + reader.read_string("name", level.name); + reader.read_int("x", level.x); + reader.read_int("y", level.y); levels.push_back(level); } @@ -471,7 +472,7 @@ void WorldMap::get_level_title(Level& level) if (strcmp(lisp_symbol(lisp_car(root_obj)), "supertux-level") == 0) { LispReader reader(lisp_cdr(root_obj)); - reader.read_string("name", &level.title); + reader.read_string("name", level.title); } lisp_free(root_obj); @@ -644,7 +645,7 @@ WorldMap::update(float delta) shrink_fade(Vector((level->x*32 + 16 + offset.x),(level->y*32 + 16 + offset.y)), 500); GameSession session(datadir + "/levels/" + level->name, - 1, ST_GL_LOAD_LEVEL_FILE); + ST_GL_LOAD_LEVEL_FILE); switch (session.run()) { @@ -653,10 +654,10 @@ WorldMap::update(float delta) bool old_level_state = level->solved; level->solved = true; - if (session.get_world()->get_tux()->got_power != - session.get_world()->get_tux()->NONE_POWER) + if (session.get_current_sector()->player->got_power != + session.get_current_sector()->player->NONE_POWER) player_status.bonus = PlayerStatus::FLOWER_BONUS; - else if (session.get_world()->get_tux()->size == BIG) + else if (session.get_current_sector()->player->size == BIG) player_status.bonus = PlayerStatus::GROWUP_BONUS; else player_status.bonus = PlayerStatus::NO_BONUS; @@ -841,9 +842,6 @@ WorldMap::draw(DrawingContext& context, const Vector& offset) void WorldMap::draw_status(DrawingContext& context) { - context.push_transform(); - context.set_translation(Vector(0, 0)); - char str[80]; sprintf(str, "%d", player_status.score); @@ -1014,25 +1012,25 @@ WorldMap::loadgame(const std::string& filename) cur = lisp_cdr(cur); LispReader reader(cur); - reader.read_int("lives", &player_status.lives); - reader.read_int("score", &player_status.score); - reader.read_int("distros", &player_status.distros); + reader.read_int("lives", player_status.lives); + reader.read_int("score", player_status.score); + reader.read_int("distros", player_status.distros); if (player_status.lives < 0) player_status.lives = START_LIVES; lisp_object_t* tux_cur = 0; - if (reader.read_lisp("tux", &tux_cur)) + if (reader.read_lisp("tux", tux_cur)) { Vector p; std::string back_str = "none"; std::string bonus_str = "none"; LispReader tux_reader(tux_cur); - tux_reader.read_float("x", &p.x); - tux_reader.read_float("y", &p.y); - tux_reader.read_string("back", &back_str); - tux_reader.read_string("bonus", &bonus_str); + tux_reader.read_float("x", p.x); + tux_reader.read_float("y", p.y); + tux_reader.read_string("back", back_str); + tux_reader.read_string("bonus", bonus_str); player_status.bonus = string_to_bonus(bonus_str); tux->back_direction = string_to_direction(back_str); @@ -1040,7 +1038,7 @@ WorldMap::loadgame(const std::string& filename) } lisp_object_t* level_cur = 0; - if (reader.read_lisp("levels", &level_cur)) + if (reader.read_lisp("levels", level_cur)) { while(level_cur) { @@ -1053,8 +1051,8 @@ WorldMap::loadgame(const std::string& filename) bool solved = false; LispReader level_reader(data); - level_reader.read_string("name", &name); - level_reader.read_bool("solved", &solved); + level_reader.read_string("name", name); + level_reader.read_bool("solved", solved); for(Levels::iterator i = levels.begin(); i != levels.end(); ++i) {