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