From: Matthias Braun Date: Fri, 26 Nov 2004 14:45:42 +0000 (+0000) Subject: some cleanups memory leak fixes and moving of source files X-Git-Url: https://git.verplant.org/?a=commitdiff_plain;h=6f801c22d97251799740317fb1d0caf2e744b321;p=supertux.git some cleanups memory leak fixes and moving of source files SVN-Revision: 2202 --- diff --git a/lib/special/sprite.cpp b/lib/special/sprite.cpp index 6ae659855..f07cfc1ef 100644 --- a/lib/special/sprite.cpp +++ b/lib/special/sprite.cpp @@ -87,7 +87,7 @@ Sprite::update() frame += frame_inc; - if(frame > get_frames()) { + if(frame >= get_frames()) { frame = fmodf(frame+get_frames(), get_frames()); animation_loops--; diff --git a/src/background.cpp b/src/background.cpp deleted file mode 100644 index 60d217cf3..000000000 --- a/src/background.cpp +++ /dev/null @@ -1,131 +0,0 @@ -// $Id$ -// -// SuperTux - A Jump'n Run -// Copyright (C) 2004 Matthias Braun - -#include "background.h" -#include "app/globals.h" -#include "camera.h" -#include "video/drawing_context.h" -#include "utils/lispwriter.h" - -Background::Background() - : type(INVALID), layer(LAYER_BACKGROUND0), image(0) -{ -} - -Background::Background(LispReader& reader) - : type(INVALID), layer(LAYER_BACKGROUND0), image(0) -{ - reader.read_int("layer", layer); - if(reader.read_string("image", imagefile) - && reader.read_float("speed", speed)) { - set_image(imagefile, speed); - } - - std::vector bkgd_top_color, bkgd_bottom_color; - if(reader.read_int_vector("top_color", bkgd_top_color) && - reader.read_int_vector("bottom_color", bkgd_bottom_color)) - set_gradient(Color(bkgd_top_color), Color(bkgd_bottom_color)); -} - -Background::~Background() -{ - printf("bgfree.\n"); - 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) { - std::vector bkgd_top_color, bkgd_bottom_color; - bkgd_top_color.push_back(gradient_top.red); - bkgd_top_color.push_back(gradient_top.green); - bkgd_top_color.push_back(gradient_top.blue); - bkgd_bottom_color.push_back(gradient_top.red); - bkgd_bottom_color.push_back(gradient_top.green); - bkgd_bottom_color.push_back(gradient_top.blue); - writer.write_int_vector("top_color", bkgd_top_color); - writer.write_int_vector("bottom_color", bkgd_bottom_color); - } - writer.write_int("layer", layer); - - writer.end_list("background"); -} - -void -Background::action(float) -{ -} - -void -Background::set_image(const std::string& name, float speed) -{ - this->type = IMAGE; - this->imagefile = name; - this->speed = speed; - - printf("seti %p\n", this); - delete image; - image = new Surface(datadir + "/images/background/" + name, false); -} - -void -Background::set_gradient(Color top, Color bottom) -{ - type = GRADIENT; - gradient_top = top; - gradient_bottom = bottom; - - delete image; - image = new Surface(top, bottom, screen->w, screen->h); -} - -void -Background::draw(DrawingContext& context) -{ - if(type == GRADIENT) { - context.push_transform(); - context.set_translation(Vector(0, 0)); - context.draw_surface(image, Vector(0, 0), layer); - context.pop_transform(); - } else if(type == IMAGE) { - if(!image) - return; - - int sx = int(-context.get_translation().x * speed) % image->w - image->w; - int sy = int(-context.get_translation().y * speed) % image->h - image->h; - context.push_transform(); - context.set_translation(Vector(0, 0)); - for(int x = sx; x < screen->w; x += image->w) - for(int y = sy; y < screen->h; y += image->h) - context.draw_surface(image, Vector(x, y), layer); - context.pop_transform(); - } -} - diff --git a/src/background.h b/src/background.h deleted file mode 100644 index 7ed3e837c..000000000 --- a/src/background.h +++ /dev/null @@ -1,71 +0,0 @@ -// $Id$ -// -// SuperTux - A Jump'n Run -// Copyright (C) 2004 Matthias Braun #include "badguy.h" -#include "camera.h" +#include "object/camera.h" static const float SQUISH_TIME = 2; static const float X_OFFSCREEN_DISTANCE = 1600; diff --git a/src/badguy/badguy.h b/src/badguy/badguy.h index c0904083c..68cdb57ed 100644 --- a/src/badguy/badguy.h +++ b/src/badguy/badguy.h @@ -7,7 +7,7 @@ #include "special/moving_object.h" #include "special/sprite.h" #include "math/physic.h" -#include "player.h" +#include "object/player.h" #include "serializable.h" #include "resources.h" #include "sector.h" diff --git a/src/camera.cpp b/src/camera.cpp deleted file mode 100644 index b2f47ed8f..000000000 --- a/src/camera.cpp +++ /dev/null @@ -1,291 +0,0 @@ -// $Id$ -// -// SuperTux - A Jump'n Run -// Copyright (C) 2004 Matthias Braun - -#include -#include -#include - -#include "camera.h" -#include "utils/lispreader.h" -#include "utils/lispwriter.h" -#include "player.h" -#include "tilemap.h" -#include "gameloop.h" -#include "app/globals.h" -#include "sector.h" - -using namespace SuperTux; - -Camera::Camera(Sector* newsector) - : sector(newsector), do_backscrolling(true), scrollchange(NONE), - auto_idx(0), auto_t(0) -{ - mode = NORMAL; -} - -Camera::~Camera() -{ -} - -const Vector& -Camera::get_translation() const -{ - return translation; -} - -void -Camera::parse(LispReader& reader) -{ - std::string modename; - - reader.read_string("mode", modename); - if(modename == "normal") { - mode = NORMAL; - - do_backscrolling = true; - reader.read_bool("backscrolling", do_backscrolling); - } else if(modename == "autoscroll") { - mode = AUTOSCROLL; - - lisp_object_t* cur = 0; - reader.read_lisp("path", cur); - if(cur == 0) { - throw std::runtime_error("No path specified in autoscroll camera."); - } - float speed = 50; - while(!lisp_nil_p(cur)) { - if(strcmp(lisp_symbol(lisp_car(lisp_car(cur))), "point") != 0) { - std::cerr << "Warning: unknown token in camera path.\n"; - continue; - } - - LispReader reader(lisp_cdr(lisp_car(cur))); - - ScrollPoint point; - 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); - point.speed = speed; - scrollpoints.push_back(point); - - cur = lisp_cdr(cur); - } - } else if(modename == "manual") { - mode = MANUAL; - } else { - std::stringstream str; - str << "invalid camera mode '" << modename << "'found in worldfile."; - throw std::runtime_error(str.str()); - } -} - -void -Camera::write(LispWriter& writer) -{ - writer.start_list("camera"); - - if(mode == NORMAL) { - writer.write_string("mode", "normal"); - writer.write_bool("backscrolling", do_backscrolling); - } else if(mode == AUTOSCROLL) { - writer.write_string("mode", "autoscroll"); - writer.start_list("path"); - for(std::vector::iterator i = scrollpoints.begin(); - i != scrollpoints.end(); ++i) { - writer.start_list("point"); - writer.write_float("x", i->position.x); - writer.write_float("y", i->position.y); - writer.write_float("speed", i->speed); - writer.end_list("point"); - } - - writer.end_list("path"); - } else if(mode == MANUAL) { - writer.write_string("mode", "manual"); - } - - 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 = 140; - -void -Camera::action(float elapsed_time) -{ - if(mode == NORMAL) - scroll_normal(elapsed_time); - else if(mode == AUTOSCROLL) - scroll_autoscroll(elapsed_time); -} - -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 > height - screen->h) - translation.y = height - screen->h; - if(translation.y < 0) - translation.y = 0; - if(translation.x > width - screen->w) - translation.x = width - screen->w; - if(translation.x < 0) - translation.x = 0; -} - -void -Camera::scroll_normal(float elapsed_time) -{ - assert(sector != 0); - Player* player = sector->player; - - // check that we don't have division by zero later - if(elapsed_time < EPSILON) - return; - - /****** Vertical Scrolling part ******/ - bool do_y_scrolling = true; - - if(player->dying || sector->solids->get_height() == 19) - do_y_scrolling = false; - - if(do_y_scrolling) { - // target_y is the high we target our scrolling at. This is not always the - // high of the player, but if he is jumping upwards we should use the - // position where he last touched the ground. (this probably needs - // exceptions for trampolines and similar things in the future) - float target_y; - if(player->fall_mode == Player::JUMPING) - target_y = player->last_ground_y + player->get_bbox().get_height(); - else - target_y = player->get_bbox().p2.y; - - // delta_y is the distance we'd have to travel to directly reach target_y - float delta_y = translation.y - (target_y - screen->h/2); - // speed is the speed the camera would need to reach target_y in this frame - float speed_y = delta_y / elapsed_time; - - // limit the camera speed when jumping upwards - if(player->fall_mode != Player::FALLING - && player->fall_mode != Player::TRAMPOLINE_JUMP) { - if(speed_y > max_speed_y) - speed_y = max_speed_y; - else if(speed_y < -max_speed_y) - speed_y = -max_speed_y; - } - - // finally scroll with calculated speed - translation.y -= speed_y * elapsed_time; - } - - /****** Horizontal scrolling part *******/ - - // our camera is either in leftscrolling, rightscrolling or nonscrollingmode. - - // when suddenly changing directions while scrolling into the other direction. - // abort scrolling, since tux might be going left/right at a relatively small - // part of the map (like when jumping upwards) - if((player->dir == ::LEFT && scrollchange == RIGHT) - || (player->dir == ::RIGHT && scrollchange == LEFT)) - scrollchange = NONE; - // when in left 1/3rd of screen scroll left - if(player->get_bbox().get_middle().x < translation.x + screen->w/3 - 16 - && do_backscrolling) - scrollchange = LEFT; - // scroll right when in right 1/3rd of screen - else if(player->get_bbox().get_middle().x > translation.x + screen->w/3*2+16) - scrollchange = RIGHT; - - // calculate our scroll target depending on scroll mode - float target_x; - if(scrollchange == LEFT) - target_x = player->get_bbox().get_middle().x - screen->w/3*2; - else if(scrollchange == RIGHT) - target_x = player->get_bbox().get_middle().x - screen->w/3; - else - target_x = translation.x; - - // that's the distance we would have to travel to reach target_x - float delta_x = translation.x - target_x; - // the speed we'd need to travel to reach target_x in this frame - float speed_x = delta_x / elapsed_time; - - // limit our speed - float maxv = 130 + (fabsf(player->physic.get_velocity_x() * 1.3)); - if(speed_x > maxv) - speed_x = maxv; - else if(speed_x < -maxv) - speed_x = -maxv; - - // apply scrolling - translation.x -= speed_x * elapsed_time; - - keep_in_bounds(); -} - -void -Camera::scroll_autoscroll(float elapsed_time) -{ - Player* player = sector->player; - - if(player->dying) - return; - - if(auto_t - elapsed_time >= 0) { - translation += current_dir * elapsed_time; - auto_t -= elapsed_time; - } else { - // do the rest of the old movement - translation += current_dir * auto_t; - elapsed_time -= auto_t; - auto_t = 0; - - // construct path for next point - if(auto_idx+1 >= scrollpoints.size()) { - keep_in_bounds(); - return; - } - Vector distance = scrollpoints[auto_idx+1].position - - scrollpoints[auto_idx].position; - current_dir = distance.unit() * scrollpoints[auto_idx].speed; - auto_t = distance.norm() / scrollpoints[auto_idx].speed; - - // do movement for the remaining time - translation += current_dir * elapsed_time; - auto_t -= elapsed_time; - auto_idx++; - } - - keep_in_bounds(); -} diff --git a/src/camera.h b/src/camera.h deleted file mode 100644 index 2294aa964..000000000 --- a/src/camera.h +++ /dev/null @@ -1,106 +0,0 @@ -// $Id$ -// -// SuperTux - A Jump'n Run -// Copyright (C) 2004 Matthias Braun -#include - -#include "defines.h" -#include "math/vector.h" -#include "special/game_object.h" -#include "video/drawing_context.h" -#include "serializable.h" - -using namespace SuperTux; - -namespace SuperTux { -class LispReader; -} - -class Sector; - -class Camera : public GameObject, public Serializable -{ -public: - Camera(Sector* sector); - virtual ~Camera(); - - /// parse camera mode from lisp file - void parse(LispReader& reader); - /// 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; - - virtual void action(float elapsed_time); - - virtual void draw(DrawingContext& ) - { - } - - void set_scrolling(int scroll_x, int scroll_y) - { - translation.x = scroll_x; - translation.y = scroll_y; - } - - enum CameraMode - { - NORMAL, AUTOSCROLL, MANUAL - }; - CameraMode mode; - -private: - void scroll_normal(float elapsed_time); - void scroll_autoscroll(float elapsed_time); - void keep_in_bounds(); - - enum LeftRightScrollChange - { - NONE, LEFT, RIGHT - }; - - Vector translation; - - Sector* sector; - - // normal mode - bool do_backscrolling; - LeftRightScrollChange scrollchange; - - // autoscroll mode - class ScrollPoint { - public: - Vector position; - float speed; - }; - std::vector scrollpoints; - size_t auto_idx; - float auto_t; - Vector current_dir; -}; - -#endif /*SUPERTUX_CAMERA_H*/ - diff --git a/src/gameloop.cpp b/src/gameloop.cpp index 2947f848c..a5933de17 100644 --- a/src/gameloop.cpp +++ b/src/gameloop.cpp @@ -47,19 +47,19 @@ #include "high_scores.h" #include "gui/menu.h" #include "sector.h" -#include "player.h" #include "level.h" #include "scene.h" #include "tile.h" -#include "particlesystem.h" +#include "object/particlesystem.h" +#include "object/background.h" +#include "object/tilemap.h" +#include "object/camera.h" +#include "object/player.h" #include "resources.h" -#include "background.h" -#include "tilemap.h" #include "app/gettext.h" #include "worldmap.h" #include "intro.h" #include "misc.h" -#include "camera.h" #include "statistics.h" #include "timer.h" #include "object/fireworks.h" diff --git a/src/gameobjs.cpp b/src/gameobjs.cpp deleted file mode 100644 index 3201eb285..000000000 --- a/src/gameobjs.cpp +++ /dev/null @@ -1,545 +0,0 @@ -// $Id$ -// -// SuperTux -// Copyright (C) 2000 Bill Kendrick -// Copyright (C) 2004 Tobias Glaesser -// -// 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 "app/globals.h" -#include "tile.h" -#include "tile_manager.h" -#include "gameloop.h" -#include "gameobjs.h" -#include "special/sprite_manager.h" -#include "resources.h" -#include "sector.h" -#include "tilemap.h" -#include "video/drawing_context.h" -#include "camera.h" - -BouncyCoin::BouncyCoin(const Vector& pos) - : position(pos) -{ - timer.start(.3); - sprite = sprite_manager->create("coin"); - sprite->set_action("still"); -} - -BouncyCoin::~BouncyCoin() -{ - delete sprite; -} - -void -BouncyCoin::action(float elapsed_time) -{ - position.y += -200 * elapsed_time; - - if(timer.check()) - remove_me(); -} - -void -BouncyCoin::draw(DrawingContext& context) -{ - sprite->draw(context, position, LAYER_OBJECTS); -} - -//--------------------------------------------------------------------------- - -BrokenBrick::BrokenBrick(Sprite* nsprite, - const Vector& pos, const Vector& nmovement) - : sprite(new Sprite(*nsprite)), position(pos), movement(nmovement) -{ - timer.start(.2); -} - -BrokenBrick::~BrokenBrick() -{ - delete sprite; -} - -void -BrokenBrick::action(float elapsed_time) -{ - position += movement * elapsed_time; - - if (timer.check()) - remove_me(); -} - -void -BrokenBrick::draw(DrawingContext& context) -{ - sprite->draw_part(context, - Vector(rand() % 16, rand() % 16), Vector(16, 16), - position, LAYER_OBJECTS + 1); -} - -//--------------------------------------------------------------------------- - -FloatingText::FloatingText(const Vector& pos, const std::string& text_) - : position(pos), text(text_) -{ - timer.start(.1); - position.x -= text.size() * 8; -} - -FloatingText::FloatingText(const Vector& pos, int score) - : position(pos) -{ - timer.start(.1); - - // turn int into a string - char str[10]; - snprintf(str, 10, "%d", score); - text = str; - - position.x -= text.size() * 8; -} - -void -FloatingText::action(float elapsed_time) -{ - position.y -= 1.4 * elapsed_time; - - if(timer.check()) - remove_me(); -} - -#define FADING_TIME .350 - -void -FloatingText::draw(DrawingContext& context) -{ - // make an alpha animation when disapearing - int alpha; - if(timer.get_timeleft() < FADING_TIME) - alpha = int(timer.get_timeleft() * 255 / FADING_TIME); - else - alpha = 255; - - context.push_transform(); - context.set_alpha(alpha); - - context.draw_text(gold_text, text, position, LEFT_ALLIGN, LAYER_OBJECTS+1); - - context.pop_transform(); -} - -/* Trampoline */ - -#if 0 -Sprite *img_trampoline; - -Trampoline::Trampoline(LispReader& reader) -{ - 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); - - frame = 0; - mode = M_NORMAL; - physic.reset(); -} - -Trampoline::Trampoline(float x, float y) -{ - base.x = x; - base.y = y; - base.width = 32; - base.height = 32; - power = 7.5; - - frame = 0; - mode = M_NORMAL; - physic.reset(); -} - -void -Trampoline::write(LispWriter& writer) -{ - writer.start_list("trampoline"); - - writer.write_float("x", base.x); - writer.write_float("y", base.y); - writer.write_float("power", power); - - writer.end_list("trampoline"); -} - -void -Trampoline::draw(DrawingContext& context) -{ - img_trampoline->set_frame(frame); - img_trampoline->draw(context, base, LAYER_OBJECTS); - frame = 0; -} - -void -Trampoline::action(float frame_ratio) -{ - // TODO: Remove if we're too far off the screen - - // Falling - if (mode != M_HELD) - { - if (issolid(base.x + base.width/2, base.y + base.height)) - { - base.y = int((base.y + base.height)/32) * 32 - base.height; - - physic.enable_gravity(false); - physic.set_velocity_y(0.0f); - - physic.set_velocity_x(0); - } - else - { - physic.enable_gravity(true); - } - } - else // Player is carrying us around - { - /* FIXME: The trampoline object shouldn't know about pplayer objects. */ - /* If we're holding the iceblock */ - Player& tux = *Sector::current()->player; - Direction dir = tux.dir; - - if(dir == RIGHT) - { - base.x = tux.base.x + 16; - base.y = tux.base.y + tux.base.height/1.5 - base.height; - } - else /* facing left */ - { - base.x = tux.base.x - 16; - base.y = tux.base.y + tux.base.height/1.5 - base.height; - } - - if(collision_object_map(base)) - { - base.x = tux.base.x; - base.y = tux.base.y + tux.base.height/1.5 - base.height; - } - } - - physic.apply(frame_ratio, base.x, base.y, Sector::current()->gravity); - collision_swept_object_map(&old_base, &base); -} - -void -Trampoline::collision(const MovingObject&, int) -{ - // comes later -} - -void -Trampoline::collision(void *p_c_object, int c_object, CollisionType type) -{ - Player* pplayer_c = NULL; - switch (c_object) - { - case CO_PLAYER: - pplayer_c = (Player*) p_c_object; - - if (type == COLLISION_NORMAL) - { - // Pick up if HELD (done in Player) - } - - else if (type == COLLISION_SQUISH) - { - int squish_amount = (32 - (int)pplayer_c->base.y % 32); - - if (squish_amount < 24) - frame = 3; - else if (squish_amount < 28) - frame = 2; - else if (squish_amount < 30) - frame = 1; - else - frame = 0; - - if (squish_amount < 20) { - pplayer_c->physic.set_velocity_y(power); - pplayer_c->fall_mode = Player::TRAMPOLINE_JUMP; - } - else if (pplayer_c->physic.get_velocity_y() < 0) - pplayer_c->physic.set_velocity_y(-squish_amount/32); - } - - break; - - default: - break; - - } -} -#endif - -/* Flying Platform */ - -#if 0 -Sprite *img_flying_platform; - -FlyingPlatform::FlyingPlatform(LispReader& reader) -{ - reader.read_int_vector("x", pos_x); - reader.read_int_vector("y", pos_y); - - velocity = 2.0; - reader.read_float("velocity", velocity); - - base.x = pos_x[0]; - base.y = pos_y[0]; - base.width = 96; - base.height = 40; - - point = 0; - move = false; - - float x = pos_x[point+1] - pos_x[point]; - float y = pos_y[point+1] - pos_y[point]; - vel_x = x*velocity / sqrt(x*x + y*y); - vel_y = -(velocity - vel_x); - - frame = 0; -} - -FlyingPlatform::FlyingPlatform(int x, int y) -{ -base.x = x; -base.y = y; -point = 0; -move = false; -} - -void -FlyingPlatform::write(LispWriter& writer) -{ - writer.start_list("flying-trampoline"); - - writer.write_int_vector("x", pos_x); - writer.write_int_vector("y", pos_y); - writer.write_float("velocity", velocity); - - writer.end_list("flying-trampoline"); -} - -void -FlyingPlatform::draw(DrawingContext& context) -{ - img_flying_platform->draw(context, base, LAYER_OBJECTS); -} - -void -FlyingPlatform::action(float frame_ratio) -{ - // TODO: Remove if we're too far off the screen - -if(!move) - return; - -if((unsigned)point+1 != pos_x.size()) - { - if(((pos_x[point+1] > pos_x[point] && base.x >= pos_x[point+1]) || - (pos_x[point+1] < pos_x[point] && base.x <= pos_x[point+1]) || - pos_x[point] == pos_x[point+1]) && - ((pos_y[point+1] > pos_y[point] && base.y >= pos_y[point+1]) || - (pos_y[point+1] < pos_y[point] && base.y <= pos_y[point+1]) || - pos_y[point] == pos_y[point+1])) - { - point++; - - float x = pos_x[point+1] - pos_x[point]; - float y = pos_y[point+1] - pos_y[point]; - vel_x = x*velocity / sqrt(x*x + y*y); - vel_y = -(velocity - vel_x); - } - } -else // last point - { - // point = 0; - // reverse vector - return; - } -/* -if(pos_x[point+1] > base.x) - base.x += velocity * frame_ratio; -else if(pos_x[point+1] < base.x) - base.x -= velocity * frame_ratio; - -if(pos_y[point+1] > base.y) - base.y += velocity * frame_ratio; -else if(pos_y[point+1] < base.y) - base.y -= velocity * frame_ratio; -*/ - -base.x += vel_x * frame_ratio; -base.y += vel_y * frame_ratio; -} - -void -FlyingPlatform::collision(const MovingObject&, int) -{ - // comes later -} - -void -FlyingPlatform::collision(void *p_c_object, int c_object, CollisionType type) -{ -(void) p_c_object; -(void) type; - -// Player* pplayer_c = NULL; - switch (c_object) - { - case CO_PLAYER: -// pplayer_c = (Player*) p_c_object; - move = true; - - break; - - default: - break; - - } -} -#endif - -Sprite *img_smoke_cloud = 0; - -SmokeCloud::SmokeCloud(const Vector& pos) - : position(pos) -{ - timer.start(.3); -} - -void -SmokeCloud::action(float elapsed_time) -{ - position.y -= 120 * elapsed_time; - - if(timer.check()) - remove_me(); -} - -void -SmokeCloud::draw(DrawingContext& context) -{ - img_smoke_cloud->draw(context, position, LAYER_OBJECTS+1); -} - -Particles::Particles(const Vector& epicenter, int min_angle, int max_angle, - const Vector& initial_velocity, const Vector& acceleration, int number, - Color color_, int size_, float life_time, int drawing_layer_) - : accel(acceleration), color(color_), size(size_), drawing_layer(drawing_layer_) -{ - if(life_time == 0) { - live_forever = true; - } else { - live_forever = false; - timer.start(life_time); - } - - // create particles - for(int p = 0; p < number; p++) - { - Particle* particle = new Particle; - particle->pos = epicenter; - - float angle = ((rand() % (max_angle-min_angle))+min_angle) - * (M_PI / 180); // convert to radius - particle->vel.x = /*fabs*/(sin(angle)) * initial_velocity.x; -// if(angle >= M_PI && angle < M_PI*2) -// particle->vel.x *= -1; // work around to fix signal - particle->vel.y = /*fabs*/(cos(angle)) * initial_velocity.y; -// if(angle >= M_PI_2 && angle < 3*M_PI_2) -// particle->vel.y *= -1; - - particles.push_back(particle); - } -} - -Particles::~Particles() -{ - // free particles - for(std::vector::iterator i = particles.begin(); - i < particles.end(); i++) - delete (*i); -} - -void -Particles::action(float elapsed_time) -{ - Vector camera = Sector::current()->camera->get_translation(); - - // update particles - for(std::vector::iterator i = particles.begin(); - i != particles.end(); ) { - (*i)->pos.x += (*i)->vel.x * elapsed_time; - (*i)->pos.y += (*i)->vel.y * elapsed_time; - - (*i)->vel.x += accel.x * elapsed_time; - (*i)->vel.y += accel.y * elapsed_time; - - if((*i)->pos.x < camera.x || (*i)->pos.x > screen->w + camera.x || - (*i)->pos.y < camera.y || (*i)->pos.y > screen->h + camera.y) { - delete (*i); - i = particles.erase(i); - } else { - ++i; - } - } - - if((timer.check() && !live_forever) || particles.size() == 0) - remove_me(); -} - -void -Particles::draw(DrawingContext& context) -{ - // draw particles - for(std::vector::iterator i = particles.begin(); - i != particles.end(); i++) { - context.draw_filled_rect((*i)->pos, Vector(size,size), color,drawing_layer); - } -} - -void load_object_gfx() -{ -#if 0 - img_trampoline = sprite_manager->load("trampoline"); - img_trampoline->start_animation(0); - img_flying_platform = sprite_manager->load("flying_platform"); -#endif - img_smoke_cloud = sprite_manager->create("stomp"); -} - -void free_object_gfx() -{ - delete img_smoke_cloud; -} - diff --git a/src/gameobjs.h b/src/gameobjs.h deleted file mode 100644 index 3d928be0e..000000000 --- a/src/gameobjs.h +++ /dev/null @@ -1,161 +0,0 @@ -// $Id$ -// -// SuperTux -// Copyright (C) 2000 Bill Kendrick -// Copyright (C) 2004 Tobias Glaesser -// -// 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_GAMEOBJS_H -#define SUPERTUX_GAMEOBJS_H - -#include "video/surface.h" -#include "timer.h" -#include "scene.h" -#include "math/physic.h" -#include "special/game_object.h" -#include "special/moving_object.h" -#include "serializable.h" -#include "utils/lispwriter.h" - -/* Bounciness of distros: */ -#define NO_BOUNCE 0 -#define BOUNCE 1 - -namespace SuperTux { -class Sprite; -} - -class BouncyCoin : public GameObject -{ -public: - BouncyCoin(const Vector& pos); - ~BouncyCoin(); - virtual void action(float elapsed_time); - virtual void draw(DrawingContext& context); - -private: - Sprite* sprite; - Vector position; - Timer2 timer; -}; - -class BrokenBrick : public GameObject -{ -public: - BrokenBrick(Sprite* sprite, const Vector& pos, const Vector& movement); - ~BrokenBrick(); - - virtual void action(float elapsed_time); - virtual void draw(DrawingContext& context); - -private: - Timer2 timer; - Sprite* sprite; - Vector position; - Vector movement; -}; - -class FloatingText : public GameObject -{ -public: - FloatingText(const Vector& pos, const std::string& text_); - FloatingText(const Vector& pos, int s); // use this for score, for instance - - virtual void action(float elapsed_time); - virtual void draw(DrawingContext& context); - -private: - Vector position; - std::string text; - Timer2 timer; -}; - -#if 0 -extern Sprite *img_trampoline; - -class Trampoline : public MovingObject, public Serializable -{ -public: - Trampoline(LispReader& reader); - Trampoline(float x, float y); - - virtual void write(LispWriter& writer); - virtual void action(float frame_ratio); - virtual void draw(DrawingContext& context); - - virtual void collision(const MovingObject& other, int); - void collision(void *p_c_object, int c_object, CollisionType type); - - Physic physic; - enum { M_NORMAL, M_HELD } mode; - - private: - float power; - unsigned int frame; -}; -#endif - -extern Sprite *img_smoke_cloud; - -class SmokeCloud : public GameObject -{ -public: - SmokeCloud(const Vector& pos); - - virtual void action(float elapsed_time); - virtual void draw(DrawingContext& context); - -private: - Timer2 timer; - Vector position; -}; - -class Particles : public GameObject -{ -public: - Particles(const Vector& epicenter, int min_angle, int max_angle, - const Vector& initial_velocity, const Vector& acceleration, - int number, Color color, int size, float life_time, int drawing_layer); - ~Particles(); - - virtual void action(float elapsed_time); - virtual void draw(DrawingContext& context); - -private: - Vector accel; - Timer2 timer; - bool live_forever; - - Color color; - float size; - int drawing_layer; - - struct Particle { - Vector pos, vel; -// float angle; - }; - std::vector particles; -}; - -void load_object_gfx(); -void free_object_gfx(); - -#endif - -/* Local Variables: */ -/* mode:c++ */ -/* End: */ diff --git a/src/level.cpp b/src/level.cpp index 88f006389..6960828e6 100644 --- a/src/level.cpp +++ b/src/level.cpp @@ -30,7 +30,6 @@ #include "app/globals.h" #include "app/setup.h" -#include "camera.h" #include "video/screen.h" #include "level.h" #include "math/physic.h" @@ -39,9 +38,10 @@ #include "tile.h" #include "utils/lispreader.h" #include "resources.h" -#include "gameobjs.h" #include "utils/lispwriter.h" -#include "tilemap.h" +#include "object/gameobjs.h" +#include "object/camera.h" +#include "object/tilemap.h" using namespace std; @@ -52,16 +52,6 @@ Level::Level() } void -Level::create(const std::string& filename) -{ - Level level; - const size_t width = 25; - const size_t height = 19; - level.add_sector(Sector::create("main", width, height)); - level.save(filename); -} - -void Level::load(const std::string& filepath) { LispReader* level = LispReader::load(filepath, "supertux-level"); @@ -167,8 +157,10 @@ Level::~Level() void Level::do_vertical_flip() { +#if 0 for(Sectors::iterator i = sectors.begin(); i != sectors.end(); ++i) i->second->do_vertical_flip(); +#endif } void @@ -242,10 +234,7 @@ Level::get_total_coins() int total_coins = 0; for(Sectors::iterator i = sectors.begin(); i != sectors.end(); ++i) { TileMap* solids = i->second->solids; - if(!solids) { - std::cerr << "Sector '" << i->first << "' contains no solids!?!\n"; - continue; - } + assert(solids != 0); for(size_t x = 0; x < solids->get_width(); ++x) for(size_t y = 0; y < solids->get_height(); ++y) { const Tile* tile = solids->get_tile(x, y); diff --git a/src/level.h b/src/level.h index d627d8245..a26435355 100644 --- a/src/level.h +++ b/src/level.h @@ -54,7 +54,6 @@ public: // loads a levelfile void load(const std::string& filename); void save(const std::string& filename); - static void create(const std::string& filename); EndSequenceType get_end_sequence_type() const { return end_sequence_type; } diff --git a/src/leveleditor.cpp b/src/leveleditor.cpp index c9b232010..d6d8a2ecc 100644 --- a/src/leveleditor.cpp +++ b/src/leveleditor.cpp @@ -31,13 +31,13 @@ #include "leveleditor.h" #include "resources.h" #include "tile.h" -#include "tilemap.h" #include "tile_manager.h" #include "sector.h" -#include "background.h" #include "gameloop.h" -#include "gameobjs.h" -#include "camera.h" +#include "object/gameobjs.h" +#include "object/camera.h" +#include "object/tilemap.h" +#include "object/background.h" LevelEditor::LevelEditor() { @@ -288,7 +288,9 @@ while(SDL_PollEvent(&event)) level_subset->description = create_subset_menu->get_item_by_id(MN_ID_DESCRIPTION_SUBSET).input; //FIXME: generate better level filenames level_subset->add_level(subset_name+'/'+"new_level.stl"); - Level::create(level_subset->get_level_filename(0)); + Level* newlevel = new Level(); + newlevel->add_sector(create_sector("main", 25, 19)); + newlevel->save(level_subset->get_level_filename(0)); level_subset->save(); load_level(0); @@ -368,7 +370,9 @@ while(SDL_PollEvent(&event)) if(confirm_dialog(NULL, str)) { level_subset->add_level("new_level.stl"); - Level::create(level_subset->get_level_filename(level_nb + 1)); + Level* newlevel = new Level(); + newlevel->add_sector(create_sector("main", 25, 19)); + newlevel->save(level_subset->get_level_filename(level_nb + 1)); level_subset->save(); load_level(level_nb + 1); } @@ -780,7 +784,7 @@ if(sector_ == NULL) { if(!confirm_dialog(NULL, _("No more sectors exist. Create another?"))) return; - sector_ = Sector::create("new_sector",25,19); + sector_ = create_sector("new_sector",25,19); level->add_sector(sector_); } @@ -1033,3 +1037,19 @@ for(unsigned int i = 0; i < sizeof(text) / sizeof(text[0]); i++) show_grid = show_grid_t; mouse_cursor->set_state(MC_NORMAL); } + +Sector* +LevelEditor::create_sector(const std::string& name, size_t width, size_t height) +{ + Sector* sector = new Sector; + sector->set_name(name); + + sector->add_object(new TileMap(LAYER_BACKGROUNDTILES, false, width, height)); + sector->add_object(new TileMap(LAYER_TILES, true, width, height)); + sector->add_object(new TileMap(LAYER_FOREGROUNDTILES, false, width, height)); + sector->add_object(new Camera(sector)); + sector->update_game_objects(); + + return sector; +} + diff --git a/src/leveleditor.h b/src/leveleditor.h index 32099a202..5867d88e6 100644 --- a/src/leveleditor.h +++ b/src/leveleditor.h @@ -143,6 +143,9 @@ private: Vector selection_ini, selection_end; bool level_changed; + +private: + Sector* create_sector(const std::string& name, size_t width, size_t height); }; #endif diff --git a/src/misc.h b/src/misc.h index e74a85450..bef88781b 100644 --- a/src/misc.h +++ b/src/misc.h @@ -19,14 +19,14 @@ #define SUPERTUX_MISC_H #include "app/setup.h" -#include "resources.h" +#include "app/gettext.h" #include "gui/menu.h" #include "utils/configfile.h" -#include "player.h" #include "title.h" +#include "resources.h" #include "worldmap.h" #include "gameloop.h" -#include "app/gettext.h" +#include "object/player.h" class MyConfig : public Config { diff --git a/src/object/background.cpp b/src/object/background.cpp new file mode 100644 index 000000000..2d6cc4121 --- /dev/null +++ b/src/object/background.cpp @@ -0,0 +1,129 @@ +// $Id$ +// +// SuperTux - A Jump'n Run +// Copyright (C) 2004 Matthias Braun + +#include "background.h" +#include "app/globals.h" +#include "camera.h" +#include "video/drawing_context.h" +#include "utils/lispwriter.h" + +Background::Background() + : type(INVALID), layer(LAYER_BACKGROUND0), image(0) +{ +} + +Background::Background(LispReader& reader) + : type(INVALID), layer(LAYER_BACKGROUND0), image(0) +{ + reader.read_int("layer", layer); + if(reader.read_string("image", imagefile) + && reader.read_float("speed", speed)) { + set_image(imagefile, speed); + } + + std::vector bkgd_top_color, bkgd_bottom_color; + if(reader.read_int_vector("top_color", bkgd_top_color) && + reader.read_int_vector("bottom_color", bkgd_bottom_color)) + set_gradient(Color(bkgd_top_color), Color(bkgd_bottom_color)); +} + +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) { + std::vector bkgd_top_color, bkgd_bottom_color; + bkgd_top_color.push_back(gradient_top.red); + bkgd_top_color.push_back(gradient_top.green); + bkgd_top_color.push_back(gradient_top.blue); + bkgd_bottom_color.push_back(gradient_top.red); + bkgd_bottom_color.push_back(gradient_top.green); + bkgd_bottom_color.push_back(gradient_top.blue); + writer.write_int_vector("top_color", bkgd_top_color); + writer.write_int_vector("bottom_color", bkgd_bottom_color); + } + writer.write_int("layer", layer); + + writer.end_list("background"); +} + +void +Background::action(float) +{ +} + +void +Background::set_image(const std::string& name, float speed) +{ + this->type = IMAGE; + this->imagefile = name; + this->speed = speed; + + delete image; + image = new Surface(datadir + "/images/background/" + name, false); +} + +void +Background::set_gradient(Color top, Color bottom) +{ + type = GRADIENT; + gradient_top = top; + gradient_bottom = bottom; + + delete image; + image = new Surface(top, bottom, screen->w, screen->h); +} + +void +Background::draw(DrawingContext& context) +{ + if(type == GRADIENT) { + context.push_transform(); + context.set_translation(Vector(0, 0)); + context.draw_surface(image, Vector(0, 0), layer); + context.pop_transform(); + } else if(type == IMAGE) { + if(!image) + return; + + int sx = int(-context.get_translation().x * speed) % image->w - image->w; + int sy = int(-context.get_translation().y * speed) % image->h - image->h; + context.push_transform(); + context.set_translation(Vector(0, 0)); + for(int x = sx; x < screen->w; x += image->w) + for(int y = sy; y < screen->h; y += image->h) + context.draw_surface(image, Vector(x, y), layer); + context.pop_transform(); + } +} + diff --git a/src/object/background.h b/src/object/background.h new file mode 100644 index 000000000..7ed3e837c --- /dev/null +++ b/src/object/background.h @@ -0,0 +1,71 @@ +// $Id$ +// +// SuperTux - A Jump'n Run +// Copyright (C) 2004 Matthias Braun + +#include +#include +#include + +#include "camera.h" +#include "utils/lispreader.h" +#include "utils/lispwriter.h" +#include "player.h" +#include "tilemap.h" +#include "gameloop.h" +#include "app/globals.h" +#include "sector.h" + +using namespace SuperTux; + +Camera::Camera(Sector* newsector) + : sector(newsector), do_backscrolling(true), scrollchange(NONE), + auto_idx(0), auto_t(0) +{ + mode = NORMAL; +} + +Camera::~Camera() +{ +} + +const Vector& +Camera::get_translation() const +{ + return translation; +} + +void +Camera::parse(LispReader& reader) +{ + std::string modename; + + reader.read_string("mode", modename); + if(modename == "normal") { + mode = NORMAL; + + do_backscrolling = true; + reader.read_bool("backscrolling", do_backscrolling); + } else if(modename == "autoscroll") { + mode = AUTOSCROLL; + + lisp_object_t* cur = 0; + reader.read_lisp("path", cur); + if(cur == 0) { + throw std::runtime_error("No path specified in autoscroll camera."); + } + float speed = 50; + while(!lisp_nil_p(cur)) { + if(strcmp(lisp_symbol(lisp_car(lisp_car(cur))), "point") != 0) { + std::cerr << "Warning: unknown token in camera path.\n"; + continue; + } + + LispReader reader(lisp_cdr(lisp_car(cur))); + + ScrollPoint point; + 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); + point.speed = speed; + scrollpoints.push_back(point); + + cur = lisp_cdr(cur); + } + } else if(modename == "manual") { + mode = MANUAL; + } else { + std::stringstream str; + str << "invalid camera mode '" << modename << "'found in worldfile."; + throw std::runtime_error(str.str()); + } +} + +void +Camera::write(LispWriter& writer) +{ + writer.start_list("camera"); + + if(mode == NORMAL) { + writer.write_string("mode", "normal"); + writer.write_bool("backscrolling", do_backscrolling); + } else if(mode == AUTOSCROLL) { + writer.write_string("mode", "autoscroll"); + writer.start_list("path"); + for(std::vector::iterator i = scrollpoints.begin(); + i != scrollpoints.end(); ++i) { + writer.start_list("point"); + writer.write_float("x", i->position.x); + writer.write_float("y", i->position.y); + writer.write_float("speed", i->speed); + writer.end_list("point"); + } + + writer.end_list("path"); + } else if(mode == MANUAL) { + writer.write_string("mode", "manual"); + } + + 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 = 140; + +void +Camera::action(float elapsed_time) +{ + if(mode == NORMAL) + scroll_normal(elapsed_time); + else if(mode == AUTOSCROLL) + scroll_autoscroll(elapsed_time); +} + +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 > height - screen->h) + translation.y = height - screen->h; + if(translation.y < 0) + translation.y = 0; + if(translation.x > width - screen->w) + translation.x = width - screen->w; + if(translation.x < 0) + translation.x = 0; +} + +void +Camera::scroll_normal(float elapsed_time) +{ + assert(sector != 0); + Player* player = sector->player; + + // check that we don't have division by zero later + if(elapsed_time < EPSILON) + return; + + /****** Vertical Scrolling part ******/ + bool do_y_scrolling = true; + + if(player->dying || sector->solids->get_height() == 19) + do_y_scrolling = false; + + if(do_y_scrolling) { + // target_y is the high we target our scrolling at. This is not always the + // high of the player, but if he is jumping upwards we should use the + // position where he last touched the ground. (this probably needs + // exceptions for trampolines and similar things in the future) + float target_y; + if(player->fall_mode == Player::JUMPING) + target_y = player->last_ground_y + player->get_bbox().get_height(); + else + target_y = player->get_bbox().p2.y; + + // delta_y is the distance we'd have to travel to directly reach target_y + float delta_y = translation.y - (target_y - screen->h/2); + // speed is the speed the camera would need to reach target_y in this frame + float speed_y = delta_y / elapsed_time; + + // limit the camera speed when jumping upwards + if(player->fall_mode != Player::FALLING + && player->fall_mode != Player::TRAMPOLINE_JUMP) { + if(speed_y > max_speed_y) + speed_y = max_speed_y; + else if(speed_y < -max_speed_y) + speed_y = -max_speed_y; + } + + // finally scroll with calculated speed + translation.y -= speed_y * elapsed_time; + } + + /****** Horizontal scrolling part *******/ + + // our camera is either in leftscrolling, rightscrolling or nonscrollingmode. + + // when suddenly changing directions while scrolling into the other direction. + // abort scrolling, since tux might be going left/right at a relatively small + // part of the map (like when jumping upwards) + if((player->dir == ::LEFT && scrollchange == RIGHT) + || (player->dir == ::RIGHT && scrollchange == LEFT)) + scrollchange = NONE; + // when in left 1/3rd of screen scroll left + if(player->get_bbox().get_middle().x < translation.x + screen->w/3 - 16 + && do_backscrolling) + scrollchange = LEFT; + // scroll right when in right 1/3rd of screen + else if(player->get_bbox().get_middle().x > translation.x + screen->w/3*2+16) + scrollchange = RIGHT; + + // calculate our scroll target depending on scroll mode + float target_x; + if(scrollchange == LEFT) + target_x = player->get_bbox().get_middle().x - screen->w/3*2; + else if(scrollchange == RIGHT) + target_x = player->get_bbox().get_middle().x - screen->w/3; + else + target_x = translation.x; + + // that's the distance we would have to travel to reach target_x + float delta_x = translation.x - target_x; + // the speed we'd need to travel to reach target_x in this frame + float speed_x = delta_x / elapsed_time; + + // limit our speed + float maxv = 130 + (fabsf(player->physic.get_velocity_x() * 1.3)); + if(speed_x > maxv) + speed_x = maxv; + else if(speed_x < -maxv) + speed_x = -maxv; + + // apply scrolling + translation.x -= speed_x * elapsed_time; + + keep_in_bounds(); +} + +void +Camera::scroll_autoscroll(float elapsed_time) +{ + Player* player = sector->player; + + if(player->dying) + return; + + if(auto_t - elapsed_time >= 0) { + translation += current_dir * elapsed_time; + auto_t -= elapsed_time; + } else { + // do the rest of the old movement + translation += current_dir * auto_t; + elapsed_time -= auto_t; + auto_t = 0; + + // construct path for next point + if(auto_idx+1 >= scrollpoints.size()) { + keep_in_bounds(); + return; + } + Vector distance = scrollpoints[auto_idx+1].position + - scrollpoints[auto_idx].position; + current_dir = distance.unit() * scrollpoints[auto_idx].speed; + auto_t = distance.norm() / scrollpoints[auto_idx].speed; + + // do movement for the remaining time + translation += current_dir * elapsed_time; + auto_t -= elapsed_time; + auto_idx++; + } + + keep_in_bounds(); +} diff --git a/src/object/camera.h b/src/object/camera.h new file mode 100644 index 000000000..2294aa964 --- /dev/null +++ b/src/object/camera.h @@ -0,0 +1,106 @@ +// $Id$ +// +// SuperTux - A Jump'n Run +// Copyright (C) 2004 Matthias Braun +#include + +#include "defines.h" +#include "math/vector.h" +#include "special/game_object.h" +#include "video/drawing_context.h" +#include "serializable.h" + +using namespace SuperTux; + +namespace SuperTux { +class LispReader; +} + +class Sector; + +class Camera : public GameObject, public Serializable +{ +public: + Camera(Sector* sector); + virtual ~Camera(); + + /// parse camera mode from lisp file + void parse(LispReader& reader); + /// 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; + + virtual void action(float elapsed_time); + + virtual void draw(DrawingContext& ) + { + } + + void set_scrolling(int scroll_x, int scroll_y) + { + translation.x = scroll_x; + translation.y = scroll_y; + } + + enum CameraMode + { + NORMAL, AUTOSCROLL, MANUAL + }; + CameraMode mode; + +private: + void scroll_normal(float elapsed_time); + void scroll_autoscroll(float elapsed_time); + void keep_in_bounds(); + + enum LeftRightScrollChange + { + NONE, LEFT, RIGHT + }; + + Vector translation; + + Sector* sector; + + // normal mode + bool do_backscrolling; + LeftRightScrollChange scrollchange; + + // autoscroll mode + class ScrollPoint { + public: + Vector position; + float speed; + }; + std::vector scrollpoints; + size_t auto_idx; + float auto_t; + Vector current_dir; +}; + +#endif /*SUPERTUX_CAMERA_H*/ + diff --git a/src/object/gameobjs.cpp b/src/object/gameobjs.cpp new file mode 100644 index 000000000..3201eb285 --- /dev/null +++ b/src/object/gameobjs.cpp @@ -0,0 +1,545 @@ +// $Id$ +// +// SuperTux +// Copyright (C) 2000 Bill Kendrick +// Copyright (C) 2004 Tobias Glaesser +// +// 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 "app/globals.h" +#include "tile.h" +#include "tile_manager.h" +#include "gameloop.h" +#include "gameobjs.h" +#include "special/sprite_manager.h" +#include "resources.h" +#include "sector.h" +#include "tilemap.h" +#include "video/drawing_context.h" +#include "camera.h" + +BouncyCoin::BouncyCoin(const Vector& pos) + : position(pos) +{ + timer.start(.3); + sprite = sprite_manager->create("coin"); + sprite->set_action("still"); +} + +BouncyCoin::~BouncyCoin() +{ + delete sprite; +} + +void +BouncyCoin::action(float elapsed_time) +{ + position.y += -200 * elapsed_time; + + if(timer.check()) + remove_me(); +} + +void +BouncyCoin::draw(DrawingContext& context) +{ + sprite->draw(context, position, LAYER_OBJECTS); +} + +//--------------------------------------------------------------------------- + +BrokenBrick::BrokenBrick(Sprite* nsprite, + const Vector& pos, const Vector& nmovement) + : sprite(new Sprite(*nsprite)), position(pos), movement(nmovement) +{ + timer.start(.2); +} + +BrokenBrick::~BrokenBrick() +{ + delete sprite; +} + +void +BrokenBrick::action(float elapsed_time) +{ + position += movement * elapsed_time; + + if (timer.check()) + remove_me(); +} + +void +BrokenBrick::draw(DrawingContext& context) +{ + sprite->draw_part(context, + Vector(rand() % 16, rand() % 16), Vector(16, 16), + position, LAYER_OBJECTS + 1); +} + +//--------------------------------------------------------------------------- + +FloatingText::FloatingText(const Vector& pos, const std::string& text_) + : position(pos), text(text_) +{ + timer.start(.1); + position.x -= text.size() * 8; +} + +FloatingText::FloatingText(const Vector& pos, int score) + : position(pos) +{ + timer.start(.1); + + // turn int into a string + char str[10]; + snprintf(str, 10, "%d", score); + text = str; + + position.x -= text.size() * 8; +} + +void +FloatingText::action(float elapsed_time) +{ + position.y -= 1.4 * elapsed_time; + + if(timer.check()) + remove_me(); +} + +#define FADING_TIME .350 + +void +FloatingText::draw(DrawingContext& context) +{ + // make an alpha animation when disapearing + int alpha; + if(timer.get_timeleft() < FADING_TIME) + alpha = int(timer.get_timeleft() * 255 / FADING_TIME); + else + alpha = 255; + + context.push_transform(); + context.set_alpha(alpha); + + context.draw_text(gold_text, text, position, LEFT_ALLIGN, LAYER_OBJECTS+1); + + context.pop_transform(); +} + +/* Trampoline */ + +#if 0 +Sprite *img_trampoline; + +Trampoline::Trampoline(LispReader& reader) +{ + 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); + + frame = 0; + mode = M_NORMAL; + physic.reset(); +} + +Trampoline::Trampoline(float x, float y) +{ + base.x = x; + base.y = y; + base.width = 32; + base.height = 32; + power = 7.5; + + frame = 0; + mode = M_NORMAL; + physic.reset(); +} + +void +Trampoline::write(LispWriter& writer) +{ + writer.start_list("trampoline"); + + writer.write_float("x", base.x); + writer.write_float("y", base.y); + writer.write_float("power", power); + + writer.end_list("trampoline"); +} + +void +Trampoline::draw(DrawingContext& context) +{ + img_trampoline->set_frame(frame); + img_trampoline->draw(context, base, LAYER_OBJECTS); + frame = 0; +} + +void +Trampoline::action(float frame_ratio) +{ + // TODO: Remove if we're too far off the screen + + // Falling + if (mode != M_HELD) + { + if (issolid(base.x + base.width/2, base.y + base.height)) + { + base.y = int((base.y + base.height)/32) * 32 - base.height; + + physic.enable_gravity(false); + physic.set_velocity_y(0.0f); + + physic.set_velocity_x(0); + } + else + { + physic.enable_gravity(true); + } + } + else // Player is carrying us around + { + /* FIXME: The trampoline object shouldn't know about pplayer objects. */ + /* If we're holding the iceblock */ + Player& tux = *Sector::current()->player; + Direction dir = tux.dir; + + if(dir == RIGHT) + { + base.x = tux.base.x + 16; + base.y = tux.base.y + tux.base.height/1.5 - base.height; + } + else /* facing left */ + { + base.x = tux.base.x - 16; + base.y = tux.base.y + tux.base.height/1.5 - base.height; + } + + if(collision_object_map(base)) + { + base.x = tux.base.x; + base.y = tux.base.y + tux.base.height/1.5 - base.height; + } + } + + physic.apply(frame_ratio, base.x, base.y, Sector::current()->gravity); + collision_swept_object_map(&old_base, &base); +} + +void +Trampoline::collision(const MovingObject&, int) +{ + // comes later +} + +void +Trampoline::collision(void *p_c_object, int c_object, CollisionType type) +{ + Player* pplayer_c = NULL; + switch (c_object) + { + case CO_PLAYER: + pplayer_c = (Player*) p_c_object; + + if (type == COLLISION_NORMAL) + { + // Pick up if HELD (done in Player) + } + + else if (type == COLLISION_SQUISH) + { + int squish_amount = (32 - (int)pplayer_c->base.y % 32); + + if (squish_amount < 24) + frame = 3; + else if (squish_amount < 28) + frame = 2; + else if (squish_amount < 30) + frame = 1; + else + frame = 0; + + if (squish_amount < 20) { + pplayer_c->physic.set_velocity_y(power); + pplayer_c->fall_mode = Player::TRAMPOLINE_JUMP; + } + else if (pplayer_c->physic.get_velocity_y() < 0) + pplayer_c->physic.set_velocity_y(-squish_amount/32); + } + + break; + + default: + break; + + } +} +#endif + +/* Flying Platform */ + +#if 0 +Sprite *img_flying_platform; + +FlyingPlatform::FlyingPlatform(LispReader& reader) +{ + reader.read_int_vector("x", pos_x); + reader.read_int_vector("y", pos_y); + + velocity = 2.0; + reader.read_float("velocity", velocity); + + base.x = pos_x[0]; + base.y = pos_y[0]; + base.width = 96; + base.height = 40; + + point = 0; + move = false; + + float x = pos_x[point+1] - pos_x[point]; + float y = pos_y[point+1] - pos_y[point]; + vel_x = x*velocity / sqrt(x*x + y*y); + vel_y = -(velocity - vel_x); + + frame = 0; +} + +FlyingPlatform::FlyingPlatform(int x, int y) +{ +base.x = x; +base.y = y; +point = 0; +move = false; +} + +void +FlyingPlatform::write(LispWriter& writer) +{ + writer.start_list("flying-trampoline"); + + writer.write_int_vector("x", pos_x); + writer.write_int_vector("y", pos_y); + writer.write_float("velocity", velocity); + + writer.end_list("flying-trampoline"); +} + +void +FlyingPlatform::draw(DrawingContext& context) +{ + img_flying_platform->draw(context, base, LAYER_OBJECTS); +} + +void +FlyingPlatform::action(float frame_ratio) +{ + // TODO: Remove if we're too far off the screen + +if(!move) + return; + +if((unsigned)point+1 != pos_x.size()) + { + if(((pos_x[point+1] > pos_x[point] && base.x >= pos_x[point+1]) || + (pos_x[point+1] < pos_x[point] && base.x <= pos_x[point+1]) || + pos_x[point] == pos_x[point+1]) && + ((pos_y[point+1] > pos_y[point] && base.y >= pos_y[point+1]) || + (pos_y[point+1] < pos_y[point] && base.y <= pos_y[point+1]) || + pos_y[point] == pos_y[point+1])) + { + point++; + + float x = pos_x[point+1] - pos_x[point]; + float y = pos_y[point+1] - pos_y[point]; + vel_x = x*velocity / sqrt(x*x + y*y); + vel_y = -(velocity - vel_x); + } + } +else // last point + { + // point = 0; + // reverse vector + return; + } +/* +if(pos_x[point+1] > base.x) + base.x += velocity * frame_ratio; +else if(pos_x[point+1] < base.x) + base.x -= velocity * frame_ratio; + +if(pos_y[point+1] > base.y) + base.y += velocity * frame_ratio; +else if(pos_y[point+1] < base.y) + base.y -= velocity * frame_ratio; +*/ + +base.x += vel_x * frame_ratio; +base.y += vel_y * frame_ratio; +} + +void +FlyingPlatform::collision(const MovingObject&, int) +{ + // comes later +} + +void +FlyingPlatform::collision(void *p_c_object, int c_object, CollisionType type) +{ +(void) p_c_object; +(void) type; + +// Player* pplayer_c = NULL; + switch (c_object) + { + case CO_PLAYER: +// pplayer_c = (Player*) p_c_object; + move = true; + + break; + + default: + break; + + } +} +#endif + +Sprite *img_smoke_cloud = 0; + +SmokeCloud::SmokeCloud(const Vector& pos) + : position(pos) +{ + timer.start(.3); +} + +void +SmokeCloud::action(float elapsed_time) +{ + position.y -= 120 * elapsed_time; + + if(timer.check()) + remove_me(); +} + +void +SmokeCloud::draw(DrawingContext& context) +{ + img_smoke_cloud->draw(context, position, LAYER_OBJECTS+1); +} + +Particles::Particles(const Vector& epicenter, int min_angle, int max_angle, + const Vector& initial_velocity, const Vector& acceleration, int number, + Color color_, int size_, float life_time, int drawing_layer_) + : accel(acceleration), color(color_), size(size_), drawing_layer(drawing_layer_) +{ + if(life_time == 0) { + live_forever = true; + } else { + live_forever = false; + timer.start(life_time); + } + + // create particles + for(int p = 0; p < number; p++) + { + Particle* particle = new Particle; + particle->pos = epicenter; + + float angle = ((rand() % (max_angle-min_angle))+min_angle) + * (M_PI / 180); // convert to radius + particle->vel.x = /*fabs*/(sin(angle)) * initial_velocity.x; +// if(angle >= M_PI && angle < M_PI*2) +// particle->vel.x *= -1; // work around to fix signal + particle->vel.y = /*fabs*/(cos(angle)) * initial_velocity.y; +// if(angle >= M_PI_2 && angle < 3*M_PI_2) +// particle->vel.y *= -1; + + particles.push_back(particle); + } +} + +Particles::~Particles() +{ + // free particles + for(std::vector::iterator i = particles.begin(); + i < particles.end(); i++) + delete (*i); +} + +void +Particles::action(float elapsed_time) +{ + Vector camera = Sector::current()->camera->get_translation(); + + // update particles + for(std::vector::iterator i = particles.begin(); + i != particles.end(); ) { + (*i)->pos.x += (*i)->vel.x * elapsed_time; + (*i)->pos.y += (*i)->vel.y * elapsed_time; + + (*i)->vel.x += accel.x * elapsed_time; + (*i)->vel.y += accel.y * elapsed_time; + + if((*i)->pos.x < camera.x || (*i)->pos.x > screen->w + camera.x || + (*i)->pos.y < camera.y || (*i)->pos.y > screen->h + camera.y) { + delete (*i); + i = particles.erase(i); + } else { + ++i; + } + } + + if((timer.check() && !live_forever) || particles.size() == 0) + remove_me(); +} + +void +Particles::draw(DrawingContext& context) +{ + // draw particles + for(std::vector::iterator i = particles.begin(); + i != particles.end(); i++) { + context.draw_filled_rect((*i)->pos, Vector(size,size), color,drawing_layer); + } +} + +void load_object_gfx() +{ +#if 0 + img_trampoline = sprite_manager->load("trampoline"); + img_trampoline->start_animation(0); + img_flying_platform = sprite_manager->load("flying_platform"); +#endif + img_smoke_cloud = sprite_manager->create("stomp"); +} + +void free_object_gfx() +{ + delete img_smoke_cloud; +} + diff --git a/src/object/gameobjs.h b/src/object/gameobjs.h new file mode 100644 index 000000000..3d928be0e --- /dev/null +++ b/src/object/gameobjs.h @@ -0,0 +1,161 @@ +// $Id$ +// +// SuperTux +// Copyright (C) 2000 Bill Kendrick +// Copyright (C) 2004 Tobias Glaesser +// +// 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_GAMEOBJS_H +#define SUPERTUX_GAMEOBJS_H + +#include "video/surface.h" +#include "timer.h" +#include "scene.h" +#include "math/physic.h" +#include "special/game_object.h" +#include "special/moving_object.h" +#include "serializable.h" +#include "utils/lispwriter.h" + +/* Bounciness of distros: */ +#define NO_BOUNCE 0 +#define BOUNCE 1 + +namespace SuperTux { +class Sprite; +} + +class BouncyCoin : public GameObject +{ +public: + BouncyCoin(const Vector& pos); + ~BouncyCoin(); + virtual void action(float elapsed_time); + virtual void draw(DrawingContext& context); + +private: + Sprite* sprite; + Vector position; + Timer2 timer; +}; + +class BrokenBrick : public GameObject +{ +public: + BrokenBrick(Sprite* sprite, const Vector& pos, const Vector& movement); + ~BrokenBrick(); + + virtual void action(float elapsed_time); + virtual void draw(DrawingContext& context); + +private: + Timer2 timer; + Sprite* sprite; + Vector position; + Vector movement; +}; + +class FloatingText : public GameObject +{ +public: + FloatingText(const Vector& pos, const std::string& text_); + FloatingText(const Vector& pos, int s); // use this for score, for instance + + virtual void action(float elapsed_time); + virtual void draw(DrawingContext& context); + +private: + Vector position; + std::string text; + Timer2 timer; +}; + +#if 0 +extern Sprite *img_trampoline; + +class Trampoline : public MovingObject, public Serializable +{ +public: + Trampoline(LispReader& reader); + Trampoline(float x, float y); + + virtual void write(LispWriter& writer); + virtual void action(float frame_ratio); + virtual void draw(DrawingContext& context); + + virtual void collision(const MovingObject& other, int); + void collision(void *p_c_object, int c_object, CollisionType type); + + Physic physic; + enum { M_NORMAL, M_HELD } mode; + + private: + float power; + unsigned int frame; +}; +#endif + +extern Sprite *img_smoke_cloud; + +class SmokeCloud : public GameObject +{ +public: + SmokeCloud(const Vector& pos); + + virtual void action(float elapsed_time); + virtual void draw(DrawingContext& context); + +private: + Timer2 timer; + Vector position; +}; + +class Particles : public GameObject +{ +public: + Particles(const Vector& epicenter, int min_angle, int max_angle, + const Vector& initial_velocity, const Vector& acceleration, + int number, Color color, int size, float life_time, int drawing_layer); + ~Particles(); + + virtual void action(float elapsed_time); + virtual void draw(DrawingContext& context); + +private: + Vector accel; + Timer2 timer; + bool live_forever; + + Color color; + float size; + int drawing_layer; + + struct Particle { + Vector pos, vel; +// float angle; + }; + std::vector particles; +}; + +void load_object_gfx(); +void free_object_gfx(); + +#endif + +/* Local Variables: */ +/* mode:c++ */ +/* End: */ diff --git a/src/object/particlesystem.cpp b/src/object/particlesystem.cpp new file mode 100644 index 000000000..b82152a13 --- /dev/null +++ b/src/object/particlesystem.cpp @@ -0,0 +1,176 @@ +// $Id$ +// +// SuperTux +// Copyright (C) 2004 Matthias Braun +// +// 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 "particlesystem.h" +#include "app/globals.h" +#include "utils/lispreader.h" +#include "utils/lispwriter.h" +#include "video/drawing_context.h" + +ParticleSystem::ParticleSystem() +{ + virtual_width = screen->w; + virtual_height = screen->h; + layer = LAYER_BACKGROUND1; +} + +ParticleSystem::~ParticleSystem() +{ + std::vector::iterator i; + for(i = particles.begin(); i != particles.end(); ++i) { + delete *i; + } +} + +void ParticleSystem::draw(DrawingContext& context) +{ + float scrollx = context.get_translation().x; + float scrolly = context.get_translation().y; + + context.push_transform(); + context.set_translation(Vector(0,0)); + + std::vector::iterator i; + for(i = particles.begin(); i != particles.end(); ++i) { + Particle* particle = *i; + + // remap x,y coordinates onto screencoordinates + Vector pos; + pos.x = fmodf(particle->pos.x - scrollx, virtual_width); + if(pos.x < 0) pos.x += virtual_width; + pos.y = fmodf(particle->pos.y - scrolly, virtual_height); + if(pos.y < 0) pos.y += virtual_height; + + 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); + } + + context.pop_transform(); +} + +SnowParticleSystem::SnowParticleSystem() +{ + snowimages[0] = new Surface(datadir+"/images/shared/snow0.png", true); + snowimages[1] = new Surface(datadir+"/images/shared/snow1.png", true); + snowimages[2] = new Surface(datadir+"/images/shared/snow2.png", true); + + virtual_width = screen->w * 2; + + // create some random snowflakes + size_t snowflakecount = size_t(virtual_width/10.0); + for(size_t i=0; ipos.x = rand() % int(virtual_width); + particle->pos.y = rand() % screen->h; + int snowsize = rand() % 3; + particle->texture = snowimages[snowsize]; + do { + particle->speed = snowsize*.2 + (float(rand()%10)*.4); + } while(particle->speed < 1); + particle->speed *= 10; // 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) + delete snowimages[i]; +} + +void SnowParticleSystem::action(float elapsed_time) +{ + std::vector::iterator i; + for(i = particles.begin(); i != particles.end(); ++i) { + SnowParticle* particle = (SnowParticle*) *i; + particle->pos.y += particle->speed * elapsed_time; + if(particle->pos.y > screen->h) { + particle->pos.y = fmodf(particle->pos.y , virtual_height); + particle->pos.x = rand() % int(virtual_width); + } + } +} + +CloudParticleSystem::CloudParticleSystem() +{ + cloudimage = new Surface(datadir + "/images/shared/cloud.png", true); + + virtual_width = 2000.0; + + // create some random clouds + for(size_t i=0; i<15; ++i) { + CloudParticle* particle = new CloudParticle; + particle->pos.x = rand() % int(virtual_width); + particle->pos.y = rand() % int(virtual_height); + particle->texture = cloudimage; + particle->speed = -float(25 + rand() % 30); + + particles.push_back(particle); + } +} + +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; +} + +void CloudParticleSystem::action(float elapsed_time) +{ + std::vector::iterator i; + for(i = particles.begin(); i != particles.end(); ++i) { + CloudParticle* particle = (CloudParticle*) *i; + particle->pos.x += particle->speed * elapsed_time; + } +} diff --git a/src/object/particlesystem.h b/src/object/particlesystem.h new file mode 100644 index 000000000..d11c5c13d --- /dev/null +++ b/src/object/particlesystem.h @@ -0,0 +1,126 @@ +// $Id$ +// +// SuperTux +// Copyright (C) 2004 Matthias Braun +// +// 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_PARTICLESYSTEM_H +#define SUPERTUX_PARTICLESYSTEM_H + +#include + +#include "video/surface.h" +#include "special/game_object.h" +#include "serializable.h" + +using namespace SuperTux; + +namespace SuperTux { +class LispReader; +} + +class DisplayManager; + +/** + * This is the base class for particle systems. It is responsible for storing a + * set of particles with each having an x- and y-coordinate the number of the + * layer where it should be drawn and a texture. + * The coordinate system used here is a virtual one. It would be a bad idea to + * populate whole levels with particles. So we're using a virtual rectangle + * here that is tiled onto the level when drawing. This rectangle has the size + * (virtual_width, virtual_height). We're using modulo on the particle + * coordinates, so when a particle leaves left, it'll reenter at the right + * side. + * + * Classes that implement a particle system should subclass from this class, + * initialize particles in the constructor and move them in the simulate + * function. + */ +class ParticleSystem : public GameObject +{ +public: + ParticleSystem(); + virtual ~ParticleSystem(); + + virtual void draw(DrawingContext& context); + +protected: + int layer; + + class Particle + { + public: + virtual ~Particle() + { } + + Vector pos; + Surface* texture; + }; + + std::vector particles; + float virtual_width, virtual_height; +}; + +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 + { return "SnowParticleSystem"; } + +private: + class SnowParticle : public Particle + { + public: + float speed; + }; + + Surface* snowimages[3]; +}; + +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 + { return "SnowParticleSystem"; } + +private: + class CloudParticle : public Particle + { + public: + float speed; + }; + + Surface* cloudimage; +}; + +#endif + diff --git a/src/object/player.cpp b/src/object/player.cpp new file mode 100644 index 000000000..11a6ae214 --- /dev/null +++ b/src/object/player.cpp @@ -0,0 +1,1018 @@ +// $Id$ +// +// SuperTux - A Jump'n Run +// Copyright (C) 2003 Tobias Glaesser +// +// 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 + +#include "app/globals.h" +#include "app/gettext.h" +#include "player.h" +#include "defines.h" +#include "scene.h" +#include "tile.h" +#include "special/sprite.h" +#include "sector.h" +#include "resources.h" +#include "video/screen.h" +#include "statistics.h" +#include "gameloop.h" +#include "object/tilemap.h" +#include "object/camera.h" +#include "object/gameobjs.h" +#include "trigger/trigger_base.h" + +static const int TILES_FOR_BUTTJUMP = 3; +static const float SHOOTING_TIME = .150; +/// time before idle animation starts +static const float IDLE_TIME = 2.5; + +static const float WALK_ACCELERATION_X = 300; +static const float RUN_ACCELERATION_X = 400; +static const float SKID_XM = 200; +static const float SKID_TIME = .3; +static const float MAX_WALK_XM = 230; +static const float MAX_RUN_XM = 320; +static const float WALK_SPEED = 100; + +// growing animation +Surface* growingtux_left[GROWING_FRAMES]; +Surface* growingtux_right[GROWING_FRAMES]; + +Surface* tux_life = 0; + +Sprite* smalltux_gameover = 0; +Sprite* smalltux_star = 0; +Sprite* bigtux_star = 0; + +TuxBodyParts* small_tux = 0; +TuxBodyParts* big_tux = 0; +TuxBodyParts* fire_tux = 0; +TuxBodyParts* ice_tux = 0; + +PlayerKeymap keymap; + +PlayerKeymap::PlayerKeymap() +{ + keymap.up = SDLK_UP; + keymap.down = SDLK_DOWN; + keymap.left = SDLK_LEFT; + keymap.right = SDLK_RIGHT; + + keymap.power = SDLK_LCTRL; + keymap.jump = SDLK_SPACE; +} + +void player_input_init(player_input_type* pplayer_input) +{ + pplayer_input->up = UP; + pplayer_input->old_up = UP; + pplayer_input->down = UP; + pplayer_input->fire = UP; + pplayer_input->left = UP; + pplayer_input->old_fire = UP; + pplayer_input->right = UP; + pplayer_input->jump = UP; + pplayer_input->old_jump = UP; + pplayer_input->activate = UP; +} + +void +TuxBodyParts::set_action(std::string action, int loops) +{ + if(head != NULL) + head->set_action(action, loops); + if(body != NULL) + body->set_action(action, loops); + if(arms != NULL) + arms->set_action(action, loops); + if(feet != NULL) + feet->set_action(action, loops); +} + +void +TuxBodyParts::draw(DrawingContext& context, const Vector& pos, int layer, + Uint32 drawing_effect) +{ + if(head != NULL) + head->draw(context, pos, layer-1, drawing_effect); + if(body != NULL) + body->draw(context, pos, layer-3, drawing_effect); + if(arms != NULL) + arms->draw(context, pos, layer, drawing_effect); + if(feet != NULL) + feet->draw(context, pos, layer-2, drawing_effect); +} + +Player::Player() +{ + init(); +} + +Player::~Player() +{ +} + +void +Player::init() +{ + holding_something = false; + + bbox.set_size(31.8, 31.8); + + size = SMALL; + got_power = NONE_POWER; + + dir = RIGHT; + old_dir = dir; + duck = false; + dead = false; + + dying = DYING_NOT; + last_ground_y = 0; + fall_mode = ON_GROUND; + jumping = false; + flapping = false; + can_jump = true; + can_flap = false; + falling_from_flap = false; + enable_hover = false; + butt_jump = false; + + flapping_velocity = 0; + + // temporary to help player's choosing a flapping + flapping_mode = MAREK_FLAP; + + // Ricardo's flapping + flaps_nb = 0; + + on_ground_flag = false; + + player_input_init(&input); + + physic.reset(); +} + +int +Player::key_event(SDLKey key, int state) +{ + idle_timer.start(IDLE_TIME, true); + + if(key == keymap.right) + { + input.right = state; + return true; + } + else if(key == keymap.left) + { + input.left = state; + return true; + } + else if(key == keymap.up) + { + if(state == UP) + input.old_up = UP; + input.up = state; + /* Up key also opens activates stuff */ + input.activate = state; + return true; + } + else if(key == keymap.down) + { + input.down = state; + return true; + } + else if(key == keymap.power) + { + if (state == UP) + input.old_fire = UP; + input.fire = state; + + return true; + } + else if(key == keymap.jump) + { + if (state == UP) + input.old_jump = UP; + input.jump = state; + return true; + } + else + return false; +} + +void +Player::level_begin() +{ + move(Vector(100, 170)); + duck = false; + + dying = DYING_NOT; + + player_input_init(&input); + + on_ground_flag = false; + + physic.reset(); +} + +PlayerStatus& +Player::get_status() +{ + return player_status; +} + +void +Player::action(float elapsed_time) +{ + if(dying && dying_timer.check()) { + dead = true; + return; + } + + if (input.fire == UP) + holding_something = false; + + if(dying == DYING_NOT) + handle_input(); + + movement = physic.get_movement(elapsed_time); + +#if 0 + // special exception for cases where we're stuck under tiles after + // being ducked. In this case we drift out + if(!duck && on_ground() && old_base.x == base.x && old_base.y == base.y + && collision_object_map(base)) + { + base.x += elapsed_time * WALK_SPEED * (dir ? 1: -1); + previous_base = old_base = base; + } + + /* Reset score multiplier (for multi-hits): */ + if (!invincible_timer.started()) + { + if(player_status.score_multiplier > player_status.max_score_multiplier) + { + player_status.max_score_multiplier = player_status.score_multiplier; + + // show a message + char str[124]; + sprintf(str, _("New max combo: %d"), player_status.max_score_multiplier-1); + Sector::current()->add_floating_text(base, str); + } + player_status.score_multiplier = 1; + } + } + + } +#endif + + on_ground_flag = false; +} + +bool +Player::on_ground() +{ + return on_ground_flag; +} + +void +Player::handle_horizontal_input() +{ + float vx = physic.get_velocity_x(); + float vy = physic.get_velocity_y(); + float ax = physic.get_acceleration_x(); + float ay = physic.get_acceleration_y(); + + float dirsign = 0; + if(input.left == DOWN && input.right == UP && (!duck || physic.get_velocity_y() != 0)) { + old_dir = dir; + dir = LEFT; + dirsign = -1; + } else if(input.left == UP && input.right == DOWN && (!duck || physic.get_velocity_y() != 0)) { + old_dir = dir; + dir = RIGHT; + dirsign = 1; + } + + if (input.fire == UP) { + ax = dirsign * WALK_ACCELERATION_X; + // limit speed + if(vx >= MAX_WALK_XM && dirsign > 0) { + vx = MAX_WALK_XM; + ax = 0; + } else if(vx <= -MAX_WALK_XM && dirsign < 0) { + vx = -MAX_WALK_XM; + ax = 0; + } + } else { + ax = dirsign * RUN_ACCELERATION_X; + // limit speed + if(vx >= MAX_RUN_XM && dirsign > 0) { + vx = MAX_RUN_XM; + ax = 0; + } else if(vx <= -MAX_RUN_XM && dirsign < 0) { + vx = -MAX_RUN_XM; + ax = 0; + } + } + + // we can reach WALK_SPEED without any acceleration + if(dirsign != 0 && fabs(vx) < WALK_SPEED) { + vx = dirsign * WALK_SPEED; + } + + // changing directions? + if(on_ground() && ((vx < 0 && dirsign >0) || (vx>0 && dirsign<0))) + { + // let's skid! + if(fabs(vx)>SKID_XM && !skidding_timer.started()) + { + skidding_timer.start(SKID_TIME); + SoundManager::get()->play_sound(IDToSound(SND_SKID)); + // dust some partcles + Sector::current()->add_object( + new Particles( + Vector(bbox.p1.x + (dir == RIGHT ? bbox.get_width() : 0), + bbox.p2.y), + dir == RIGHT ? 270+20 : 90-40, dir == RIGHT ? 270+40 : 90-20, + Vector(280,-260), Vector(0,0.030), 3, Color(100,100,100), 3, .8, + LAYER_OBJECTS+1)); + + ax *= 2.5; + } + else + { + ax *= 2; + } + } + + // we get slower when not pressing any keys + if(dirsign == 0) { + if(fabs(vx) < WALK_SPEED) { + vx = 0; + ax = 0; + } else if(vx < 0) { + ax = WALK_ACCELERATION_X * 1.5; + } else { + ax = WALK_ACCELERATION_X * -1.5; + } + } + +#if 0 + // if we're on ice slow down acceleration or deceleration + if (isice(base.x, base.y + base.height)) + { + /* the acceleration/deceleration rate on ice is inversely proportional to + * the current velocity. + */ + + // increasing 1 will increase acceleration/deceleration rate + // decreasing 1 will decrease acceleration/deceleration rate + // must stay above zero, though + if (ax != 0) ax *= 1 / fabs(vx); + } +#endif + + // extend/shrink tux collision rectangle so that we fall through/walk over 1 + // tile holes + if(fabsf(vx) > MAX_WALK_XM) { + bbox.set_width(33); + } else { + bbox.set_width(31.8); + } + + physic.set_velocity(vx, vy); + physic.set_acceleration(ax, ay); +} + +void +Player::handle_vertical_input() +{ + // set fall mode... + if(on_ground()) { + fall_mode = ON_GROUND; + last_ground_y = get_pos().y; + } else { + if(get_pos().y > last_ground_y) + fall_mode = FALLING; + else if(fall_mode == ON_GROUND) + fall_mode = JUMPING; + } + + if(on_ground()) { /* Make sure jumping is off. */ + jumping = false; + flapping = false; + falling_from_flap = false; + if (flapping_timer.started()) { + flapping_timer.start(0); + } + + physic.set_acceleration_y(0); //for flapping + } + + // Press jump key + if(input.jump == DOWN && can_jump && on_ground()) + { + if(duck) { // only jump a little bit when in duck mode { + physic.set_velocity_y(300); + } else { + // jump higher if we are running + if (fabs(physic.get_velocity_x()) > MAX_WALK_XM) + physic.set_velocity_y(580); + else + physic.set_velocity_y(520); + } + + //bbox.move(Vector(0, -1)); + jumping = true; + flapping = false; + can_jump = false; + can_flap = false; + flaps_nb = 0; // Ricardo's flapping + if (size == SMALL) + SoundManager::get()->play_sound(IDToSound(SND_JUMP)); + else + SoundManager::get()->play_sound(IDToSound(SND_BIGJUMP)); + } + // Let go of jump key + else if(input.jump == UP) + { + if (!flapping && !duck && !falling_from_flap && !on_ground()) + { + can_flap = true; + } + if (jumping && physic.get_velocity_y() > 0) + { + jumping = false; + physic.set_velocity_y(0); + } + } + + // temporary to help player's choosing a flapping + if(flapping_mode == RICARDO_FLAP) + { + // Flapping, Ricardo's version + // similar to SM3 Fox + if(input.jump == DOWN && input.old_jump == UP && can_flap && + flaps_nb < 3) + { + physic.set_velocity_y(350); + physic.set_velocity_x(physic.get_velocity_x() * 35); + flaps_nb++; + } + } + else if(flapping_mode == MAREK_FLAP) + { + // Flapping, Marek's version + if (input.jump == DOWN && can_flap) + { + if (!flapping_timer.started()) + { + flapping_timer.start(TUX_FLAPPING_TIME); + flapping_velocity = physic.get_velocity_x(); + } + if (flapping_timer.check()) + { + can_flap = false; + falling_from_flap = true; + } + jumping = true; + flapping = true; + if (!flapping_timer.check()) { + float cv = flapping_velocity * sqrt( + TUX_FLAPPING_TIME - flapping_timer.get_timegone() + / TUX_FLAPPING_TIME); + + //Handle change of direction while flapping + if (((dir == LEFT) && (cv > 0)) || (dir == RIGHT) && (cv < 0)) { + cv *= (-1); + } + physic.set_velocity_x(cv); + physic.set_velocity_y( + flapping_timer.get_timegone()/.850); + } + } + } + else if(flapping_mode == RYAN_FLAP) + { + // Flapping, Ryan's version + if (input.jump == DOWN && can_flap) + { + if (!flapping_timer.started()) + { + flapping_timer.start(TUX_FLAPPING_TIME); + } + if (flapping_timer.check()) + { + can_flap = false; + falling_from_flap = true; + } + jumping = true; + flapping = true; + if (flapping && flapping_timer.get_timegone() <= TUX_FLAPPING_TIME + && physic.get_velocity_y() < 0) + { + float gravity = Sector::current()->gravity; + (void)gravity; + float xr = (fabsf(physic.get_velocity_x()) / MAX_RUN_XM); + + // XXX: magic numbers. should be a percent of gravity + // gravity is (by default) -0.1f + physic.set_acceleration_y(12 + 1*xr); + +#if 0 + // To slow down x-vel when flapping (not working) + if (fabsf(physic.get_velocity_x()) > MAX_WALK_XM) + { + if (physic.get_velocity_x() < 0) + physic.set_acceleration_x(1.0f); + else if (physic.get_velocity_x() > 0) + physic.set_acceleration_x(-1.0f); + } +#endif + } + } + else + { + physic.set_acceleration_y(0); + } + } + + // Hover + //(disabled by default, use cheat code "hover" to toggle on/off) + //TODO: needs some tweaking, especially when used together with double jump and jumping off badguys + if (enable_hover && input.jump == DOWN && !jumping && !butt_jump && physic.get_velocity_y() <= 0) + { + physic.set_velocity_y(-100); + } + +#if 0 + /* In case the player has pressed Down while in a certain range of air, + enable butt jump action */ + if (input.down == DOWN && !butt_jump && !duck) + if(tiles_on_air(TILES_FOR_BUTTJUMP) && jumping) + butt_jump = true; +#endif + + /* When Down is not held anymore, disable butt jump */ + if(butt_jump && input.down == UP) + butt_jump = false; + + // Do butt jump + if (butt_jump && on_ground() && size == BIG) + { + // Add a smoke cloud + if (duck) + Sector::current()->add_smoke_cloud(Vector(get_pos().x - 32, get_pos().y)); + else + Sector::current()->add_smoke_cloud( + Vector(get_pos().x - 32, get_pos().y + 32)); + + butt_jump = false; + +#if 0 + // Break bricks beneath Tux + 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; + } +#endif + +#if 0 + // Kill nearby badguys + std::vector gameobjects = Sector::current()->gameobjects; + for (std::vector::iterator i = gameobjects.begin(); + i != gameobjects.end(); + i++) + { + BadGuy* badguy = dynamic_cast (*i); + if(badguy) + { + // don't kill when badguys are already dying or in a certain mode + if(badguy->dying == DYING_NOT && badguy->mode != BadGuy::BOMB_TICKING && + badguy->mode != BadGuy::BOMB_EXPLODE) + { + if (fabsf(base.x - badguy->base.x) < 96 && + fabsf(base.y - badguy->base.y) < 64) + badguy->kill_me(25); + } + } + } +#endif + } + + /** jumping is only allowed if we're about to touch ground soon and if the + * button has been up in between the last jump + */ + // FIXME +#if 0 + if ( (issolid(get_pos().x + bbox.get_width() / 2, + get_pos().y + bbox.get_height() + 64) || + issolid(get_pos().x + 1, get_pos().y + bbox.get_height() + 64) || + issolid(get_pos().x + bbox.get_width() - 1, + get_pos().y + bbox.get_height() + 64)) + && jumping == false + && can_jump == false + && input.jump == DOWN + && input.old_jump == UP) + { + can_jump = true; + } +#endif + + input.old_jump = input.jump; +} + +void +Player::handle_input() +{ + /* Handle horizontal movement: */ + handle_horizontal_input(); + + /* Jump/jumping? */ + if (on_ground() && input.jump == UP) + can_jump = true; + handle_vertical_input(); + + /* Shoot! */ + if (input.fire == DOWN && input.old_fire == UP && got_power != NONE_POWER) { + if(Sector::current()->add_bullet( + get_pos() + Vector(0, bbox.get_height()/2), + physic.get_velocity_x(), dir)) + shooting_timer.start(SHOOTING_TIME); + input.old_fire = DOWN; + } + + /* Duck! */ + if (input.down == DOWN && size == BIG && !duck + && physic.get_velocity_y() == 0 && on_ground()) + { + duck = true; + bbox.move(Vector(0, 32)); + bbox.set_height(31.8); + } + else if(input.down == UP && size == BIG && duck) + { + // try if we can really unduck + bbox.move(Vector(0, -32)); + bbox.set_height(63.8); + duck = false; + // FIXME +#if 0 + // when unducking in air we need some space to do so + if(on_ground() || !collision_object_map(bbox)) { + duck = false; + } else { + // undo the ducking changes + bbox.move(Vector(0, 32)); + bbox.set_height(31.8); + } +#endif + } +} + +void +Player::grow(bool animate) +{ + if(size == BIG) + return; + + size = BIG; + bbox.set_height(63.8); + bbox.move(Vector(0, -32)); + + if(animate) + growing_timer.start(GROWING_TIME); +} + +void +Player::grabdistros() +{ +} + +void +Player::draw(DrawingContext& context) +{ + TuxBodyParts* tux_body; + + if (size == SMALL) + tux_body = small_tux; + else if (got_power == FIRE_POWER) + tux_body = fire_tux; + else if (got_power == ICE_POWER) + tux_body = ice_tux; + else + tux_body = big_tux; + + int layer = LAYER_OBJECTS + 10; + + /* Set Tux sprite action */ + if (duck && size == BIG) + { + if(dir == LEFT) + tux_body->set_action("duck-left"); + else // dir == RIGHT + tux_body->set_action("duck-right"); + } + else if (skidding_timer.started() && !skidding_timer.check()) + { + if(dir == LEFT) + tux_body->set_action("skid-left"); + else // dir == RIGHT + tux_body->set_action("skid-right"); + } + else if (kick_timer.started() && !kick_timer.check()) + { + if(dir == LEFT) + tux_body->set_action("kick-left"); + else // dir == RIGHT + tux_body->set_action("kick-right"); + } + else if (butt_jump && size == BIG) + { + if(dir == LEFT) + tux_body->set_action("buttjump-left"); + else // dir == RIGHT + tux_body->set_action("buttjump-right"); + } + else if (physic.get_velocity_y() != 0) + { + if(dir == LEFT) + tux_body->set_action("jump-left"); + else // dir == RIGHT + tux_body->set_action("jump-right"); + } + else + { + if (fabsf(physic.get_velocity_x()) < 1.0f) // standing + { + if(dir == LEFT) + tux_body->set_action("stand-left"); + else // dir == RIGHT + tux_body->set_action("stand-right"); + } + else // moving + { + if(dir == LEFT) + tux_body->set_action("walk-left"); + else // dir == RIGHT + tux_body->set_action("walk-right"); + } + } + + if(idle_timer.check()) + { + if(size == BIG) + { + if(dir == LEFT) + tux_body->head->set_action("idle-left", 1); + else // dir == RIGHT + tux_body->head->set_action("idle-right", 1); + } + + } + + // Tux is holding something + if ((holding_something && physic.get_velocity_y() == 0) || + (shooting_timer.get_timeleft() > 0 && !shooting_timer.check())) + { + if (duck) + { + if(dir == LEFT) + tux_body->arms->set_action("duck+grab-left"); + else // dir == RIGHT + tux_body->arms->set_action("duck+grab-right"); + } + else + { + if(dir == LEFT) + tux_body->arms->set_action("grab-left"); + else // dir == RIGHT + tux_body->arms->set_action("grab-right"); + } + } + + /* Draw Tux */ + if (dying == DYING_SQUISHED) { + smalltux_gameover->draw(context, get_pos(), LAYER_FOREGROUNDTILES+1); + } else if(growing_timer.get_timeleft() > 0) { + if(size == SMALL) + { + if (dir == RIGHT) + context.draw_surface(growingtux_right[GROWING_FRAMES-1 - + int((growing_timer.get_timegone() * + GROWING_FRAMES) / GROWING_TIME)], get_pos(), layer); + else + context.draw_surface(growingtux_left[GROWING_FRAMES-1 - + int((growing_timer.get_timegone() * + GROWING_FRAMES) / GROWING_TIME)], get_pos(), layer); + } + else + { + if (dir == RIGHT) + context.draw_surface(growingtux_right[ + int((growing_timer.get_timegone() * + GROWING_FRAMES) / GROWING_TIME)], get_pos(), layer); + else + context.draw_surface(growingtux_left[ + int((growing_timer.get_timegone() * + GROWING_FRAMES) / GROWING_TIME)], + get_pos(), layer); + } + } + else if (safe_timer.started() && size_t(global_time*40)%2) + ; // don't draw Tux + else + tux_body->draw(context, get_pos(), layer); + + // Draw blinking star overlay + if (invincible_timer.started() && + (invincible_timer.get_timeleft() > TUX_INVINCIBLE_TIME_WARNING + || size_t(global_time*20)%2) + && !dying) + { + if (size == SMALL || duck) + smalltux_star->draw(context, get_pos(), layer + 5); + else + bigtux_star->draw(context, get_pos(), layer + 5); + } + + if (debug_mode) + context.draw_filled_rect(get_pos(), + Vector(bbox.get_width(), bbox.get_height()), + Color(75,75,75, 150), LAYER_OBJECTS+20); +} + +HitResponse +Player::collision(GameObject& other, const CollisionHit& hit) +{ + if(other.get_flags() & FLAG_SOLID) { + if(hit.normal.y < 0) { // landed on floor? + if (physic.get_velocity_y() < 0) + physic.set_velocity_y(0); + on_ground_flag = true; + } else if(hit.normal.y > 0) { // bumped against the roof + physic.set_velocity_y(.1); + } + + if(fabsf(hit.normal.x) > .9) { // hit on the side? + physic.set_velocity_x(0); + } + + return CONTINUE; + } + + TriggerBase* trigger = dynamic_cast (&other); + if(trigger) { + if(input.up == DOWN && input.old_up == UP) + trigger->event(*this, TriggerBase::EVENT_ACTIVATE); + } + + return FORCE_MOVE; +} + +void +Player::make_invincible() +{ + SoundManager::get()->play_sound(IDToSound(SND_HERRING)); + invincible_timer.start(TUX_INVINCIBLE_TIME); + Sector::current()->play_music(HERRING_MUSIC); +} + +/* Kill Player! */ +void +Player::kill(HurtMode mode) +{ + if(dying) + return; + + if(safe_timer.get_timeleft() > 0 || invincible_timer.get_timeleft() > 0) + return; + + SoundManager::get()->play_sound(IDToSound(SND_HURT)); + + physic.set_velocity_x(0); + + if (mode == SHRINK && size == BIG) + { + if (got_power != NONE_POWER) + { + safe_timer.start(TUX_SAFE_TIME); + got_power = NONE_POWER; + } + else + { + growing_timer.start(GROWING_TIME); + safe_timer.start(TUX_SAFE_TIME + GROWING_TIME); + size = SMALL; + bbox.set_height(31.8); + duck = false; + } + } + else + { + physic.enable_gravity(true); + physic.set_acceleration(0, 0); + physic.set_velocity(0, 700); + --player_status.lives; + dying = DYING_SQUISHED; + dying_timer.start(3.0); + flags |= FLAG_NO_COLLDET; + } +} + +/* Remove Tux's power ups */ +void +Player::remove_powerups() +{ + got_power = NONE_POWER; + size = SMALL; + bbox.set_height(31.8); +} + +void +Player::move(const Vector& vector) +{ + bbox.set_pos(vector); +} + +void +Player::check_bounds(Camera* camera) +{ + /* Keep tux in bounds: */ + if (get_pos().x < 0) + { // Lock Tux to the size of the level, so that he doesn't fall of + // on the left side + bbox.set_pos(Vector(0, get_pos().y)); + } + + /* Keep in-bounds, vertically: */ + if (get_pos().y > Sector::current()->solids->get_height() * 32) + { + kill(KILL); + return; + } + + bool adjust = false; + // can happen if back scrolling is disabled + if(get_pos().x < camera->get_translation().x) { + bbox.set_pos(Vector(camera->get_translation().x, get_pos().y)); + adjust = true; + } + if(get_pos().x >= camera->get_translation().x + screen->w - bbox.get_width()) + { + bbox.set_pos(Vector( + camera->get_translation().x + screen->w - bbox.get_width(), + get_pos().y)); + adjust = true; + } + + if(adjust) { + // FIXME +#if 0 + // squished now? + if(collision_object_map(bbox)) { + kill(KILL); + return; + } +#endif + } +} + +void +Player::bounce(BadGuy& ) +{ + //Make sure we stopped flapping + flapping = false; + falling_from_flap = false; + + if (input.jump) + physic.set_velocity_y(520); + else + physic.set_velocity_y(200); +} + diff --git a/src/object/player.h b/src/object/player.h new file mode 100644 index 000000000..be6398cba --- /dev/null +++ b/src/object/player.h @@ -0,0 +1,224 @@ +// $Id$ +// +// SuperTux - A Jump'n Run +// Copyright (C) 2003 Tobias Glaesser +// +// 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_PLAYER_H +#define SUPERTUX_PLAYER_H + +#include "SDL.h" + +#include "timer.h" +#include "video/surface.h" +#include "special/moving_object.h" +#include "special/sprite.h" +#include "math/physic.h" +#include "defines.h" + +using namespace SuperTux; + +class BadGuy; + +/* Times: */ + +#define TUX_SAFE_TIME 1.250 +#define TUX_INVINCIBLE_TIME 10.0 +#define TUX_INVINCIBLE_TIME_WARNING 2.0 +#define TUX_FLAPPING_TIME 1 /* How long Tux can flap his wings to gain additional jump height */ +#define TIME_WARNING 20 /* When to alert player they're low on time! */ + +/* Scores: */ + +#define SCORE_BRICK 5 +#define SCORE_DISTRO 20 + +/* Sizes: */ + +#define SMALL 0 +#define BIG 1 + +#include + +struct PlayerKeymap +{ +public: + int jump; + int up; + int down; + int left; + int right; + int power; + + PlayerKeymap(); +}; + +extern PlayerKeymap keymap; + +struct player_input_type +{ + int right; + int left; + int up; + int old_up; + int down; + int fire; + int old_fire; + int activate; + int jump; + int old_jump; +}; + +void player_input_init(player_input_type* pplayer_input); + +class Camera; +class PlayerStatus; + +extern Surface* tux_life; + +extern Sprite* smalltux_gameover; +extern Sprite* smalltux_star; +extern Sprite* bigtux_star; + +#define GROWING_TIME 1.0 +#define GROWING_FRAMES 7 +extern Surface* growingtux_left[GROWING_FRAMES]; +extern Surface* growingtux_right[GROWING_FRAMES]; + +class TuxBodyParts +{ +public: + TuxBodyParts() + : head(0), body(0), arms(0), feet(0) + { } + ~TuxBodyParts() { + delete head; + delete body; + delete arms; + delete feet; + } + + void set_action(std::string action, int loops = -1); + void one_time_animation(); + void draw(DrawingContext& context, const Vector& pos, int layer, + Uint32 drawing_effect = NONE_EFFECT); + + Sprite* head; + Sprite* body; + Sprite* arms; + Sprite* feet; +}; + +extern TuxBodyParts* small_tux; +extern TuxBodyParts* big_tux; +extern TuxBodyParts* fire_tux; +extern TuxBodyParts* ice_tux; + +class Player : public MovingObject +{ +public: + enum HurtMode { KILL, SHRINK }; + enum Power { NONE_POWER, FIRE_POWER, ICE_POWER }; + enum FallMode { ON_GROUND, JUMPING, TRAMPOLINE_JUMP, FALLING }; + + player_input_type input; + int got_power; + int size; + bool duck; + bool holding_something; + bool dead; + DyingType dying; + + Direction dir; + Direction old_dir; + + float last_ground_y; + FallMode fall_mode; + + bool on_ground_flag; + bool jumping; + bool flapping; + bool can_jump; + bool can_flap; + bool falling_from_flap; + bool enable_hover; + bool butt_jump; + + float flapping_velocity; + + // Ricardo's flapping + int flaps_nb; + + // temporary to help player's choosing a flapping + enum { MAREK_FLAP, RICARDO_FLAP, RYAN_FLAP, NONE_FLAP }; + int flapping_mode; + + Timer2 invincible_timer; + Timer2 skidding_timer; + Timer2 safe_timer; + Timer2 kick_timer; + Timer2 shooting_timer; // used to show the arm when Tux is shooting + Timer2 dying_timer; + Timer2 growing_timer; + Timer2 idle_timer; + Timer2 flapping_timer; + Physic physic; + +public: + Player(); + virtual ~Player(); + + int key_event(SDLKey key, int state); + void level_begin(); + void handle_input(); + void grabdistros(); + + PlayerStatus& get_status(); + + virtual void action(float elapsed_time); + virtual void draw(DrawingContext& context); + virtual HitResponse collision(GameObject& other, const CollisionHit& hit); + + void make_invincible(); + bool is_invincible() const + { + return invincible_timer.started(); + } + void kill(HurtMode mode); + void player_remove_powerups(); + void check_bounds(Camera* camera); + bool on_ground(); + bool under_solid(); + void grow(bool animate = false); + void move(const Vector& vector); + + void bounce(BadGuy& badguy); + + bool is_dead() const + { return dead; } + +private: + void init(); + + void handle_horizontal_input(); + void handle_vertical_input(); + void remove_powerups(); +}; + +#endif /*SUPERTUX_PLAYER_H*/ + +/* Local Variables: */ +/* mode:c++ */ +/* End: */ diff --git a/src/object/tilemap.cpp b/src/object/tilemap.cpp new file mode 100644 index 000000000..08784415c --- /dev/null +++ b/src/object/tilemap.cpp @@ -0,0 +1,274 @@ +// $Id$ +// +// SuperTux - A Jump'n Run +// Copyright (C) 2004 Matthias Braun + +#include +#include +#include +#include +#include + +#include "tilemap.h" +#include "video/drawing_context.h" +#include "level.h" +#include "tile.h" +#include "tile_manager.h" +#include "app/globals.h" +#include "utils/lispreader.h" +#include "utils/lispwriter.h" + +TileMap::TileMap() + : solid(false), speed(1), width(0), height(0), layer(LAYER_TILES), + vertical_flip(false) +{ + tilemanager = TileManager::instance(); + + if(solid) + flags |= FLAG_SOLID; +} + +TileMap::TileMap(LispReader& reader) + : solid(false), speed(1), width(0), height(0), layer(LAYER_TILES), + vertical_flip(false) +{ + tilemanager = TileManager::instance(); + + std::string layer_str; + if(reader.read_string("layer", layer_str)) { + if(layer_str == "background") + layer = LAYER_BACKGROUNDTILES; + else if(layer_str == "interactive") + layer = LAYER_TILES; + else if(layer_str == "foreground") + layer = LAYER_FOREGROUNDTILES; + else + std::cerr << "Unknown layer '" << layer_str << "' 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(solid) + flags |= FLAG_SOLID; + + 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(int layer_, bool solid_, size_t width_, size_t height_) + : solid(solid_), speed(1), width(0), height(0), layer(layer_), + vertical_flip(false) +{ + tilemanager = TileManager::instance(); + + resize(width_, height_); + + if(solid) + flags |= FLAG_SOLID; +} + +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 { + writer.write_string("layer", "unknown"); + std::cerr << "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(DrawingContext& context) +{ + context.push_transform(); + + if(vertical_flip) + context.set_drawing_effect(VERTICAL_FLIP); + float trans_x = roundf(context.get_translation().x); + float trans_y = roundf(context.get_translation().y); + context.set_translation(Vector(trans_x * speed, trans_y * speed)); + + /** 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(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 + int tsy = int(start_y / 32); // tilestartindex y + + Vector pos; + 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) { + const Tile* tile = tilemanager->get(tiles[ty*width + tx]); + assert(tile != 0); + tile->draw(context, pos, layer); + } + } + + if (debug_grid) + { + for (pos.x = start_x; pos.x < end_x; pos.x += 32) + { + context.draw_filled_rect(Vector (pos.x, start_y), Vector(1, fabsf(start_y - end_y)), + Color(225, 225, 225), LAYER_GUI-50); + } + + for (pos.y = start_y; pos.y < end_y; pos.y += 32) + { + context.draw_filled_rect(Vector (start_x, pos.y), Vector(fabsf(start_x - end_x), 1), + Color(225, 225, 225), LAYER_GUI-50); + } + } + + context.pop_transform(); +} + +void +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.resize(newt.size()); + tiles = newt; + + layer = newlayer; + solid = newsolid; + if(solid) + flags |= FLAG_SOLID; +} + +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; + continue; + } + + tiles[y * new_width + x] = tiles[y * width + x]; + } + } + } + + height = new_height; + width = new_width; +} + +void +TileMap::do_vertical_flip() +{ + // remap tiles vertically flipped + for(int y = 0; y < height / 2; ++y) { + for(int x = 0; x < width; ++x) { + std::swap(tiles[y*width + x], tiles[(((height-1)*width) - (y*width)) + x]); + } + } + + vertical_flip = true; +} + +const Tile* +TileMap::get_tile(int x, int y) const +{ + if(x < 0 || x >= width || y < 0 || y >= height) { +#ifdef DEBUG + //std::cout << "Warning: tile outside tilemap requested!\n"; +#endif + return tilemanager->get(0); + } + + return tilemanager->get(tiles[y*width + x]); +} + +const Tile* +TileMap::get_tile_at(const Vector& pos) const +{ + return get_tile(int(pos.x)/32, int(pos.y)/32); +} + +void +TileMap::change(int x, int y, uint32_t newtile) +{ + assert(x >= 0 && x < width && y >= 0 && y < height); + tiles[y*width + x] = newtile; +} + +void +TileMap::change_at(const Vector& pos, uint32_t newtile) +{ + change(int(pos.x)/32, int(pos.y)/32, newtile); +} diff --git a/src/object/tilemap.h b/src/object/tilemap.h new file mode 100644 index 000000000..8aa2b481a --- /dev/null +++ b/src/object/tilemap.h @@ -0,0 +1,103 @@ +// $Id$ +// +// SuperTux - A Jump'n Run +// Copyright (C) 2004 Matthias Braun +#include + +#include "special/game_object.h" +#include "serializable.h" +#include "math/vector.h" + +using namespace SuperTux; + +namespace SuperTux { +class LispReader; +} + +class Level; +class TileManager; +class Tile; + +/** + * This class is reponsible for drawing the level tiles + */ +class TileMap : public GameObject, public Serializable +{ +public: + TileMap(); + TileMap(LispReader& reader); + TileMap(int layer_, bool solid_, size_t width_, size_t height_); + virtual ~TileMap(); + + virtual void write(LispWriter& writer); + + virtual void action(float elapsed_time); + 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); + + /** Flip the all tile map vertically. The purpose of this is to let + player to play the same level in a different way :) */ + void do_vertical_flip(); + + size_t get_width() const + { return width; } + + size_t get_height() const + { return height; } + + int get_layer() const + { return layer; } + + bool is_solid() const + { return solid; } + + /// returns tile in row y and column y (of the tilemap) + const Tile* get_tile(int x, int y) const; + /// returns tile at position pos (in world coordinates) + const Tile* get_tile_at(const Vector& pos) const; + + void change(int x, int y, uint32_t newtile); + + void change_at(const Vector& pos, uint32_t newtile); + +private: + std::vector tiles; + +private: + TileManager* tilemanager; + bool solid; + float speed; + int width, height; + int layer; + + bool vertical_flip; +}; + +#endif /*SUPERTUX_TILEMAP_H*/ + diff --git a/src/particlesystem.cpp b/src/particlesystem.cpp deleted file mode 100644 index b82152a13..000000000 --- a/src/particlesystem.cpp +++ /dev/null @@ -1,176 +0,0 @@ -// $Id$ -// -// SuperTux -// Copyright (C) 2004 Matthias Braun -// -// 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 "particlesystem.h" -#include "app/globals.h" -#include "utils/lispreader.h" -#include "utils/lispwriter.h" -#include "video/drawing_context.h" - -ParticleSystem::ParticleSystem() -{ - virtual_width = screen->w; - virtual_height = screen->h; - layer = LAYER_BACKGROUND1; -} - -ParticleSystem::~ParticleSystem() -{ - std::vector::iterator i; - for(i = particles.begin(); i != particles.end(); ++i) { - delete *i; - } -} - -void ParticleSystem::draw(DrawingContext& context) -{ - float scrollx = context.get_translation().x; - float scrolly = context.get_translation().y; - - context.push_transform(); - context.set_translation(Vector(0,0)); - - std::vector::iterator i; - for(i = particles.begin(); i != particles.end(); ++i) { - Particle* particle = *i; - - // remap x,y coordinates onto screencoordinates - Vector pos; - pos.x = fmodf(particle->pos.x - scrollx, virtual_width); - if(pos.x < 0) pos.x += virtual_width; - pos.y = fmodf(particle->pos.y - scrolly, virtual_height); - if(pos.y < 0) pos.y += virtual_height; - - 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); - } - - context.pop_transform(); -} - -SnowParticleSystem::SnowParticleSystem() -{ - snowimages[0] = new Surface(datadir+"/images/shared/snow0.png", true); - snowimages[1] = new Surface(datadir+"/images/shared/snow1.png", true); - snowimages[2] = new Surface(datadir+"/images/shared/snow2.png", true); - - virtual_width = screen->w * 2; - - // create some random snowflakes - size_t snowflakecount = size_t(virtual_width/10.0); - for(size_t i=0; ipos.x = rand() % int(virtual_width); - particle->pos.y = rand() % screen->h; - int snowsize = rand() % 3; - particle->texture = snowimages[snowsize]; - do { - particle->speed = snowsize*.2 + (float(rand()%10)*.4); - } while(particle->speed < 1); - particle->speed *= 10; // 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) - delete snowimages[i]; -} - -void SnowParticleSystem::action(float elapsed_time) -{ - std::vector::iterator i; - for(i = particles.begin(); i != particles.end(); ++i) { - SnowParticle* particle = (SnowParticle*) *i; - particle->pos.y += particle->speed * elapsed_time; - if(particle->pos.y > screen->h) { - particle->pos.y = fmodf(particle->pos.y , virtual_height); - particle->pos.x = rand() % int(virtual_width); - } - } -} - -CloudParticleSystem::CloudParticleSystem() -{ - cloudimage = new Surface(datadir + "/images/shared/cloud.png", true); - - virtual_width = 2000.0; - - // create some random clouds - for(size_t i=0; i<15; ++i) { - CloudParticle* particle = new CloudParticle; - particle->pos.x = rand() % int(virtual_width); - particle->pos.y = rand() % int(virtual_height); - particle->texture = cloudimage; - particle->speed = -float(25 + rand() % 30); - - particles.push_back(particle); - } -} - -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; -} - -void CloudParticleSystem::action(float elapsed_time) -{ - std::vector::iterator i; - for(i = particles.begin(); i != particles.end(); ++i) { - CloudParticle* particle = (CloudParticle*) *i; - particle->pos.x += particle->speed * elapsed_time; - } -} diff --git a/src/particlesystem.h b/src/particlesystem.h deleted file mode 100644 index d11c5c13d..000000000 --- a/src/particlesystem.h +++ /dev/null @@ -1,126 +0,0 @@ -// $Id$ -// -// SuperTux -// Copyright (C) 2004 Matthias Braun -// -// 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_PARTICLESYSTEM_H -#define SUPERTUX_PARTICLESYSTEM_H - -#include - -#include "video/surface.h" -#include "special/game_object.h" -#include "serializable.h" - -using namespace SuperTux; - -namespace SuperTux { -class LispReader; -} - -class DisplayManager; - -/** - * This is the base class for particle systems. It is responsible for storing a - * set of particles with each having an x- and y-coordinate the number of the - * layer where it should be drawn and a texture. - * The coordinate system used here is a virtual one. It would be a bad idea to - * populate whole levels with particles. So we're using a virtual rectangle - * here that is tiled onto the level when drawing. This rectangle has the size - * (virtual_width, virtual_height). We're using modulo on the particle - * coordinates, so when a particle leaves left, it'll reenter at the right - * side. - * - * Classes that implement a particle system should subclass from this class, - * initialize particles in the constructor and move them in the simulate - * function. - */ -class ParticleSystem : public GameObject -{ -public: - ParticleSystem(); - virtual ~ParticleSystem(); - - virtual void draw(DrawingContext& context); - -protected: - int layer; - - class Particle - { - public: - virtual ~Particle() - { } - - Vector pos; - Surface* texture; - }; - - std::vector particles; - float virtual_width, virtual_height; -}; - -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 - { return "SnowParticleSystem"; } - -private: - class SnowParticle : public Particle - { - public: - float speed; - }; - - Surface* snowimages[3]; -}; - -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 - { return "SnowParticleSystem"; } - -private: - class CloudParticle : public Particle - { - public: - float speed; - }; - - Surface* cloudimage; -}; - -#endif - diff --git a/src/player.cpp b/src/player.cpp deleted file mode 100644 index 6608f8c48..000000000 --- a/src/player.cpp +++ /dev/null @@ -1,1018 +0,0 @@ -// $Id$ -// -// SuperTux - A Jump'n Run -// Copyright (C) 2003 Tobias Glaesser -// -// 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 - -#include "app/globals.h" -#include "app/gettext.h" -#include "player.h" -#include "defines.h" -#include "scene.h" -#include "tile.h" -#include "special/sprite.h" -#include "sector.h" -#include "tilemap.h" -#include "camera.h" -#include "gameobjs.h" -#include "resources.h" -#include "video/screen.h" -#include "statistics.h" -#include "gameloop.h" -#include "trigger/trigger_base.h" - -static const int TILES_FOR_BUTTJUMP = 3; -static const float SHOOTING_TIME = .150; -/// time before idle animation starts -static const float IDLE_TIME = 2.5; - -static const float WALK_ACCELERATION_X = 300; -static const float RUN_ACCELERATION_X = 400; -static const float SKID_XM = 200; -static const float SKID_TIME = .3; -static const float MAX_WALK_XM = 230; -static const float MAX_RUN_XM = 320; -static const float WALK_SPEED = 100; - -// growing animation -Surface* growingtux_left[GROWING_FRAMES]; -Surface* growingtux_right[GROWING_FRAMES]; - -Surface* tux_life = 0; - -Sprite* smalltux_gameover = 0; -Sprite* smalltux_star = 0; -Sprite* bigtux_star = 0; - -TuxBodyParts* small_tux = 0; -TuxBodyParts* big_tux = 0; -TuxBodyParts* fire_tux = 0; -TuxBodyParts* ice_tux = 0; - -PlayerKeymap keymap; - -PlayerKeymap::PlayerKeymap() -{ - keymap.up = SDLK_UP; - keymap.down = SDLK_DOWN; - keymap.left = SDLK_LEFT; - keymap.right = SDLK_RIGHT; - - keymap.power = SDLK_LCTRL; - keymap.jump = SDLK_SPACE; -} - -void player_input_init(player_input_type* pplayer_input) -{ - pplayer_input->up = UP; - pplayer_input->old_up = UP; - pplayer_input->down = UP; - pplayer_input->fire = UP; - pplayer_input->left = UP; - pplayer_input->old_fire = UP; - pplayer_input->right = UP; - pplayer_input->jump = UP; - pplayer_input->old_jump = UP; - pplayer_input->activate = UP; -} - -void -TuxBodyParts::set_action(std::string action, int loops) -{ - if(head != NULL) - head->set_action(action, loops); - if(body != NULL) - body->set_action(action, loops); - if(arms != NULL) - arms->set_action(action, loops); - if(feet != NULL) - feet->set_action(action, loops); -} - -void -TuxBodyParts::draw(DrawingContext& context, const Vector& pos, int layer, - Uint32 drawing_effect) -{ - if(head != NULL) - head->draw(context, pos, layer-1, drawing_effect); - if(body != NULL) - body->draw(context, pos, layer-3, drawing_effect); - if(arms != NULL) - arms->draw(context, pos, layer, drawing_effect); - if(feet != NULL) - feet->draw(context, pos, layer-2, drawing_effect); -} - -Player::Player() -{ - init(); -} - -Player::~Player() -{ -} - -void -Player::init() -{ - holding_something = false; - - bbox.set_size(31.8, 31.8); - - size = SMALL; - got_power = NONE_POWER; - - dir = RIGHT; - old_dir = dir; - duck = false; - dead = false; - - dying = DYING_NOT; - last_ground_y = 0; - fall_mode = ON_GROUND; - jumping = false; - flapping = false; - can_jump = true; - can_flap = false; - falling_from_flap = false; - enable_hover = false; - butt_jump = false; - - flapping_velocity = 0; - - // temporary to help player's choosing a flapping - flapping_mode = MAREK_FLAP; - - // Ricardo's flapping - flaps_nb = 0; - - on_ground_flag = false; - - player_input_init(&input); - - physic.reset(); -} - -int -Player::key_event(SDLKey key, int state) -{ - idle_timer.start(IDLE_TIME, true); - - if(key == keymap.right) - { - input.right = state; - return true; - } - else if(key == keymap.left) - { - input.left = state; - return true; - } - else if(key == keymap.up) - { - if(state == UP) - input.old_up = UP; - input.up = state; - /* Up key also opens activates stuff */ - input.activate = state; - return true; - } - else if(key == keymap.down) - { - input.down = state; - return true; - } - else if(key == keymap.power) - { - if (state == UP) - input.old_fire = UP; - input.fire = state; - - return true; - } - else if(key == keymap.jump) - { - if (state == UP) - input.old_jump = UP; - input.jump = state; - return true; - } - else - return false; -} - -void -Player::level_begin() -{ - move(Vector(100, 170)); - duck = false; - - dying = DYING_NOT; - - player_input_init(&input); - - on_ground_flag = false; - - physic.reset(); -} - -PlayerStatus& -Player::get_status() -{ - return player_status; -} - -void -Player::action(float elapsed_time) -{ - if(dying && dying_timer.check()) { - dead = true; - return; - } - - if (input.fire == UP) - holding_something = false; - - if(dying == DYING_NOT) - handle_input(); - - movement = physic.get_movement(elapsed_time); - -#if 0 - // special exception for cases where we're stuck under tiles after - // being ducked. In this case we drift out - if(!duck && on_ground() && old_base.x == base.x && old_base.y == base.y - && collision_object_map(base)) - { - base.x += elapsed_time * WALK_SPEED * (dir ? 1: -1); - previous_base = old_base = base; - } - - /* Reset score multiplier (for multi-hits): */ - if (!invincible_timer.started()) - { - if(player_status.score_multiplier > player_status.max_score_multiplier) - { - player_status.max_score_multiplier = player_status.score_multiplier; - - // show a message - char str[124]; - sprintf(str, _("New max combo: %d"), player_status.max_score_multiplier-1); - Sector::current()->add_floating_text(base, str); - } - player_status.score_multiplier = 1; - } - } - - } -#endif - - on_ground_flag = false; -} - -bool -Player::on_ground() -{ - return on_ground_flag; -} - -void -Player::handle_horizontal_input() -{ - float vx = physic.get_velocity_x(); - float vy = physic.get_velocity_y(); - float ax = physic.get_acceleration_x(); - float ay = physic.get_acceleration_y(); - - float dirsign = 0; - if(input.left == DOWN && input.right == UP && (!duck || physic.get_velocity_y() != 0)) { - old_dir = dir; - dir = LEFT; - dirsign = -1; - } else if(input.left == UP && input.right == DOWN && (!duck || physic.get_velocity_y() != 0)) { - old_dir = dir; - dir = RIGHT; - dirsign = 1; - } - - if (input.fire == UP) { - ax = dirsign * WALK_ACCELERATION_X; - // limit speed - if(vx >= MAX_WALK_XM && dirsign > 0) { - vx = MAX_WALK_XM; - ax = 0; - } else if(vx <= -MAX_WALK_XM && dirsign < 0) { - vx = -MAX_WALK_XM; - ax = 0; - } - } else { - ax = dirsign * RUN_ACCELERATION_X; - // limit speed - if(vx >= MAX_RUN_XM && dirsign > 0) { - vx = MAX_RUN_XM; - ax = 0; - } else if(vx <= -MAX_RUN_XM && dirsign < 0) { - vx = -MAX_RUN_XM; - ax = 0; - } - } - - // we can reach WALK_SPEED without any acceleration - if(dirsign != 0 && fabs(vx) < WALK_SPEED) { - vx = dirsign * WALK_SPEED; - } - - // changing directions? - if(on_ground() && ((vx < 0 && dirsign >0) || (vx>0 && dirsign<0))) - { - // let's skid! - if(fabs(vx)>SKID_XM && !skidding_timer.started()) - { - skidding_timer.start(SKID_TIME); - SoundManager::get()->play_sound(IDToSound(SND_SKID)); - // dust some partcles - Sector::current()->add_object( - new Particles( - Vector(bbox.p1.x + (dir == RIGHT ? bbox.get_width() : 0), - bbox.p2.y), - dir == RIGHT ? 270+20 : 90-40, dir == RIGHT ? 270+40 : 90-20, - Vector(280,-260), Vector(0,0.030), 3, Color(100,100,100), 3, .8, - LAYER_OBJECTS+1)); - - ax *= 2.5; - } - else - { - ax *= 2; - } - } - - // we get slower when not pressing any keys - if(dirsign == 0) { - if(fabs(vx) < WALK_SPEED) { - vx = 0; - ax = 0; - } else if(vx < 0) { - ax = WALK_ACCELERATION_X * 1.5; - } else { - ax = WALK_ACCELERATION_X * -1.5; - } - } - -#if 0 - // if we're on ice slow down acceleration or deceleration - if (isice(base.x, base.y + base.height)) - { - /* the acceleration/deceleration rate on ice is inversely proportional to - * the current velocity. - */ - - // increasing 1 will increase acceleration/deceleration rate - // decreasing 1 will decrease acceleration/deceleration rate - // must stay above zero, though - if (ax != 0) ax *= 1 / fabs(vx); - } -#endif - - // extend/shrink tux collision rectangle so that we fall through/walk over 1 - // tile holes - if(fabsf(vx) > MAX_WALK_XM) { - bbox.set_width(33); - } else { - bbox.set_width(31.8); - } - - physic.set_velocity(vx, vy); - physic.set_acceleration(ax, ay); -} - -void -Player::handle_vertical_input() -{ - // set fall mode... - if(on_ground()) { - fall_mode = ON_GROUND; - last_ground_y = get_pos().y; - } else { - if(get_pos().y > last_ground_y) - fall_mode = FALLING; - else if(fall_mode == ON_GROUND) - fall_mode = JUMPING; - } - - if(on_ground()) { /* Make sure jumping is off. */ - jumping = false; - flapping = false; - falling_from_flap = false; - if (flapping_timer.started()) { - flapping_timer.start(0); - } - - physic.set_acceleration_y(0); //for flapping - } - - // Press jump key - if(input.jump == DOWN && can_jump && on_ground()) - { - if(duck) { // only jump a little bit when in duck mode { - physic.set_velocity_y(300); - } else { - // jump higher if we are running - if (fabs(physic.get_velocity_x()) > MAX_WALK_XM) - physic.set_velocity_y(580); - else - physic.set_velocity_y(520); - } - - //bbox.move(Vector(0, -1)); - jumping = true; - flapping = false; - can_jump = false; - can_flap = false; - flaps_nb = 0; // Ricardo's flapping - if (size == SMALL) - SoundManager::get()->play_sound(IDToSound(SND_JUMP)); - else - SoundManager::get()->play_sound(IDToSound(SND_BIGJUMP)); - } - // Let go of jump key - else if(input.jump == UP) - { - if (!flapping && !duck && !falling_from_flap && !on_ground()) - { - can_flap = true; - } - if (jumping && physic.get_velocity_y() > 0) - { - jumping = false; - physic.set_velocity_y(0); - } - } - - // temporary to help player's choosing a flapping - if(flapping_mode == RICARDO_FLAP) - { - // Flapping, Ricardo's version - // similar to SM3 Fox - if(input.jump == DOWN && input.old_jump == UP && can_flap && - flaps_nb < 3) - { - physic.set_velocity_y(350); - physic.set_velocity_x(physic.get_velocity_x() * 35); - flaps_nb++; - } - } - else if(flapping_mode == MAREK_FLAP) - { - // Flapping, Marek's version - if (input.jump == DOWN && can_flap) - { - if (!flapping_timer.started()) - { - flapping_timer.start(TUX_FLAPPING_TIME); - flapping_velocity = physic.get_velocity_x(); - } - if (flapping_timer.check()) - { - can_flap = false; - falling_from_flap = true; - } - jumping = true; - flapping = true; - if (!flapping_timer.check()) { - float cv = flapping_velocity * sqrt( - TUX_FLAPPING_TIME - flapping_timer.get_timegone() - / TUX_FLAPPING_TIME); - - //Handle change of direction while flapping - if (((dir == LEFT) && (cv > 0)) || (dir == RIGHT) && (cv < 0)) { - cv *= (-1); - } - physic.set_velocity_x(cv); - physic.set_velocity_y( - flapping_timer.get_timegone()/.850); - } - } - } - else if(flapping_mode == RYAN_FLAP) - { - // Flapping, Ryan's version - if (input.jump == DOWN && can_flap) - { - if (!flapping_timer.started()) - { - flapping_timer.start(TUX_FLAPPING_TIME); - } - if (flapping_timer.check()) - { - can_flap = false; - falling_from_flap = true; - } - jumping = true; - flapping = true; - if (flapping && flapping_timer.get_timegone() <= TUX_FLAPPING_TIME - && physic.get_velocity_y() < 0) - { - float gravity = Sector::current()->gravity; - (void)gravity; - float xr = (fabsf(physic.get_velocity_x()) / MAX_RUN_XM); - - // XXX: magic numbers. should be a percent of gravity - // gravity is (by default) -0.1f - physic.set_acceleration_y(12 + 1*xr); - -#if 0 - // To slow down x-vel when flapping (not working) - if (fabsf(physic.get_velocity_x()) > MAX_WALK_XM) - { - if (physic.get_velocity_x() < 0) - physic.set_acceleration_x(1.0f); - else if (physic.get_velocity_x() > 0) - physic.set_acceleration_x(-1.0f); - } -#endif - } - } - else - { - physic.set_acceleration_y(0); - } - } - - // Hover - //(disabled by default, use cheat code "hover" to toggle on/off) - //TODO: needs some tweaking, especially when used together with double jump and jumping off badguys - if (enable_hover && input.jump == DOWN && !jumping && !butt_jump && physic.get_velocity_y() <= 0) - { - physic.set_velocity_y(-100); - } - -#if 0 - /* In case the player has pressed Down while in a certain range of air, - enable butt jump action */ - if (input.down == DOWN && !butt_jump && !duck) - if(tiles_on_air(TILES_FOR_BUTTJUMP) && jumping) - butt_jump = true; -#endif - - /* When Down is not held anymore, disable butt jump */ - if(butt_jump && input.down == UP) - butt_jump = false; - - // Do butt jump - if (butt_jump && on_ground() && size == BIG) - { - // Add a smoke cloud - if (duck) - Sector::current()->add_smoke_cloud(Vector(get_pos().x - 32, get_pos().y)); - else - Sector::current()->add_smoke_cloud( - Vector(get_pos().x - 32, get_pos().y + 32)); - - butt_jump = false; - -#if 0 - // Break bricks beneath Tux - 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; - } -#endif - -#if 0 - // Kill nearby badguys - std::vector gameobjects = Sector::current()->gameobjects; - for (std::vector::iterator i = gameobjects.begin(); - i != gameobjects.end(); - i++) - { - BadGuy* badguy = dynamic_cast (*i); - if(badguy) - { - // don't kill when badguys are already dying or in a certain mode - if(badguy->dying == DYING_NOT && badguy->mode != BadGuy::BOMB_TICKING && - badguy->mode != BadGuy::BOMB_EXPLODE) - { - if (fabsf(base.x - badguy->base.x) < 96 && - fabsf(base.y - badguy->base.y) < 64) - badguy->kill_me(25); - } - } - } -#endif - } - - /** jumping is only allowed if we're about to touch ground soon and if the - * button has been up in between the last jump - */ - // FIXME -#if 0 - if ( (issolid(get_pos().x + bbox.get_width() / 2, - get_pos().y + bbox.get_height() + 64) || - issolid(get_pos().x + 1, get_pos().y + bbox.get_height() + 64) || - issolid(get_pos().x + bbox.get_width() - 1, - get_pos().y + bbox.get_height() + 64)) - && jumping == false - && can_jump == false - && input.jump == DOWN - && input.old_jump == UP) - { - can_jump = true; - } -#endif - - input.old_jump = input.jump; -} - -void -Player::handle_input() -{ - /* Handle horizontal movement: */ - handle_horizontal_input(); - - /* Jump/jumping? */ - if (on_ground() && input.jump == UP) - can_jump = true; - handle_vertical_input(); - - /* Shoot! */ - if (input.fire == DOWN && input.old_fire == UP && got_power != NONE_POWER) { - if(Sector::current()->add_bullet( - get_pos() + Vector(0, bbox.get_height()/2), - physic.get_velocity_x(), dir)) - shooting_timer.start(SHOOTING_TIME); - input.old_fire = DOWN; - } - - /* Duck! */ - if (input.down == DOWN && size == BIG && !duck - && physic.get_velocity_y() == 0 && on_ground()) - { - duck = true; - bbox.move(Vector(0, 32)); - bbox.set_height(31.8); - } - else if(input.down == UP && size == BIG && duck) - { - // try if we can really unduck - bbox.move(Vector(0, -32)); - bbox.set_height(63.8); - duck = false; - // FIXME -#if 0 - // when unducking in air we need some space to do so - if(on_ground() || !collision_object_map(bbox)) { - duck = false; - } else { - // undo the ducking changes - bbox.move(Vector(0, 32)); - bbox.set_height(31.8); - } -#endif - } -} - -void -Player::grow(bool animate) -{ - if(size == BIG) - return; - - size = BIG; - bbox.set_height(63.8); - bbox.move(Vector(0, -32)); - - if(animate) - growing_timer.start(GROWING_TIME); -} - -void -Player::grabdistros() -{ -} - -void -Player::draw(DrawingContext& context) -{ - TuxBodyParts* tux_body; - - if (size == SMALL) - tux_body = small_tux; - else if (got_power == FIRE_POWER) - tux_body = fire_tux; - else if (got_power == ICE_POWER) - tux_body = ice_tux; - else - tux_body = big_tux; - - int layer = LAYER_OBJECTS + 10; - - /* Set Tux sprite action */ - if (duck && size == BIG) - { - if(dir == LEFT) - tux_body->set_action("duck-left"); - else // dir == RIGHT - tux_body->set_action("duck-right"); - } - else if (skidding_timer.started() && !skidding_timer.check()) - { - if(dir == LEFT) - tux_body->set_action("skid-left"); - else // dir == RIGHT - tux_body->set_action("skid-right"); - } - else if (kick_timer.started() && !kick_timer.check()) - { - if(dir == LEFT) - tux_body->set_action("kick-left"); - else // dir == RIGHT - tux_body->set_action("kick-right"); - } - else if (butt_jump && size == BIG) - { - if(dir == LEFT) - tux_body->set_action("buttjump-left"); - else // dir == RIGHT - tux_body->set_action("buttjump-right"); - } - else if (physic.get_velocity_y() != 0) - { - if(dir == LEFT) - tux_body->set_action("jump-left"); - else // dir == RIGHT - tux_body->set_action("jump-right"); - } - else - { - if (fabsf(physic.get_velocity_x()) < 1.0f) // standing - { - if(dir == LEFT) - tux_body->set_action("stand-left"); - else // dir == RIGHT - tux_body->set_action("stand-right"); - } - else // moving - { - if(dir == LEFT) - tux_body->set_action("walk-left"); - else // dir == RIGHT - tux_body->set_action("walk-right"); - } - } - - if(idle_timer.check()) - { - if(size == BIG) - { - if(dir == LEFT) - tux_body->head->set_action("idle-left", 1); - else // dir == RIGHT - tux_body->head->set_action("idle-right", 1); - } - - } - - // Tux is holding something - if ((holding_something && physic.get_velocity_y() == 0) || - (shooting_timer.get_timeleft() > 0 && !shooting_timer.check())) - { - if (duck) - { - if(dir == LEFT) - tux_body->arms->set_action("duck+grab-left"); - else // dir == RIGHT - tux_body->arms->set_action("duck+grab-right"); - } - else - { - if(dir == LEFT) - tux_body->arms->set_action("grab-left"); - else // dir == RIGHT - tux_body->arms->set_action("grab-right"); - } - } - - /* Draw Tux */ - if (dying == DYING_SQUISHED) { - smalltux_gameover->draw(context, get_pos(), LAYER_FOREGROUNDTILES+1); - } else if(growing_timer.get_timeleft() > 0) { - if(size == SMALL) - { - if (dir == RIGHT) - context.draw_surface(growingtux_right[GROWING_FRAMES-1 - - int((growing_timer.get_timegone() * - GROWING_FRAMES) / GROWING_TIME)], get_pos(), layer); - else - context.draw_surface(growingtux_left[GROWING_FRAMES-1 - - int((growing_timer.get_timegone() * - GROWING_FRAMES) / GROWING_TIME)], get_pos(), layer); - } - else - { - if (dir == RIGHT) - context.draw_surface(growingtux_right[ - int((growing_timer.get_timegone() * - GROWING_FRAMES) / GROWING_TIME)], get_pos(), layer); - else - context.draw_surface(growingtux_left[ - int((growing_timer.get_timegone() * - GROWING_FRAMES) / GROWING_TIME)], - get_pos(), layer); - } - } - else if (safe_timer.started() && size_t(global_time*40)%2) - ; // don't draw Tux - else - tux_body->draw(context, get_pos(), layer); - - // Draw blinking star overlay - if (invincible_timer.started() && - (invincible_timer.get_timeleft() > TUX_INVINCIBLE_TIME_WARNING - || size_t(global_time*20)%2) - && !dying) - { - if (size == SMALL || duck) - smalltux_star->draw(context, get_pos(), layer + 5); - else - bigtux_star->draw(context, get_pos(), layer + 5); - } - - if (debug_mode) - context.draw_filled_rect(get_pos(), - Vector(bbox.get_width(), bbox.get_height()), - Color(75,75,75, 150), LAYER_OBJECTS+20); -} - -HitResponse -Player::collision(GameObject& other, const CollisionHit& hit) -{ - if(other.get_flags() & FLAG_SOLID) { - if(hit.normal.y < 0) { // landed on floor? - if (physic.get_velocity_y() < 0) - physic.set_velocity_y(0); - on_ground_flag = true; - } else if(hit.normal.y > 0) { // bumped against the roof - physic.set_velocity_y(.1); - } - - if(fabsf(hit.normal.x) > .9) { // hit on the side? - physic.set_velocity_x(0); - } - - return CONTINUE; - } - - TriggerBase* trigger = dynamic_cast (&other); - if(trigger) { - if(input.up == DOWN && input.old_up == UP) - trigger->event(*this, TriggerBase::EVENT_ACTIVATE); - } - - return FORCE_MOVE; -} - -void -Player::make_invincible() -{ - SoundManager::get()->play_sound(IDToSound(SND_HERRING)); - invincible_timer.start(TUX_INVINCIBLE_TIME); - Sector::current()->play_music(HERRING_MUSIC); -} - -/* Kill Player! */ -void -Player::kill(HurtMode mode) -{ - if(dying) - return; - - if(safe_timer.get_timeleft() > 0 || invincible_timer.get_timeleft() > 0) - return; - - SoundManager::get()->play_sound(IDToSound(SND_HURT)); - - physic.set_velocity_x(0); - - if (mode == SHRINK && size == BIG) - { - if (got_power != NONE_POWER) - { - safe_timer.start(TUX_SAFE_TIME); - got_power = NONE_POWER; - } - else - { - growing_timer.start(GROWING_TIME); - safe_timer.start(TUX_SAFE_TIME + GROWING_TIME); - size = SMALL; - bbox.set_height(31.8); - duck = false; - } - } - else - { - physic.enable_gravity(true); - physic.set_acceleration(0, 0); - physic.set_velocity(0, 700); - --player_status.lives; - dying = DYING_SQUISHED; - dying_timer.start(3.0); - flags |= FLAG_NO_COLLDET; - } -} - -/* Remove Tux's power ups */ -void -Player::remove_powerups() -{ - got_power = NONE_POWER; - size = SMALL; - bbox.set_height(31.8); -} - -void -Player::move(const Vector& vector) -{ - bbox.set_pos(vector); -} - -void -Player::check_bounds(Camera* camera) -{ - /* Keep tux in bounds: */ - if (get_pos().x < 0) - { // Lock Tux to the size of the level, so that he doesn't fall of - // on the left side - bbox.set_pos(Vector(0, get_pos().y)); - } - - /* Keep in-bounds, vertically: */ - if (get_pos().y > Sector::current()->solids->get_height() * 32) - { - kill(KILL); - return; - } - - bool adjust = false; - // can happen if back scrolling is disabled - if(get_pos().x < camera->get_translation().x) { - bbox.set_pos(Vector(camera->get_translation().x, get_pos().y)); - adjust = true; - } - if(get_pos().x >= camera->get_translation().x + screen->w - bbox.get_width()) - { - bbox.set_pos(Vector( - camera->get_translation().x + screen->w - bbox.get_width(), - get_pos().y)); - adjust = true; - } - - if(adjust) { - // FIXME -#if 0 - // squished now? - if(collision_object_map(bbox)) { - kill(KILL); - return; - } -#endif - } -} - -void -Player::bounce(BadGuy& ) -{ - //Make sure we stopped flapping - flapping = false; - falling_from_flap = false; - - if (input.jump) - physic.set_velocity_y(520); - else - physic.set_velocity_y(200); -} - diff --git a/src/player.h b/src/player.h deleted file mode 100644 index be6398cba..000000000 --- a/src/player.h +++ /dev/null @@ -1,224 +0,0 @@ -// $Id$ -// -// SuperTux - A Jump'n Run -// Copyright (C) 2003 Tobias Glaesser -// -// 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_PLAYER_H -#define SUPERTUX_PLAYER_H - -#include "SDL.h" - -#include "timer.h" -#include "video/surface.h" -#include "special/moving_object.h" -#include "special/sprite.h" -#include "math/physic.h" -#include "defines.h" - -using namespace SuperTux; - -class BadGuy; - -/* Times: */ - -#define TUX_SAFE_TIME 1.250 -#define TUX_INVINCIBLE_TIME 10.0 -#define TUX_INVINCIBLE_TIME_WARNING 2.0 -#define TUX_FLAPPING_TIME 1 /* How long Tux can flap his wings to gain additional jump height */ -#define TIME_WARNING 20 /* When to alert player they're low on time! */ - -/* Scores: */ - -#define SCORE_BRICK 5 -#define SCORE_DISTRO 20 - -/* Sizes: */ - -#define SMALL 0 -#define BIG 1 - -#include - -struct PlayerKeymap -{ -public: - int jump; - int up; - int down; - int left; - int right; - int power; - - PlayerKeymap(); -}; - -extern PlayerKeymap keymap; - -struct player_input_type -{ - int right; - int left; - int up; - int old_up; - int down; - int fire; - int old_fire; - int activate; - int jump; - int old_jump; -}; - -void player_input_init(player_input_type* pplayer_input); - -class Camera; -class PlayerStatus; - -extern Surface* tux_life; - -extern Sprite* smalltux_gameover; -extern Sprite* smalltux_star; -extern Sprite* bigtux_star; - -#define GROWING_TIME 1.0 -#define GROWING_FRAMES 7 -extern Surface* growingtux_left[GROWING_FRAMES]; -extern Surface* growingtux_right[GROWING_FRAMES]; - -class TuxBodyParts -{ -public: - TuxBodyParts() - : head(0), body(0), arms(0), feet(0) - { } - ~TuxBodyParts() { - delete head; - delete body; - delete arms; - delete feet; - } - - void set_action(std::string action, int loops = -1); - void one_time_animation(); - void draw(DrawingContext& context, const Vector& pos, int layer, - Uint32 drawing_effect = NONE_EFFECT); - - Sprite* head; - Sprite* body; - Sprite* arms; - Sprite* feet; -}; - -extern TuxBodyParts* small_tux; -extern TuxBodyParts* big_tux; -extern TuxBodyParts* fire_tux; -extern TuxBodyParts* ice_tux; - -class Player : public MovingObject -{ -public: - enum HurtMode { KILL, SHRINK }; - enum Power { NONE_POWER, FIRE_POWER, ICE_POWER }; - enum FallMode { ON_GROUND, JUMPING, TRAMPOLINE_JUMP, FALLING }; - - player_input_type input; - int got_power; - int size; - bool duck; - bool holding_something; - bool dead; - DyingType dying; - - Direction dir; - Direction old_dir; - - float last_ground_y; - FallMode fall_mode; - - bool on_ground_flag; - bool jumping; - bool flapping; - bool can_jump; - bool can_flap; - bool falling_from_flap; - bool enable_hover; - bool butt_jump; - - float flapping_velocity; - - // Ricardo's flapping - int flaps_nb; - - // temporary to help player's choosing a flapping - enum { MAREK_FLAP, RICARDO_FLAP, RYAN_FLAP, NONE_FLAP }; - int flapping_mode; - - Timer2 invincible_timer; - Timer2 skidding_timer; - Timer2 safe_timer; - Timer2 kick_timer; - Timer2 shooting_timer; // used to show the arm when Tux is shooting - Timer2 dying_timer; - Timer2 growing_timer; - Timer2 idle_timer; - Timer2 flapping_timer; - Physic physic; - -public: - Player(); - virtual ~Player(); - - int key_event(SDLKey key, int state); - void level_begin(); - void handle_input(); - void grabdistros(); - - PlayerStatus& get_status(); - - virtual void action(float elapsed_time); - virtual void draw(DrawingContext& context); - virtual HitResponse collision(GameObject& other, const CollisionHit& hit); - - void make_invincible(); - bool is_invincible() const - { - return invincible_timer.started(); - } - void kill(HurtMode mode); - void player_remove_powerups(); - void check_bounds(Camera* camera); - bool on_ground(); - bool under_solid(); - void grow(bool animate = false); - void move(const Vector& vector); - - void bounce(BadGuy& badguy); - - bool is_dead() const - { return dead; } - -private: - void init(); - - void handle_horizontal_input(); - void handle_vertical_input(); - void remove_powerups(); -}; - -#endif /*SUPERTUX_PLAYER_H*/ - -/* Local Variables: */ -/* mode:c++ */ -/* End: */ diff --git a/src/resources.cpp b/src/resources.cpp index da929586f..5267f4b8a 100644 --- a/src/resources.cpp +++ b/src/resources.cpp @@ -25,9 +25,9 @@ #include "gui/menu.h" #include "gui/button.h" #include "scene.h" -#include "player.h" -#include "gameobjs.h" #include "resources.h" +#include "object/gameobjs.h" +#include "object/player.h" Surface* img_waves[3]; Surface* img_water; diff --git a/src/sector.cpp b/src/sector.cpp index fedeee512..98731009a 100644 --- a/src/sector.cpp +++ b/src/sector.cpp @@ -29,12 +29,12 @@ #include "app/globals.h" #include "sector.h" #include "utils/lispreader.h" -#include "gameobjs.h" -#include "camera.h" -#include "background.h" -#include "particlesystem.h" +#include "object/gameobjs.h" +#include "object/camera.h" +#include "object/background.h" +#include "object/particlesystem.h" +#include "object/tilemap.h" #include "tile.h" -#include "tilemap.h" #include "audio/sound_manager.h" #include "gameloop.h" #include "resources.h" @@ -69,13 +69,13 @@ Sector::Sector() song_title = "Mortimers_chipdisko.mod"; player = new Player(); add_object(player); - - printf("seccreated: %p.\n", this); } Sector::~Sector() { - printf("secdel: %p.\n", this); + update_game_objects(); + assert(gameobjects_new.size() == 0); + for(GameObjects::iterator i = gameobjects.begin(); i != gameobjects.end(); ++i) { delete *i; @@ -89,23 +89,6 @@ Sector::~Sector() _current = 0; } -Sector *Sector::create(const std::string& name, size_t width, size_t height) -{ - Sector *sector = new Sector; - sector->name = name; - TileMap *background = new TileMap(LAYER_BACKGROUNDTILES, false, width, height); - TileMap *interactive = new TileMap(LAYER_TILES, true, width, height); - TileMap *foreground = new TileMap(LAYER_FOREGROUNDTILES, false, width, height); - sector->add_object(background); - sector->add_object(interactive); - sector->add_object(foreground); - sector->solids = interactive; - sector->camera = new Camera(sector); - sector->add_object(sector->camera); - sector->update_game_objects(); - return sector; -} - GameObject* Sector::parse_object(const std::string& name, LispReader& reader) { @@ -151,14 +134,6 @@ Sector::parse_object(const std::string& name, LispReader& reader) } else if(name == "nolok_01") { return new Nolok_01(reader); } -#if 0 - else if(badguykind_from_string(name) != BAD_INVALID) { - return new BadGuy(badguykind_from_string(name), reader); - } else if(name == "trampoline") { - return new Trampoline(reader); - } else if(name == "flying-platform") { - return new FlyingPlatform(reader); -#endif std::cerr << "Unknown object type '" << name << "'.\n"; return 0; @@ -197,6 +172,9 @@ Sector::parse(LispReader& lispreader) } } + update_game_objects(); + fix_old_tiles(); + update_game_objects(); if(!camera) { std::cerr << "sector '" << name << "' does not contain a camera.\n"; camera = new Camera(this); @@ -237,11 +215,11 @@ Sector::parse_old_format(LispReader& reader) bkgd_bottom.blue = b; if(backgroundimage != "") { - background = new Background; + Background* background = new Background; background->set_image(backgroundimage, bgspeed); add_object(background); } else { - background = new Background; + Background* background = new Background; background->set_gradient(bkgd_top, bkgd_bottom); add_object(background); } @@ -275,10 +253,7 @@ Sector::parse_old_format(LispReader& reader) || reader.read_int_vector("tilemap", tiles)) { TileMap* tilemap = new TileMap(); tilemap->set(width, height, tiles, LAYER_TILES, true); - solids = tilemap; add_object(tilemap); - - fix_old_tiles(); } if(reader.read_int_vector("background-tm", tiles)) { @@ -338,8 +313,14 @@ Sector::parse_old_format(LispReader& reader) } // add a camera - camera = new Camera(this); + Camera* camera = new Camera(this); add_object(camera); + + update_game_objects(); + fix_old_tiles(); + update_game_objects(); + if(solids == 0) + throw std::runtime_error("sector does not contain a solid tile layer."); } void @@ -411,41 +392,6 @@ Sector::write(LispWriter& writer) } void -Sector::do_vertical_flip() -{ - // remove or fix later -#if 0 - for(GameObjects::iterator i = gameobjects_new.begin(); i != gameobjects_new.end(); ++i) - { - TileMap* tilemap = dynamic_cast (*i); - if(tilemap) - { - tilemap->do_vertical_flip(); - } - - BadGuy* badguy = dynamic_cast (*i); - if(badguy) - badguy->start_position.y = solids->get_height()*32 - badguy->start_position.y - 32; - Trampoline* trampoline = dynamic_cast (*i); - if(trampoline) - trampoline->base.y = solids->get_height()*32 - trampoline->base.y - 32; - FlyingPlatform* flying_platform = dynamic_cast (*i); - if(flying_platform) - flying_platform->base.y = solids->get_height()*32 - flying_platform->base.y - 32; - Door* door = dynamic_cast (*i); - if(door) - door->set_area(door->get_area().x, solids->get_height()*32 - door->get_area().y - 32); - } - - for(SpawnPoints::iterator i = spawnpoints.begin(); i != spawnpoints.end(); - ++i) { - SpawnPoint* spawn = *i; - spawn->pos.y = solids->get_height()*32 - spawn->pos.y - 32; - } -#endif -} - -void Sector::add_object(GameObject* object) { // make sure the object isn't already in the list @@ -536,8 +482,7 @@ Sector::action(float elapsed_time) } /* Handle all possible collisions. */ - collision_handler(); - + collision_handler(); update_game_objects(); } @@ -573,7 +518,6 @@ Sector::update_game_objects() if(tilemap && tilemap->is_solid()) { if(solids == 0) { solids = tilemap; - fix_old_tiles(); } else { std::cerr << "Another solid tilemaps added. Ignoring."; } @@ -774,14 +718,6 @@ Sector::collision_handler() } } -void -Sector::add_score(const Vector& pos, int s) -{ - global_stats.add_points(SCORE_STAT, s); - - add_object(new FloatingText(pos, s)); -} - bool Sector::add_bullet(const Vector& pos, float xm, Direction dir) { diff --git a/src/sector.h b/src/sector.h index 598842bb5..a30e5d41d 100644 --- a/src/sector.h +++ b/src/sector.h @@ -67,8 +67,6 @@ public: Sector(); ~Sector(); - /// create new sector - static Sector *create(const std::string& name, size_t width, size_t height); /// read sector from lisp file void parse(LispReader& reader); void parse_old_format(LispReader& reader); @@ -88,6 +86,8 @@ public: /// adds a gameobject void add_object(GameObject* object); + void set_name(const std::string& name) + { this->name = name; } const std::string& get_name() const { return name; } @@ -102,16 +102,10 @@ public: case (or not). */ void collision_handler(); - void add_score(const Vector& pos, int s); - bool add_bullet(const Vector& pos, float xm, Direction dir); bool add_smoke_cloud(const Vector& pos); void add_floating_text(const Vector& pos, const std::string& text); - /** Flip the all the sector vertically. The purpose of this is to let - player to play the same level in a different way :) */ - void do_vertical_flip(); - /** @evil@ but can#t always be avoided in current design... */ static Sector* current() { return _current; } diff --git a/src/supertux.cpp b/src/supertux.cpp index 23a3fcf21..77ea138fe 100644 --- a/src/supertux.cpp +++ b/src/supertux.cpp @@ -39,7 +39,6 @@ #include "video/surface.h" #include "tile_manager.h" #include "app/gettext.h" -#include "player.h" #include "misc.h" #include "utils/configfile.h" diff --git a/src/tilemap.cpp b/src/tilemap.cpp deleted file mode 100644 index 08784415c..000000000 --- a/src/tilemap.cpp +++ /dev/null @@ -1,274 +0,0 @@ -// $Id$ -// -// SuperTux - A Jump'n Run -// Copyright (C) 2004 Matthias Braun - -#include -#include -#include -#include -#include - -#include "tilemap.h" -#include "video/drawing_context.h" -#include "level.h" -#include "tile.h" -#include "tile_manager.h" -#include "app/globals.h" -#include "utils/lispreader.h" -#include "utils/lispwriter.h" - -TileMap::TileMap() - : solid(false), speed(1), width(0), height(0), layer(LAYER_TILES), - vertical_flip(false) -{ - tilemanager = TileManager::instance(); - - if(solid) - flags |= FLAG_SOLID; -} - -TileMap::TileMap(LispReader& reader) - : solid(false), speed(1), width(0), height(0), layer(LAYER_TILES), - vertical_flip(false) -{ - tilemanager = TileManager::instance(); - - std::string layer_str; - if(reader.read_string("layer", layer_str)) { - if(layer_str == "background") - layer = LAYER_BACKGROUNDTILES; - else if(layer_str == "interactive") - layer = LAYER_TILES; - else if(layer_str == "foreground") - layer = LAYER_FOREGROUNDTILES; - else - std::cerr << "Unknown layer '" << layer_str << "' 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(solid) - flags |= FLAG_SOLID; - - 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(int layer_, bool solid_, size_t width_, size_t height_) - : solid(solid_), speed(1), width(0), height(0), layer(layer_), - vertical_flip(false) -{ - tilemanager = TileManager::instance(); - - resize(width_, height_); - - if(solid) - flags |= FLAG_SOLID; -} - -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 { - writer.write_string("layer", "unknown"); - std::cerr << "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(DrawingContext& context) -{ - context.push_transform(); - - if(vertical_flip) - context.set_drawing_effect(VERTICAL_FLIP); - float trans_x = roundf(context.get_translation().x); - float trans_y = roundf(context.get_translation().y); - context.set_translation(Vector(trans_x * speed, trans_y * speed)); - - /** 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(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 - int tsy = int(start_y / 32); // tilestartindex y - - Vector pos; - 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) { - const Tile* tile = tilemanager->get(tiles[ty*width + tx]); - assert(tile != 0); - tile->draw(context, pos, layer); - } - } - - if (debug_grid) - { - for (pos.x = start_x; pos.x < end_x; pos.x += 32) - { - context.draw_filled_rect(Vector (pos.x, start_y), Vector(1, fabsf(start_y - end_y)), - Color(225, 225, 225), LAYER_GUI-50); - } - - for (pos.y = start_y; pos.y < end_y; pos.y += 32) - { - context.draw_filled_rect(Vector (start_x, pos.y), Vector(fabsf(start_x - end_x), 1), - Color(225, 225, 225), LAYER_GUI-50); - } - } - - context.pop_transform(); -} - -void -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.resize(newt.size()); - tiles = newt; - - layer = newlayer; - solid = newsolid; - if(solid) - flags |= FLAG_SOLID; -} - -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; - continue; - } - - tiles[y * new_width + x] = tiles[y * width + x]; - } - } - } - - height = new_height; - width = new_width; -} - -void -TileMap::do_vertical_flip() -{ - // remap tiles vertically flipped - for(int y = 0; y < height / 2; ++y) { - for(int x = 0; x < width; ++x) { - std::swap(tiles[y*width + x], tiles[(((height-1)*width) - (y*width)) + x]); - } - } - - vertical_flip = true; -} - -const Tile* -TileMap::get_tile(int x, int y) const -{ - if(x < 0 || x >= width || y < 0 || y >= height) { -#ifdef DEBUG - //std::cout << "Warning: tile outside tilemap requested!\n"; -#endif - return tilemanager->get(0); - } - - return tilemanager->get(tiles[y*width + x]); -} - -const Tile* -TileMap::get_tile_at(const Vector& pos) const -{ - return get_tile(int(pos.x)/32, int(pos.y)/32); -} - -void -TileMap::change(int x, int y, uint32_t newtile) -{ - assert(x >= 0 && x < width && y >= 0 && y < height); - tiles[y*width + x] = newtile; -} - -void -TileMap::change_at(const Vector& pos, uint32_t newtile) -{ - change(int(pos.x)/32, int(pos.y)/32, newtile); -} diff --git a/src/tilemap.h b/src/tilemap.h deleted file mode 100644 index 8aa2b481a..000000000 --- a/src/tilemap.h +++ /dev/null @@ -1,103 +0,0 @@ -// $Id$ -// -// SuperTux - A Jump'n Run -// Copyright (C) 2004 Matthias Braun -#include - -#include "special/game_object.h" -#include "serializable.h" -#include "math/vector.h" - -using namespace SuperTux; - -namespace SuperTux { -class LispReader; -} - -class Level; -class TileManager; -class Tile; - -/** - * This class is reponsible for drawing the level tiles - */ -class TileMap : public GameObject, public Serializable -{ -public: - TileMap(); - TileMap(LispReader& reader); - TileMap(int layer_, bool solid_, size_t width_, size_t height_); - virtual ~TileMap(); - - virtual void write(LispWriter& writer); - - virtual void action(float elapsed_time); - 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); - - /** Flip the all tile map vertically. The purpose of this is to let - player to play the same level in a different way :) */ - void do_vertical_flip(); - - size_t get_width() const - { return width; } - - size_t get_height() const - { return height; } - - int get_layer() const - { return layer; } - - bool is_solid() const - { return solid; } - - /// returns tile in row y and column y (of the tilemap) - const Tile* get_tile(int x, int y) const; - /// returns tile at position pos (in world coordinates) - const Tile* get_tile_at(const Vector& pos) const; - - void change(int x, int y, uint32_t newtile); - - void change_at(const Vector& pos, uint32_t newtile); - -private: - std::vector tiles; - -private: - TileManager* tilemanager; - bool solid; - float speed; - int width, height; - int layer; - - bool vertical_flip; -}; - -#endif /*SUPERTUX_TILEMAP_H*/ - diff --git a/src/title.cpp b/src/title.cpp index 4874916c1..714f9bd0b 100644 --- a/src/title.cpp +++ b/src/title.cpp @@ -52,14 +52,14 @@ #include "worldmap.h" #include "leveleditor.h" #include "scene.h" -#include "player.h" #include "tile.h" #include "sector.h" -#include "tilemap.h" +#include "object/tilemap.h" +#include "object/camera.h" +#include "object/player.h" #include "resources.h" #include "app/gettext.h" #include "misc.h" -#include "camera.h" static Surface* bkg_title; static Surface* logo; diff --git a/src/trigger/trigger_base.cpp b/src/trigger/trigger_base.cpp index e6b143a2d..608b784a3 100644 --- a/src/trigger/trigger_base.cpp +++ b/src/trigger/trigger_base.cpp @@ -19,8 +19,8 @@ #include #include "trigger_base.h" -#include "player.h" #include "video/drawing_context.h" +#include "object/player.h" TriggerBase::TriggerBase() : sprite(0)