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 "badguy/goldbomb.hpp"
19
20#include "audio/sound_manager.hpp"
21#include "audio/sound_source.hpp"
22#include "object/coin_explode.hpp"
23#include "object/explosion.hpp"
24#include "object/player.hpp"
25#include "sprite/sprite.hpp"
26#include "sprite/sprite_manager.hpp"
27#include "supertux/sector.hpp"
28#include "util/reader_mapping.hpp"
29
30GoldBomb::GoldBomb(const ReaderMapping& reader) :
31 WalkingBadguy(reader, "images/creatures/gold_bomb/gold_bomb.sprite", "left", "right"),
32 tstate(STATE_NORMAL),
33 ticking()
34{
35 walk_speed = 80;
36 max_drop_height = 16;
37
38 //Prevent stutter when Tux jumps on Gold Bomb
39 SoundManager::current()->preload("sounds/explosion.wav");
40
41 //Check if we need another sprite
42 if ( !reader.get( "sprite", m_sprite_name ) ){
43 return;
44 }
45 if (m_sprite_name.empty()) {
46 m_sprite_name = "images/creatures/gold_bomb/gold_bomb.sprite";
47 return;
48 }
49 //Replace sprite
50 m_sprite = SpriteManager::current()->create( m_sprite_name );
51}
52
53void
54GoldBomb::collision_solid(const CollisionHit& hit)
55{
56 if (tstate == STATE_TICKING) {
57 if (hit.bottom) {
58 m_physic.set_velocity_y(0);
59 m_physic.set_velocity_x(0);
60 }else if (hit.left || hit.right)
61 m_physic.set_velocity_x(-m_physic.get_velocity_x());
62 else if (hit.top)
63 m_physic.set_velocity_y(0);
64 update_on_ground_flag(hit);
65 return;
66 }
67 WalkingBadguy::collision_solid(hit);
68}
69
70HitResponse
71GoldBomb::collision(GameObject& object, const CollisionHit& hit)
72{
73 if (tstate == STATE_TICKING) {
74 if ( dynamic_cast<Player*>(&object) ) {
75 return ABORT_MOVE;
76 }
77 if ( dynamic_cast<BadGuy*>(&object) ) {
78 return ABORT_MOVE;
79 }
80 }
81 if (is_grabbed())
82 return FORCE_MOVE;
83 return WalkingBadguy::collision(object, hit);
84}
85
86HitResponse
87GoldBomb::collision_player(Player& player, const CollisionHit& hit)
88{
89 if (tstate == STATE_TICKING)
90 return FORCE_MOVE;
91 if (is_grabbed())
92 return FORCE_MOVE;
93 return WalkingBadguy::collision_player(player, hit);
94}
95
96HitResponse
97GoldBomb::collision_badguy(BadGuy& badguy, const CollisionHit& hit)
98{
99 if (tstate == STATE_TICKING)
100 return FORCE_MOVE;
101 return WalkingBadguy::collision_badguy(badguy, hit);
102}
103
104bool
105GoldBomb::collision_squished(GameObject& object)
106{
107 Player* player = dynamic_cast<Player*>(&object);
108 if (player && player->is_invincible()) {
109 player->bounce(*this);
110 kill_fall();
111 return true;
112 }
113 if (is_valid() && tstate == STATE_NORMAL) {
114 tstate = STATE_TICKING;
115 m_frozen = false;
116 set_action(m_dir == Direction::LEFT ? "ticking-left" : "ticking-right", 1);
117 m_physic.set_velocity_x(0);
118
119 if (player)
120 player->bounce(*this);
121
122 ticking = SoundManager::current()->create_sound_source("sounds/fizz.wav");
123 ticking->set_position(get_pos());
124 ticking->set_looping(true);
125 ticking->set_gain(1.0f);
126 ticking->set_reference_distance(32);
127 ticking->play();
128 }
129 return true;
130}
131
132void
133GoldBomb::active_update(float dt_sec)
134{
135 if (tstate == STATE_TICKING) {
136 if (on_ground()) m_physic.set_velocity_x(0);
137 ticking->set_position(get_pos());
138 if (m_sprite->animation_done()) {
139 kill_fall();
140 }
141 else if (!is_grabbed()) {
142 m_col.m_movement = m_physic.get_movement(dt_sec);
143 }
144 return;
145 }
146 if (is_grabbed())
147 return;
148 WalkingBadguy::active_update(dt_sec);
149}
150
151void
152GoldBomb::kill_fall()
153{
154 if (tstate == STATE_TICKING)
155 ticking->stop();
156
157 // Make the player let go before we explode, otherwise the player is holding
158 // an invalid object. There's probably a better way to do this than in the
159 // GoldBomb class.
160 if (is_grabbed()) {
161 Player* player = dynamic_cast<Player*>(m_owner);
162
163 if (player)
164 player->stop_grabbing();
165 }
166
167 if (is_valid()) {
168 remove_me();
169 Sector::get().add<Explosion>(m_col.m_bbox.get_middle());
170 Sector::get().add<CoinExplode>(get_pos() + Vector (0, -40));
171 }
172
173 run_dead_script();
174}
175
176void
177GoldBomb::ignite()
178{
179 kill_fall();
180}
181
182void
183GoldBomb::grab(MovingObject& object, const Vector& pos, Direction dir_)
184{
185 Portable::grab(object,pos,dir_);
186 if (tstate == STATE_TICKING){
187 m_col.m_movement = pos - get_pos();
188 m_dir = dir_;
189
190 // We actually face the opposite direction of Tux here to make the fuse more
191 // visible instead of hiding it behind Tux
192 m_sprite->set_action_continued(m_dir == Direction::LEFT ? "ticking-right" : "ticking-left");
193 set_colgroup_active(COLGROUP_DISABLED);
194 }
195 else if (m_frozen){
196 m_col.m_movement = pos - get_pos();
197 m_dir = dir_;
198 m_sprite->set_action(dir_ == Direction::LEFT ? "iced-left" : "iced-right");
199 set_colgroup_active(COLGROUP_DISABLED);
200 }
201}
202
203void
204GoldBomb::ungrab(MovingObject& object, Direction dir_)
205{
206 int toss_velocity_x = 0;
207 int toss_velocity_y = 0;
208 auto player = dynamic_cast<Player*> (&object);
209
210 // toss upwards
211 if (dir_ == Direction::UP)
212 toss_velocity_y += -500;
213
214 // toss to the side when moving sideways
215 if (player && player->get_physic().get_velocity_x()*(dir_ == Direction::LEFT ? -1 : 1) > 1) {
216 toss_velocity_x += (dir_ == Direction::LEFT) ? -200 : 200;
217 toss_velocity_y = (toss_velocity_y < -200) ? toss_velocity_y : -200;
218 // toss farther when running
219 if (player && player->get_physic().get_velocity_x()*(dir_ == Direction::LEFT ? -1 : 1) > 200)
220 toss_velocity_x += static_cast<int>(player->get_physic().get_velocity_x() - (190*(dir_ == Direction::LEFT ? -1 : 1)));
221 }
222 log_warning << toss_velocity_x << toss_velocity_y << std::endl;////
223
224 //set_pos(object.get_pos() + Vector((dir_ == LEFT ? -33 : 33), get_bbox().get_height()*0.66666 - 32));
225 m_physic.set_velocity(static_cast<float>(toss_velocity_x),
226 static_cast<float>(toss_velocity_y));
227 set_colgroup_active(COLGROUP_MOVING);
228 Portable::ungrab(object, dir_);
229}
230
231void
232GoldBomb::freeze()
233{
234 if (tstate == STATE_NORMAL) {
235 WalkingBadguy::freeze();
236 }
237}
238
239bool
240GoldBomb::is_freezable() const
241{
242 return true;
243}
244
245bool
246GoldBomb::is_portable() const
247{
248 return (m_frozen || (tstate == STATE_TICKING));
249}
250
251void GoldBomb::stop_looping_sounds()
252{
253 if (ticking) {
254 ticking->stop();
255 }
256}
257
258void GoldBomb::play_looping_sounds()
259{
260 if (tstate == STATE_TICKING && ticking) {
261 ticking->play();
262 }
263}
264
265/* EOF */
266