Use run_dead_script wherever possible and make kill_* operations only do stuff once
[supertux.git] / src / badguy / bomb.cpp
1 //  $Id$
2 //
3 //  SuperTux
4 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
5 //
6 //  This program is free software; you can redistribute it and/or
7 //  modify it under the terms of the GNU General Public License
8 //  as published by the Free Software Foundation; either version 2
9 //  of the License, or (at your option) any later version.
10 //
11 //  This program is distributed in the hope that it will be useful,
12 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //  GNU General Public License for more details.
15 //
16 //  You should have received a copy of the GNU General Public License
17 //  along with this program; if not, write to the Free Software
18 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19
20 #include <config.h>
21
22 #include "bomb.hpp"
23 #include "random_generator.hpp"
24 #include "object/explosion.hpp"
25 #include "audio/sound_manager.hpp"
26 #include "lisp/writer.hpp"
27 #include "sprite/sprite.hpp"
28 #include "object/player.hpp"
29 #include "sector.hpp"
30
31 Bomb::Bomb(const Vector& pos, Direction dir, std::string custom_sprite /*= "images/creatures/mr_bomb/mr_bomb.sprite"*/ )
32         : BadGuy( pos, dir, custom_sprite ), grabbed(false), grabber(NULL)
33 {
34   state = STATE_TICKING;
35   set_action(dir == LEFT ? "ticking-left" : "ticking-right", 1);
36   countMe = false;
37
38   ticking.reset(sound_manager->create_sound_source("sounds/fizz.wav"));
39   ticking->set_position(get_pos());
40   ticking->set_looping(true);
41   ticking->set_gain(2.0);
42   ticking->set_reference_distance(32);
43   ticking->play();
44 }
45
46 Bomb::Bomb(const Bomb& other)
47         : BadGuy(other), Portable(other), state(other.state)
48 {
49   if (state == STATE_TICKING) {
50     ticking.reset(sound_manager->create_sound_source("sounds/fizz.wav"));
51     ticking->set_position(get_pos());
52     ticking->set_looping(true);
53     ticking->set_gain(2.0);
54     ticking->set_reference_distance(32);
55     ticking->play();
56   }
57 }
58
59 void
60 Bomb::write(lisp::Writer& )
61 {
62   // bombs are only temporarily so don't write them out...
63 }
64
65 void
66 Bomb::collision_solid(const CollisionHit& hit)
67 {
68   if(hit.bottom)
69     physic.set_velocity_y(0);
70
71     update_on_ground_flag(hit);
72 }
73
74 HitResponse
75 Bomb::collision_player(Player& , const CollisionHit& )
76 {
77   return ABORT_MOVE;
78 }
79
80 HitResponse
81 Bomb::collision_badguy(BadGuy& , const CollisionHit& )
82 {
83   return ABORT_MOVE;
84 }
85
86 void
87 Bomb::active_update(float elapsed_time)
88 {
89   ticking->set_position(get_pos());
90   if(sprite->animation_done()) {
91     explode();
92   }
93   else if (!grabbed) {
94     movement = physic.get_movement(elapsed_time);
95   }
96 }
97
98 void
99 Bomb::explode()
100 {
101   ticking->stop();
102
103   // Make the player let go before we explode, otherwise the player is holding
104   // an invalid object. There's probably a better way to do this than in the
105   // Bomb class.
106   if (grabber != NULL) {
107     Player* player = dynamic_cast<Player*>(grabber);
108     
109     if (player)
110         player->stop_grabbing();
111   }
112
113   if(is_valid()) {
114     remove_me();
115     Explosion* explosion = new Explosion(get_bbox().get_middle());
116     Sector::current()->add_object(explosion);
117   }
118
119   run_dead_script();
120 }
121
122 void
123 Bomb::kill_fall()
124 {
125   explode();
126 }
127
128 void
129 Bomb::grab(MovingObject& object, const Vector& pos, Direction dir)
130 {
131   movement = pos - get_pos();
132   this->dir = dir;
133
134   // We actually face the opposite direction of Tux here to make the fuse more
135   // visible instead of hiding it behind Tux
136   sprite->set_action_continued(dir == LEFT ? "ticking-right" : "ticking-left");
137   set_colgroup_active(COLGROUP_DISABLED);
138   grabbed = true;
139   grabber = &object;
140 }
141
142 void
143 Bomb::ungrab(MovingObject& object, Direction dir)
144 {
145   this->dir = dir;
146   // portable objects are usually pushed away from Tux when dropped, but we
147   // don't want that, so we set the position
148   set_pos(object.get_pos() + Vector(dir == LEFT ? -16 : 16, get_bbox().get_height()*0.66666 - 32));
149   set_colgroup_active(COLGROUP_MOVING);
150   grabbed = false;
151 }
152