Replaced Ref and RefCounter with std::shared_ptr<>
[supertux.git] / src / badguy / goldbomb.cpp
1 //  SuperTux BadGuy GoldBomb - a bomb that throws up coins when exploding
2 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
3 //  Copyright (C) 2013 LMH <lmh.0013@gmail.com>
4 //
5 //  This program is free software: you can redistribute it and/or modify
6 //  it under the terms of the GNU General Public License as published by
7 //  the Free Software Foundation, either version 3 of the License, or
8 //  (at your option) any later version.
9 //
10 //  This program is distributed in the hope that it will be useful,
11 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 //  GNU General Public License for more details.
14 //
15 //  You should have received a copy of the GNU General Public License
16 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18 #include "audio/sound_manager.hpp"
19 #include "badguy/goldbomb.hpp"
20 #include "object/coin_explode.hpp"
21 #include "object/explosion.hpp"
22 #include "object/player.hpp"
23 #include "object/portable.hpp"
24 #include "sprite/sprite.hpp"
25 #include "sprite/sprite_manager.hpp"
26 #include "supertux/object_factory.hpp"
27 #include "supertux/sector.hpp"
28 #include "util/reader.hpp"
29
30 GoldBomb::GoldBomb(const Reader& reader) :
31   WalkingBadguy(reader, "images/creatures/gold_bomb/gold_bomb.sprite", "left", "right"),
32   tstate(STATE_NORMAL),
33   grabbed(false),
34   grabber(NULL),
35   ticking()
36 {
37   walk_speed = 80;
38   max_drop_height = 16;
39
40   //Prevent stutter when Tux jumps on Gold Bomb
41   SoundManager::current()->preload("sounds/explosion.wav");
42
43   //Check if we need another sprite
44   if( !reader.get( "sprite", sprite_name ) ){
45     return;
46   }
47   if( sprite_name == "" ){
48     sprite_name = "images/creatures/gold_bomb/gold_bomb.sprite";
49     return;
50   }
51   //Replace sprite
52   sprite = SpriteManager::current()->create( sprite_name );
53 }
54
55 void
56 GoldBomb::collision_solid(const CollisionHit& hit)
57 {
58   if(tstate == STATE_TICKING) {
59     if(hit.bottom) {
60       physic.set_velocity_y(0);
61       physic.set_velocity_x(0);
62     }else if (hit.left || hit.right)
63       physic.set_velocity_x(-physic.get_velocity_x());
64     else if (hit.top)
65       physic.set_velocity_y(0);
66     return;
67   }
68   WalkingBadguy::collision_solid(hit);
69 }
70
71 HitResponse
72 GoldBomb::collision(GameObject& object, const CollisionHit& hit)
73 {
74   if(tstate == STATE_TICKING)
75     return ABORT_MOVE;
76   if(grabbed)
77     return FORCE_MOVE;
78   return WalkingBadguy::collision(object, hit);
79 }
80
81 HitResponse
82 GoldBomb::collision_player(Player& player, const CollisionHit& hit)
83 {
84   if(tstate == STATE_TICKING)
85     return ABORT_MOVE;
86   if(grabbed)
87     return FORCE_MOVE;
88   return WalkingBadguy::collision_player(player, hit);
89 }
90
91 HitResponse
92 GoldBomb::collision_badguy(BadGuy& badguy, const CollisionHit& hit)
93 {
94   if(tstate == STATE_TICKING)
95     return ABORT_MOVE;
96   return WalkingBadguy::collision_badguy(badguy, hit);
97 }
98
99 bool
100 GoldBomb::collision_squished(GameObject& object)
101 {
102   Player* player = dynamic_cast<Player*>(&object);
103   if(player && player->is_invincible()) {
104     player->bounce(*this);
105     kill_fall();
106     return true;
107   }
108   if(is_valid() && tstate == STATE_NORMAL) {
109     tstate = STATE_TICKING;
110     frozen = false;
111     set_action(dir == LEFT ? "ticking-left" : "ticking-right", 1);
112     physic.set_velocity_x(0);
113
114     if (player)
115       player->bounce(*this);
116
117     ticking = SoundManager::current()->create_sound_source("sounds/fizz.wav");
118     ticking->set_position(get_pos());
119     ticking->set_looping(true);
120     ticking->set_gain(2.0);
121     ticking->set_reference_distance(32);
122     ticking->play();
123   }
124   return true;
125 }
126
127 void
128 GoldBomb::active_update(float elapsed_time)
129 {
130   if(tstate == STATE_TICKING) {
131     ticking->set_position(get_pos());
132     if(sprite->animation_done()) {
133       kill_fall();
134     }
135     else if (!grabbed) {
136       movement = physic.get_movement(elapsed_time);
137     }
138     return;
139   }
140   if(grabbed)
141     return;
142   WalkingBadguy::active_update(elapsed_time);
143 }
144
145 void
146 GoldBomb::kill_fall()
147 {
148   if(tstate == STATE_TICKING)
149     ticking->stop();
150
151   // Make the player let go before we explode, otherwise the player is holding
152   // an invalid object. There's probably a better way to do this than in the
153   // GoldBomb class.
154   if (grabber != NULL) {
155     Player* player = dynamic_cast<Player*>(grabber);
156
157     if (player)
158       player->stop_grabbing();
159   }
160
161   if(is_valid()) {
162     remove_me();
163     Sector::current()->add_object(std::make_shared<Explosion>(get_bbox().get_middle()));
164     Sector::current()->add_object(std::make_shared<CoinExplode>(get_pos() + Vector (0, -40)));
165   }
166
167   run_dead_script();
168 }
169
170 void
171 GoldBomb::grab(MovingObject& object, const Vector& pos, Direction dir_)
172 {
173   if(tstate == STATE_TICKING){
174     movement = pos - get_pos();
175     this->dir = dir_;
176
177     // We actually face the opposite direction of Tux here to make the fuse more
178     // visible instead of hiding it behind Tux
179     sprite->set_action_continued(dir == LEFT ? "ticking-right" : "ticking-left");
180     set_colgroup_active(COLGROUP_DISABLED);
181     grabbed = true;
182     grabber = &object;
183   }
184   else if(frozen){
185     movement = pos - get_pos();
186     this->dir = dir_;
187     sprite->set_action(dir_ == LEFT ? "iced-left" : "iced-right");
188     set_colgroup_active(COLGROUP_DISABLED);
189     grabbed = true;
190   }
191 }
192
193 void
194 GoldBomb::ungrab(MovingObject& object, Direction dir_)
195 {
196   int toss_velocity_x = 0;
197   int toss_velocity_y = 0;
198   Player* player = dynamic_cast<Player*> (&object);
199
200   // toss upwards
201   if(dir_ == UP)
202     toss_velocity_y += -500;
203
204   // toss to the side when moving sideways
205   if(player && player->physic.get_velocity_x()*(dir_ == LEFT ? -1 : 1) > 1) {
206     toss_velocity_x += (dir_ == LEFT) ? -200 : 200;
207     toss_velocity_y = (toss_velocity_y < -200) ? toss_velocity_y : -200;
208     // toss farther when running
209     if(player && player->physic.get_velocity_x()*(dir_ == LEFT ? -1 : 1) > 200)
210       toss_velocity_x += player->physic.get_velocity_x()-(190*(dir_ == LEFT ? -1 : 1));
211   }
212   log_warning << toss_velocity_x << toss_velocity_y << std::endl;////
213
214   //set_pos(object.get_pos() + Vector((dir_ == LEFT ? -33 : 33), get_bbox().get_height()*0.66666 - 32));
215   physic.set_velocity(toss_velocity_x, toss_velocity_y);
216   set_colgroup_active(COLGROUP_MOVING);
217   grabbed = false;
218 }
219
220 void
221 GoldBomb::freeze()
222 {
223   if(tstate == STATE_NORMAL){
224     WalkingBadguy::freeze();
225   }
226 }
227
228 bool
229 GoldBomb::is_freezable() const
230 {
231   return true;
232 }
233
234 bool
235 GoldBomb::is_portable() const
236 {
237   return (frozen || (tstate == STATE_TICKING));
238 }
239
240 /* EOF */