1// SuperTux - Badguy "Snail"
2// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17#include "badguy/snail.hpp"
18
19#include <math.h>
20
21#include "audio/sound_manager.hpp"
22#include "object/player.hpp"
23#include "sprite/sprite.hpp"
24
25namespace {
26const float SNAIL_KICK_SPEED = 500;
27const int MAX_SNAIL_SQUISHES = 10;
28const float SNAIL_KICK_SPEED_Y = -500; /**< y-velocity gained when kicked */
29}
30
31Snail::Snail(const ReaderMapping& reader) :
32 WalkingBadguy(reader, "images/creatures/snail/snail.sprite", "left", "right"),
33 state(STATE_NORMAL),
34 kicked_delay_timer(),
35 squishcount(0)
36{
37 walk_speed = 80;
38 max_drop_height = 600;
39 SoundManager::current()->preload("sounds/iceblock_bump.wav");
40 SoundManager::current()->preload("sounds/stomp.wav");
41 SoundManager::current()->preload("sounds/kick.wav");
42}
43
44void
45Snail::initialize()
46{
47 WalkingBadguy::initialize();
48 be_normal();
49}
50
51void
52Snail::be_normal()
53{
54 if (state == STATE_NORMAL) return;
55
56 state = STATE_NORMAL;
57 WalkingBadguy::initialize();
58}
59
60void
61Snail::be_flat()
62{
63 state = STATE_FLAT;
64 m_sprite->set_action(m_dir == Direction::LEFT ? "flat-left" : "flat-right", 1);
65
66 m_physic.set_velocity_x(0);
67 m_physic.set_velocity_y(0);
68}
69
70void Snail::be_grabbed()
71{
72 state = STATE_GRABBED;
73 m_sprite->set_action(m_dir == Direction::LEFT ? "flat-left" : "flat-right", 1);
74}
75
76void
77Snail::be_kicked()
78{
79 state = STATE_KICKED_DELAY;
80 m_sprite->set_action(m_dir == Direction::LEFT ? "flat-left" : "flat-right", 1);
81
82 m_physic.set_velocity_x(m_dir == Direction::LEFT ? -SNAIL_KICK_SPEED : SNAIL_KICK_SPEED);
83 m_physic.set_velocity_y(0);
84
85 // start a timer to delay addition of upward movement until we are (hopefully) out from under the player
86 kicked_delay_timer.start(0.05f);
87}
88
89bool
90Snail::can_break() const {
91 return state == STATE_KICKED;
92}
93
94void
95Snail::active_update(float dt_sec)
96{
97 if (state == STATE_GRABBED)
98 return;
99
100 if (m_frozen)
101 {
102 BadGuy::active_update(dt_sec);
103 return;
104 }
105
106 switch (state) {
107
108 case STATE_NORMAL:
109 WalkingBadguy::active_update(dt_sec);
110 return;
111
112 case STATE_FLAT:
113 if (m_sprite->animation_done()) {
114 be_normal();
115 }
116 break;
117
118 case STATE_KICKED_DELAY:
119 if (kicked_delay_timer.check()) {
120 m_physic.set_velocity_x(m_dir == Direction::LEFT ? -SNAIL_KICK_SPEED : SNAIL_KICK_SPEED);
121 m_physic.set_velocity_y(SNAIL_KICK_SPEED_Y);
122 state = STATE_KICKED;
123 }
124 break;
125
126 case STATE_KICKED:
127 m_physic.set_velocity_x(m_physic.get_velocity_x() * powf(0.99f, dt_sec/0.02f));
128 if (m_sprite->animation_done() || (fabsf(m_physic.get_velocity_x()) < walk_speed)) be_normal();
129 break;
130
131 case STATE_GRABBED:
132 break;
133 }
134
135 BadGuy::active_update(dt_sec);
136
137 if (m_ignited)
138 remove_me();
139}
140
141bool
142Snail::is_freezable() const
143{
144 return true;
145}
146
147void
148Snail::collision_solid(const CollisionHit& hit)
149{
150 if (m_frozen)
151 {
152 WalkingBadguy::collision_solid(hit);
153 return;
154 }
155
156 switch (state)
157 {
158 case STATE_NORMAL:
159 WalkingBadguy::collision_solid(hit);
160 return;
161
162 case STATE_KICKED:
163 if (hit.left || hit.right) {
164 SoundManager::current()->play("sounds/iceblock_bump.wav", get_pos());
165
166 if ( ( m_dir == Direction::LEFT && hit.left ) || ( m_dir == Direction::RIGHT && hit.right) ){
167 m_dir = (m_dir == Direction::LEFT) ? Direction::RIGHT : Direction::LEFT;
168 m_sprite->set_action(m_dir == Direction::LEFT ? "flat-left" : "flat-right");
169
170 m_physic.set_velocity_x(-m_physic.get_velocity_x());
171 }
172 }
173 BOOST_FALLTHROUGH;
174 case STATE_FLAT:
175 case STATE_KICKED_DELAY:
176 if (hit.top || hit.bottom) {
177 m_physic.set_velocity_y(0);
178 }
179 break;
180
181 case STATE_GRABBED:
182 break;
183 }
184
185 update_on_ground_flag(hit);
186
187}
188
189HitResponse
190Snail::collision_badguy(BadGuy& badguy, const CollisionHit& hit)
191{
192 if (m_frozen)
193 return WalkingBadguy::collision_badguy(badguy, hit);
194
195 switch (state) {
196 case STATE_NORMAL:
197 return WalkingBadguy::collision_badguy(badguy, hit);
198 case STATE_FLAT:
199 case STATE_KICKED_DELAY:
200 return FORCE_MOVE;
201 case STATE_KICKED:
202 badguy.kill_fall();
203 return FORCE_MOVE;
204 default:
205 assert(false);
206 }
207
208 return ABORT_MOVE;
209}
210
211HitResponse
212Snail::collision_player(Player& player, const CollisionHit& hit)
213{
214 if (m_frozen)
215 return WalkingBadguy::collision_player(player, hit);
216
217 // handle kicks from left or right side
218 if (state == STATE_FLAT && (hit.left || hit.right)) {
219 if (hit.left) {
220 m_dir = Direction::RIGHT;
221 } else if (hit.right) {
222 m_dir = Direction::LEFT;
223 }
224 player.kick();
225 be_kicked();
226 return FORCE_MOVE;
227 }
228
229 return BadGuy::collision_player(player, hit);
230}
231
232bool
233Snail::collision_squished(GameObject& object)
234{
235 if (m_frozen)
236 return WalkingBadguy::collision_squished(object);
237
238 Player* player = dynamic_cast<Player*>(&object);
239 if (player && (player->m_does_buttjump || player->is_invincible())) {
240 kill_fall();
241 player->bounce(*this);
242 return true;
243 }
244
245 switch (state) {
246
247 case STATE_KICKED:
248 case STATE_NORMAL:
249
250 // Can't stomp in midair
251 if (!on_ground())
252 break;
253
254 squishcount++;
255 if (squishcount >= MAX_SNAIL_SQUISHES) {
256 kill_fall();
257 return true;
258 }
259 SoundManager::current()->play("sounds/stomp.wav", get_pos());
260 be_flat();
261 break;
262
263 case STATE_FLAT:
264 SoundManager::current()->play("sounds/kick.wav", get_pos());
265 {
266 MovingObject* movingobject = dynamic_cast<MovingObject*>(&object);
267 if (movingobject && (movingobject->get_pos().x < get_pos().x)) {
268 m_dir = Direction::RIGHT;
269 } else {
270 m_dir = Direction::LEFT;
271 }
272 }
273 be_kicked();
274 break;
275 case STATE_GRABBED:
276 case STATE_KICKED_DELAY:
277 break;
278
279 }
280
281 if (player) player->bounce(*this);
282 return true;
283}
284
285void
286Snail::grab(MovingObject& object, const Vector& pos, Direction dir_)
287{
288 Portable::grab(object, pos, dir_);
289 m_col.m_movement = pos - get_pos();
290 m_dir = dir_;
291 set_action(dir_ == Direction::LEFT ? "flat-left" : "flat-right", /* loops = */ -1);
292 be_grabbed();
293 set_colgroup_active(COLGROUP_DISABLED);
294}
295
296void
297Snail::ungrab(MovingObject& object, Direction dir_)
298{
299 if (dir_ == Direction::UP) {
300 be_flat();
301 } else {
302 m_dir = dir_;
303 be_kicked();
304 }
305 set_colgroup_active(COLGROUP_MOVING);
306 Portable::ungrab(object, dir_);
307}
308
309bool
310Snail::is_portable() const
311{
312 return state == STATE_FLAT && !m_ignited;
313}
314
315/* EOF */
316