-Incorporated Marcin Ko��cielnicki patch that reintroduces the flying
[supertux.git] / src / badguy / badguy.cpp
1 #include <config.h>
2
3 #include "badguy.h"
4 #include "object/camera.h"
5
6 static const float SQUISH_TIME = 2;
7 static const float X_OFFSCREEN_DISTANCE = 1600;
8 static const float Y_OFFSCREEN_DISTANCE = 1200;
9
10 BadGuy::BadGuy()
11   : sprite(0), dir(LEFT), state(STATE_INIT)
12 {
13 }
14
15 BadGuy::~BadGuy()
16 {
17   delete sprite;
18 }
19
20 void
21 BadGuy::draw(DrawingContext& context)
22 {
23   if(!sprite)
24     return;
25   if(state == STATE_INIT || state == STATE_INACTIVE)
26     return;
27   if(state == STATE_FALLING) {
28     sprite->draw(context, get_pos(), LAYER_OBJECTS, VERTICAL_FLIP);
29   } else {
30     sprite->draw(context, get_pos(), LAYER_OBJECTS);
31   }
32 }
33
34 void
35 BadGuy::action(float elapsed_time)
36 {
37   if(!Sector::current()->inside(bbox)) {
38     remove_me();
39     return;
40   }
41   if(is_offscreen()) {
42     set_state(STATE_INACTIVE);
43   }
44   
45   switch(state) {
46     case STATE_ACTIVE:
47       active_action(elapsed_time);
48       break;
49     case STATE_INIT:
50     case STATE_INACTIVE:
51       inactive_action(elapsed_time);
52       try_activate();
53       break;
54     case STATE_SQUISHED:
55       if(state_timer.check()) {
56         remove_me();
57         break;
58       }
59       movement = physic.get_movement(elapsed_time);
60       break;
61     case STATE_FALLING:
62       movement = physic.get_movement(elapsed_time);
63       break;
64   }
65 }
66
67 void
68 BadGuy::activate()
69 {
70 }
71
72 void
73 BadGuy::deactivate()
74 {
75 }
76
77 void
78 BadGuy::active_action(float elapsed_time)
79 {
80   movement = physic.get_movement(elapsed_time);
81 }
82
83 void
84 BadGuy::inactive_action(float )
85 {
86 }
87
88 HitResponse
89 BadGuy::collision(GameObject& other, const CollisionHit& hit)
90 {
91   switch(state) {
92     case STATE_INIT:
93     case STATE_INACTIVE:
94       return ABORT_MOVE;
95     case STATE_ACTIVE: {
96       if(other.get_flags() & FLAG_SOLID)
97         return collision_solid(other, hit);
98
99       BadGuy* badguy = dynamic_cast<BadGuy*> (&other);
100       if(badguy && badguy->state == STATE_ACTIVE)
101         return collision_badguy(*badguy, hit);
102
103       Player* player = dynamic_cast<Player*> (&other);
104       if(player)
105         return collision_player(*player, hit);
106
107       return FORCE_MOVE;
108     }
109     case STATE_SQUISHED:
110       if(other.get_flags() & FLAG_SOLID)
111         return CONTINUE;
112       return FORCE_MOVE;
113     case STATE_FALLING:
114       return FORCE_MOVE;
115   }
116
117   return ABORT_MOVE;
118 }
119
120 HitResponse
121 BadGuy::collision_solid(GameObject& , const CollisionHit& )
122 {
123   return FORCE_MOVE;
124 }
125
126 HitResponse
127 BadGuy::collision_player(Player& player, const CollisionHit& hit)
128 {
129   if(player.is_invincible()) {
130     kill_fall();
131     return ABORT_MOVE;
132   }
133   if(hit.normal.y > .9) {
134     if(collision_squished(player))
135       return ABORT_MOVE;
136   }
137   player.kill(Player::SHRINK);
138   return FORCE_MOVE;
139 }
140
141 HitResponse
142 BadGuy::collision_badguy(BadGuy& , const CollisionHit& )
143 {
144   return FORCE_MOVE;
145 }
146
147 bool
148 BadGuy::collision_squished(Player& )
149 {
150   return false;
151 }
152
153 void
154 BadGuy::kill_squished(Player& player)
155 {
156   SoundManager::get()->play_sound(IDToSound(SND_SQUISH), get_pos(),
157       player.get_pos());
158   physic.enable_gravity(true);
159   physic.set_velocity_x(0);
160   physic.set_velocity_y(0);
161   set_state(STATE_SQUISHED);
162   player.bounce(*this);
163 }
164
165 void
166 BadGuy::kill_fall()
167 {
168   SoundManager::get()->play_sound(IDToSound(SND_FALL), this,
169       Sector::current()->player->get_pos());
170   physic.set_velocity_y(0);
171   physic.enable_gravity(true);
172   set_state(STATE_FALLING);
173 }
174
175 void
176 BadGuy::set_state(State state)
177 {
178   if(this->state == state)
179     return;
180
181   State laststate = this->state;
182   this->state = state;
183   switch(state) {
184     case STATE_SQUISHED:
185       state_timer.start(SQUISH_TIME);
186       break;
187     case STATE_ACTIVE:
188       flags &= ~FLAG_NO_COLLDET;
189       bbox.set_pos(start_position);
190       break;
191     case STATE_INACTIVE:
192       // was the badguy dead anyway?
193       if(laststate == STATE_SQUISHED || laststate == STATE_SQUISHED) {
194         remove_me();
195       }
196       flags |= FLAG_NO_COLLDET;
197       break;
198     case STATE_FALLING:
199       flags |= FLAG_NO_COLLDET;
200       break;
201     default:
202       break;
203   }
204 }
205
206 bool
207 BadGuy::is_offscreen()
208 {
209   float scroll_x = Sector::current()->camera->get_translation().x;
210   float scroll_y = Sector::current()->camera->get_translation().y;
211      
212   if(bbox.p2.x < scroll_x - X_OFFSCREEN_DISTANCE
213       || bbox.p1.x > scroll_x + X_OFFSCREEN_DISTANCE
214       || bbox.p2.y < scroll_y - Y_OFFSCREEN_DISTANCE
215       || bbox.p1.y > scroll_y + Y_OFFSCREEN_DISTANCE)
216     return true;
217
218   return false;
219 }
220
221 void
222 BadGuy::try_activate()
223 {
224   float scroll_x = Sector::current()->camera->get_translation().x;
225   float scroll_y = Sector::current()->camera->get_translation().y;
226
227   /* Activate badguys if they're just around the screen to avoid
228    * the effect of having badguys suddenly popping up from nowhere.
229    */
230   if (start_position.x > scroll_x - X_OFFSCREEN_DISTANCE &&
231       start_position.x < scroll_x - bbox.get_width() &&
232       start_position.y > scroll_y - Y_OFFSCREEN_DISTANCE &&
233       start_position.y < scroll_y + Y_OFFSCREEN_DISTANCE) {
234     dir = RIGHT;
235     set_state(STATE_ACTIVE);
236     activate();
237   } else if (start_position.x > scroll_x &&
238       start_position.x < scroll_x + X_OFFSCREEN_DISTANCE &&
239       start_position.y > scroll_y - Y_OFFSCREEN_DISTANCE &&
240       start_position.y < scroll_y + Y_OFFSCREEN_DISTANCE) {
241     dir = LEFT;
242     set_state(STATE_ACTIVE);
243     activate();
244   } else if (start_position.x > scroll_x - X_OFFSCREEN_DISTANCE &&
245       start_position.x < scroll_x + X_OFFSCREEN_DISTANCE &&
246       ((start_position.y > scroll_y &&
247         start_position.y < scroll_y + Y_OFFSCREEN_DISTANCE) ||
248        (start_position.y > scroll_y - Y_OFFSCREEN_DISTANCE &&
249         start_position.y < scroll_y))) {
250     dir = start_position.x < scroll_x ? RIGHT : LEFT;
251     set_state(STATE_ACTIVE);
252     activate();
253   } else if(state == STATE_INIT
254       && start_position.x > scroll_x - X_OFFSCREEN_DISTANCE
255       && start_position.x < scroll_x + X_OFFSCREEN_DISTANCE
256       && start_position.y > scroll_y - Y_OFFSCREEN_DISTANCE
257       && start_position.y < scroll_y + Y_OFFSCREEN_DISTANCE) {
258     dir = LEFT;
259     set_state(STATE_ACTIVE);
260     activate();
261   } 
262 }