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