X-Git-Url: https://git.verplant.org/?a=blobdiff_plain;f=src%2Fobject%2Fblock.cpp;h=fd6e2fc211308b5188723058b91fdbf49ac3129a;hb=fea3446f05e1e7673607b835c269d3e8d1929ab3;hp=ed0ea7008fa1c9f6ddc74559ed454794d924051b;hpb=795f0b283fcb1c8777723dc1cc850826d39c6806;p=supertux.git diff --git a/src/object/block.cpp b/src/object/block.cpp index ed0ea7008..fd6e2fc21 100644 --- a/src/object/block.cpp +++ b/src/object/block.cpp @@ -1,7 +1,7 @@ // $Id$ -// +// // SuperTux -// Copyright (C) 2005 Matthias Braun +// Copyright (C) 2006 Matthias Braun // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -12,46 +12,51 @@ // 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. +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + #include -#include "block.h" +#include "block.hpp" +#include "log.hpp" #include -#include "resources.h" -#include "player.h" -#include "sector.h" -#include "sprite/sprite.h" -#include "sprite/sprite_manager.h" -#include "video/drawing_context.h" -#include "lisp/lisp.h" -#include "gameobjs.h" -#include "specialriser.h" -#include "growup.h" -#include "flower.h" -#include "oneup.h" -#include "star.h" -#include "player_status.h" -#include "badguy/badguy.h" -#include "coin.h" -#include "object_factory.h" -#include "lisp/list_iterator.h" -#include "object_factory.h" - -static const float BOUNCY_BRICK_MAX_OFFSET=8; -static const float BOUNCY_BRICK_SPEED=90; -static const float EPSILON = .0001; +#include "resources.hpp" +#include "player.hpp" +#include "sector.hpp" +#include "sprite/sprite.hpp" +#include "sprite/sprite_manager.hpp" +#include "video/drawing_context.hpp" +#include "lisp/lisp.hpp" +#include "gameobjs.hpp" +#include "portable.hpp" +#include "specialriser.hpp" +#include "growup.hpp" +#include "flower.hpp" +#include "oneup.hpp" +#include "star.hpp" +#include "player_status.hpp" +#include "badguy/badguy.hpp" +#include "coin.hpp" +#include "object_factory.hpp" +#include "lisp/list_iterator.hpp" +#include "object_factory.hpp" +#include "level.hpp" + +static const float BOUNCY_BRICK_MAX_OFFSET = 8; +static const float BOUNCY_BRICK_SPEED = 90; +static const float EPSILON = .0001f; Block::Block(Sprite* newsprite) - : sprite(newsprite), bouncing(false), bounce_dir(0), bounce_offset(0) + : sprite(newsprite), bouncing(false), breaking(false), bounce_dir(0), bounce_offset(0) { - bbox.set_size(32, 32.1); - flags |= FLAG_SOLID; + bbox.set_size(32, 32.1f); + set_group(COLGROUP_STATIC); + sound_manager->preload("sounds/upgrade.wav"); + sound_manager->preload("sounds/brick.wav"); } Block::~Block() @@ -60,28 +65,37 @@ Block::~Block() } HitResponse -Block::collision(GameObject& other, const CollisionHit& hitdata) +Block::collision(GameObject& other, const CollisionHit& ) { Player* player = dynamic_cast (&other); if(player) { - // collided from below? - if(hitdata.normal.x == 0 && hitdata.normal.y < 0) { + if(player->get_bbox().get_top() > get_bbox().get_bottom() - 7.0) { hit(*player); } } - if(bouncing) { + // only interact with other objects if... + // 1) we are bouncing + // and + // 2) the object is not portable (either never or not currently) + Portable* portable = dynamic_cast (&other); + if(bouncing && (portable == 0 || (!portable->is_portable()))) { + + // Badguys get killed BadGuy* badguy = dynamic_cast (&other); if(badguy) { badguy->kill_fall(); } + + // Coins get collected Coin* coin = dynamic_cast (&other); if(coin) { coin->collect(); } + } - return FORCE_MOVE; + return SOLID; } void @@ -89,11 +103,14 @@ Block::update(float elapsed_time) { if(!bouncing) return; - + float offset = original_y - get_pos().y; if(offset > BOUNCY_BRICK_MAX_OFFSET) { bounce_dir = BOUNCY_BRICK_SPEED; movement = Vector(0, bounce_dir * elapsed_time); + if(breaking){ + break_me(); + } } else if(offset < BOUNCY_BRICK_SPEED * elapsed_time && bounce_dir > 0) { movement = Vector(0, offset); bounce_dir = 0; @@ -118,10 +135,17 @@ Block::start_bounce() bounce_offset = 0; } +void +Block::start_break() +{ + start_bounce(); + breaking = true; +} + //--------------------------------------------------------------------------- BonusBlock::BonusBlock(const Vector& pos, int data) - : Block(sprite_manager->create("bonusblock")), object(0) + : Block(sprite_manager->create("images/objects/bonus_block/bonusblock.sprite")), object(0) { bbox.set_pos(pos); sprite->set_action("normal"); @@ -132,14 +156,14 @@ BonusBlock::BonusBlock(const Vector& pos, int data) case 4: contents = CONTENT_1UP; break; case 5: contents = CONTENT_ICEGROW; break; default: - std::cerr << "Invalid box contents!\n"; + log_warning << "Invalid box contents" << std::endl; contents = CONTENT_COIN; break; - } + } } BonusBlock::BonusBlock(const lisp::Lisp& lisp) - : Block(sprite_manager->create("bonusblock")) + : Block(sprite_manager->create("images/objects/bonus_block/bonusblock.sprite")) { Vector pos; @@ -167,7 +191,7 @@ BonusBlock::BonusBlock(const lisp::Lisp& lisp) } else if(contentstring == "custom") { contents = CONTENT_CUSTOM; } else { - std::cerr << "Invalid box contents '" << contentstring << "'.\n"; + log_warning << "Invalid box contents '" << contentstring << "'" << std::endl; } } else { if(contents == CONTENT_CUSTOM) { @@ -177,14 +201,14 @@ BonusBlock::BonusBlock(const lisp::Lisp& lisp) throw std::runtime_error( "Only MovingObjects are allowed inside BonusBlocks"); } else { - std::cerr << "Invalid element '" << token << "' in bonusblock.\n"; + log_warning << "Invalid element '" << token << "' in bonusblock" << std::endl; } - } + } } if(contents == CONTENT_CUSTOM && object == 0) throw std::runtime_error("Need to specify content object for custom block"); - + bbox.set_pos(pos); } @@ -199,75 +223,116 @@ BonusBlock::hit(Player& ) try_open(); } +HitResponse +BonusBlock::collision(GameObject& other, const CollisionHit& hit){ + BadGuy* badguy = dynamic_cast (&other); + if(badguy) { + // hit contains no information for collisions with blocks. + // Badguy's bottom has to be below the top of the bonusblock + // +7 is required to slide over one tile gaps. + if( badguy->can_break() && ( badguy->get_bbox().get_bottom() > get_bbox().get_top() + 7.0) ){ + try_open(); + } + } + Portable* portable = dynamic_cast (&other); + if(portable) { + MovingObject* moving = dynamic_cast (&other); + if(moving->get_bbox().get_top() > get_bbox().get_bottom() - 7.0) { + try_open(); + } + } + return Block::collision(other, hit); +} + void BonusBlock::try_open() { - if(sprite->get_action_name() == "empty") { - sound_manager->play("brick"); + if(sprite->get_action() == "empty") { + sound_manager->play("sounds/brick.wav"); return; } - + Sector* sector = Sector::current(); + assert(sector); + assert(sector->player); Player& player = *(sector->player); + Direction direction = (player.get_bbox().get_middle().x > get_bbox().get_middle().x) ? LEFT : RIGHT; + switch(contents) { case CONTENT_COIN: Sector::current()->add_object(new BouncyCoin(get_pos())); - player.get_status()->incCoins(); + player.get_status()->add_coins(1); + Sector::current()->get_level()->stats.coins++; break; case CONTENT_FIREGROW: if(player.get_status()->bonus == NO_BONUS) { - SpecialRiser* riser = new SpecialRiser(get_pos(), new GrowUp()); + SpecialRiser* riser = new SpecialRiser(get_pos(), new GrowUp(direction)); sector->add_object(riser); } else { SpecialRiser* riser = new SpecialRiser( - get_pos(), new Flower(Flower::FIREFLOWER)); + get_pos(), new Flower(FIRE_BONUS)); sector->add_object(riser); } - sound_manager->play("upgrade"); + sound_manager->play("sounds/upgrade.wav"); break; case CONTENT_ICEGROW: if(player.get_status()->bonus == NO_BONUS) { - SpecialRiser* riser = new SpecialRiser(get_pos(), new GrowUp()); - sector->add_object(riser); + SpecialRiser* riser = new SpecialRiser(get_pos(), new GrowUp(direction)); + sector->add_object(riser); } else { SpecialRiser* riser = new SpecialRiser( - get_pos(), new Flower(Flower::ICEFLOWER)); + get_pos(), new Flower(ICE_BONUS)); sector->add_object(riser); - } - sound_manager->play("upgrade"); + } + sound_manager->play("sounds/upgrade.wav"); break; case CONTENT_STAR: - sector->add_object(new Star(get_pos() + Vector(0, -32))); + sector->add_object(new Star(get_pos() + Vector(0, -32), direction)); break; case CONTENT_1UP: - sector->add_object(new OneUp(get_pos())); + sector->add_object(new OneUp(get_pos(), direction)); break; case CONTENT_CUSTOM: SpecialRiser* riser = new SpecialRiser(get_pos(), object); object = 0; sector->add_object(riser); - sound_manager->play("upgrade"); + sound_manager->play("sounds/upgrade.wav"); break; - - //default: - //assert(false); } start_bounce(); sprite->set_action("empty"); } +void +Block::break_me() +{ + Sector* sector = Sector::current(); + sector->add_object( + new BrokenBrick(new Sprite(*sprite), get_pos(), Vector(-100, -400))); + sector->add_object( + new BrokenBrick(new Sprite(*sprite), get_pos() + Vector(0, 16), + Vector(-150, -300))); + sector->add_object( + new BrokenBrick(new Sprite(*sprite), get_pos() + Vector(16, 0), + Vector(100, -400))); + sector->add_object( + new BrokenBrick(new Sprite(*sprite), get_pos() + Vector(16, 16), + Vector(150, -300))); + remove_me(); +} + IMPLEMENT_FACTORY(BonusBlock, "bonusblock"); //--------------------------------------------------------------------------- Brick::Brick(const Vector& pos, int data) - : Block(sprite_manager->create("brick")), breakable(false), + : Block(sprite_manager->create("images/objects/bonus_block/brick.sprite")), breakable(false), coin_counter(0) { bbox.set_pos(pos); @@ -280,48 +345,61 @@ Brick::Brick(const Vector& pos, int data) void Brick::hit(Player& ) { - if(sprite->get_action_name() == "empty") + if(sprite->get_action() == "empty") return; - + try_break(true); } +HitResponse +Brick::collision(GameObject& other, const CollisionHit& hit){ + BadGuy* badguy = dynamic_cast (&other); + if(badguy) { + // hit contains no information for collisions with blocks. + // Badguy's bottom has to be below the top of the brick + // +7 is required to slide over one tile gaps. + if( badguy->can_break() && ( badguy->get_bbox().get_bottom() > get_bbox().get_top() + 7.0 ) ){ + try_break(false); + } + } + Portable* portable = dynamic_cast (&other); + if(portable) { + MovingObject* moving = dynamic_cast (&other); + if(moving->get_bbox().get_top() > get_bbox().get_bottom() - 7.0) { + try_break(); + } + } + return Block::collision(other, hit); +} + void Brick::try_break(bool playerhit) { - if(sprite->get_action_name() == "empty") + if(sprite->get_action() == "empty") return; - - sound_manager->play("brick"); + + sound_manager->play("sounds/brick.wav"); Sector* sector = Sector::current(); Player& player = *(sector->player); if(coin_counter > 0) { sector->add_object(new BouncyCoin(get_pos())); coin_counter--; - player.get_status()->incCoins(); + player.get_status()->add_coins(1); if(coin_counter == 0) sprite->set_action("empty"); start_bounce(); } else if(breakable) { - if(playerhit && !player.is_big()) { - start_bounce(); - return; + if(playerhit){ + if(player.is_big()){ + start_break(); + return; + } else { + start_bounce(); + return; + } } - - sector->add_object( - new BrokenBrick(new Sprite(*sprite), get_pos(), Vector(-100, -400))); - sector->add_object( - new BrokenBrick(new Sprite(*sprite), get_pos() + Vector(0, 16), - Vector(-150, -300))); - sector->add_object( - new BrokenBrick(new Sprite(*sprite), get_pos() + Vector(16, 0), - Vector(100, -400))); - sector->add_object( - new BrokenBrick(new Sprite(*sprite), get_pos() + Vector(16, 16), - Vector(150, -300))); - remove_me(); + break_me(); } } //IMPLEMENT_FACTORY(Brick, "brick"); -