converted all sound files to .ogg (to avoid problems with different sampling rates...
[supertux.git] / src / badguy / badguy.cpp
1 //  $Id$
2 // 
3 //  SuperTux
4 //  Copyright (C) 2005 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
19 //  02111-1307, USA.
20 #include <config.h>
21
22 #include "badguy.hpp"
23 #include "object/camera.hpp"
24 #include "statistics.hpp"
25
26 static const float SQUISH_TIME = 2;
27 static const float X_OFFSCREEN_DISTANCE = 1600;
28 static const float Y_OFFSCREEN_DISTANCE = 1200;
29
30 BadGuy::BadGuy()
31   : countMe(true), sprite(0), dir(LEFT), state(STATE_INIT)
32 {
33 }
34
35 BadGuy::~BadGuy()
36 {
37   delete sprite;
38 }
39
40 void
41 BadGuy::draw(DrawingContext& context)
42 {
43   if(!sprite)
44     return;
45   if(state == STATE_INIT || state == STATE_INACTIVE)
46     return;
47   if(state == STATE_FALLING) {
48     uint32_t old_effect = context.get_drawing_effect();
49     context.set_drawing_effect(old_effect | VERTICAL_FLIP);
50     sprite->draw(context, get_pos(), LAYER_OBJECTS);
51     context.set_drawing_effect(old_effect);
52   } else {
53     sprite->draw(context, get_pos(), LAYER_OBJECTS);
54   }
55 }
56
57 void
58 BadGuy::update(float elapsed_time)
59 {
60   if(!Sector::current()->inside(bbox)) {
61     remove_me();
62     return;
63   }
64   if(is_offscreen()) {
65     set_state(STATE_INACTIVE);
66   }
67   
68   switch(state) {
69     case STATE_ACTIVE:
70       active_update(elapsed_time);
71       break;
72     case STATE_INIT:
73     case STATE_INACTIVE:
74       inactive_update(elapsed_time);
75       try_activate();
76       break;
77     case STATE_SQUISHED:
78       if(state_timer.check()) {
79         remove_me();
80         break;
81       }
82       movement = physic.get_movement(elapsed_time);
83       break;
84     case STATE_FALLING:
85       movement = physic.get_movement(elapsed_time);
86       break;
87   }
88 }
89
90 void
91 BadGuy::activate()
92 {
93 }
94
95 void
96 BadGuy::deactivate()
97 {
98 }
99
100 void
101 BadGuy::active_update(float elapsed_time)
102 {
103   movement = physic.get_movement(elapsed_time);
104 }
105
106 void
107 BadGuy::inactive_update(float )
108 {
109 }
110
111 HitResponse
112 BadGuy::collision(GameObject& other, const CollisionHit& hit)
113 {
114   switch(state) {
115     case STATE_INIT:
116     case STATE_INACTIVE:
117       return ABORT_MOVE;
118     case STATE_ACTIVE: {
119       if(other.get_flags() & FLAG_SOLID)
120         return collision_solid(other, hit);
121
122       BadGuy* badguy = dynamic_cast<BadGuy*> (&other);
123       if(badguy && badguy->state == STATE_ACTIVE)
124         return collision_badguy(*badguy, hit);
125
126       Player* player = dynamic_cast<Player*> (&other);
127       if(player)
128         return collision_player(*player, hit);
129
130       return FORCE_MOVE;
131     }
132     case STATE_SQUISHED:
133       if(other.get_flags() & FLAG_SOLID)
134         return CONTINUE;
135       return FORCE_MOVE;
136     case STATE_FALLING:
137       return FORCE_MOVE;
138   }
139
140   return ABORT_MOVE;
141 }
142
143 HitResponse
144 BadGuy::collision_solid(GameObject& , const CollisionHit& )
145 {
146   return FORCE_MOVE;
147 }
148
149 HitResponse
150 BadGuy::collision_player(Player& player, const CollisionHit& )
151 {
152   if(player.is_invincible()) {
153     kill_fall();
154     return ABORT_MOVE;
155   }
156   // hit from above?
157   if(player.get_movement().y - get_movement().y > 0 && player.get_bbox().p2.y <
158       (get_bbox().p1.y + get_bbox().p2.y) / 2) {
159     // if it's not is it possible to squish us, then this will hurt
160     if(!collision_squished(player))
161       player.kill(Player::SHRINK);
162
163     return FORCE_MOVE;
164   }
165   player.kill(Player::SHRINK);
166   return FORCE_MOVE;
167 }
168
169 HitResponse
170 BadGuy::collision_badguy(BadGuy& , const CollisionHit& )
171 {
172   return FORCE_MOVE;
173 }
174
175 bool
176 BadGuy::collision_squished(Player& )
177 {
178   return false;
179 }
180
181 void
182 BadGuy::kill_squished(Player& player)
183 {
184   sound_manager->play("sounds/squish.ogg", get_pos());
185   physic.enable_gravity(true);
186   physic.set_velocity_x(0);
187   physic.set_velocity_y(0);
188   set_state(STATE_SQUISHED);
189   global_stats.add_points(BADGUYS_KILLED_STAT, 1);
190   player.bounce(*this);
191 }
192
193 void
194 BadGuy::kill_fall()
195 {
196   sound_manager->play("sounds/fall.ogg", get_pos());
197   global_stats.add_points(BADGUYS_KILLED_STAT, 1);
198   physic.set_velocity_y(0);
199   physic.enable_gravity(true);
200   set_state(STATE_FALLING);
201 }
202
203 void
204 BadGuy::set_state(State state)
205 {
206   if(this->state == state)
207     return;
208
209   State laststate = this->state;
210   this->state = state;
211   switch(state) {
212     case STATE_SQUISHED:
213       state_timer.start(SQUISH_TIME);
214       break;
215     case STATE_ACTIVE:
216       flags &= ~FLAG_NO_COLLDET;
217       bbox.set_pos(start_position);
218       break;
219     case STATE_INACTIVE:
220       // was the badguy dead anyway?
221       if(laststate == STATE_SQUISHED || laststate == STATE_FALLING) {
222         remove_me();
223       }
224       flags |= FLAG_NO_COLLDET;
225       break;
226     case STATE_FALLING:
227       flags |= FLAG_NO_COLLDET;
228       break;
229     default:
230       break;
231   }
232 }
233
234 bool
235 BadGuy::is_offscreen()
236 {
237   float scroll_x = Sector::current()->camera->get_translation().x;
238   float scroll_y = Sector::current()->camera->get_translation().y;
239      
240   if(bbox.p2.x < scroll_x - X_OFFSCREEN_DISTANCE
241       || bbox.p1.x > scroll_x + X_OFFSCREEN_DISTANCE
242       || bbox.p2.y < scroll_y - Y_OFFSCREEN_DISTANCE
243       || bbox.p1.y > scroll_y + Y_OFFSCREEN_DISTANCE)
244     return true;
245
246   return false;
247 }
248
249 void
250 BadGuy::try_activate()
251 {
252   float scroll_x = Sector::current()->camera->get_translation().x;
253   float scroll_y = Sector::current()->camera->get_translation().y;
254
255   /* Activate badguys if they're just around the screen to avoid
256    * the effect of having badguys suddenly popping up from nowhere.
257    */
258   if (start_position.x > scroll_x - X_OFFSCREEN_DISTANCE &&
259       start_position.x < scroll_x - bbox.get_width() &&
260       start_position.y > scroll_y - Y_OFFSCREEN_DISTANCE &&
261       start_position.y < scroll_y + Y_OFFSCREEN_DISTANCE) {
262     dir = RIGHT;
263     set_state(STATE_ACTIVE);
264     activate();
265   } else if (start_position.x > scroll_x &&
266       start_position.x < scroll_x + X_OFFSCREEN_DISTANCE &&
267       start_position.y > scroll_y - Y_OFFSCREEN_DISTANCE &&
268       start_position.y < scroll_y + Y_OFFSCREEN_DISTANCE) {
269     dir = LEFT;
270     set_state(STATE_ACTIVE);
271     activate();
272   } else if (start_position.x > scroll_x - X_OFFSCREEN_DISTANCE &&
273       start_position.x < scroll_x + X_OFFSCREEN_DISTANCE &&
274       ((start_position.y > scroll_y &&
275         start_position.y < scroll_y + Y_OFFSCREEN_DISTANCE) ||
276        (start_position.y > scroll_y - Y_OFFSCREEN_DISTANCE &&
277         start_position.y < scroll_y))) {
278     dir = start_position.x < scroll_x ? RIGHT : LEFT;
279     set_state(STATE_ACTIVE);
280     activate();
281   } else if(state == STATE_INIT
282       && start_position.x > scroll_x - X_OFFSCREEN_DISTANCE
283       && start_position.x < scroll_x + X_OFFSCREEN_DISTANCE
284       && start_position.y > scroll_y - Y_OFFSCREEN_DISTANCE
285       && start_position.y < scroll_y + Y_OFFSCREEN_DISTANCE) {
286     dir = LEFT;
287     set_state(STATE_ACTIVE);
288     activate();
289   } 
290 }