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