enemies fall down again, small improvements to tile manager
[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   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 elapsed_time)
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)
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& other, const CollisionHit& hit)
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& other, const CollisionHit& hit)
143 {
144   return FORCE_MOVE;
145 }
146
147 bool
148 BadGuy::collision_squished(Player& 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.set_velocity_x(0);
159   physic.set_velocity_y(0);
160   set_state(STATE_SQUISHED);
161   player.bounce(*this);
162 }
163
164 void
165 BadGuy::kill_fall()
166 {
167   physic.set_velocity_y(0);
168   physic.enable_gravity(true);
169   set_state(STATE_FALLING);
170 }
171
172 void
173 BadGuy::set_state(State state)
174 {
175   if(this->state == state)
176     return;
177
178   State laststate = this->state;
179   this->state = state;
180   switch(state) {
181     case STATE_SQUISHED:
182       state_timer.start(SQUISH_TIME);
183       break;
184     case STATE_ACTIVE:
185       flags &= ~FLAG_NO_COLLDET;
186       bbox.set_pos(start_position);
187       break;
188     case STATE_INACTIVE:
189       // was the badguy dead anyway?
190       if(laststate == STATE_SQUISHED || laststate == STATE_SQUISHED) {
191         remove_me();
192       }
193       flags |= FLAG_NO_COLLDET;
194       break;
195     default:
196       break;
197   }
198 }
199
200 bool
201 BadGuy::is_offscreen()
202 {
203   float scroll_x = Sector::current()->camera->get_translation().x;
204   float scroll_y = Sector::current()->camera->get_translation().y;
205      
206   if(bbox.p2.x < scroll_x - X_OFFSCREEN_DISTANCE
207       || bbox.p1.x > scroll_x + X_OFFSCREEN_DISTANCE
208       || bbox.p2.y < scroll_y - Y_OFFSCREEN_DISTANCE
209       || bbox.p1.y > scroll_y + Y_OFFSCREEN_DISTANCE)
210     return true;
211
212   return false;
213 }
214
215 void
216 BadGuy::try_activate()
217 {
218   float scroll_x = Sector::current()->camera->get_translation().x;
219   float scroll_y = Sector::current()->camera->get_translation().y;
220
221   /* Activate badguys if they're just around the screen to avoid
222    * the effect of having badguys suddenly popping up from nowhere.
223    */
224   if (start_position.x > scroll_x - X_OFFSCREEN_DISTANCE &&
225       start_position.x < scroll_x - bbox.get_width() &&
226       start_position.y > scroll_y - Y_OFFSCREEN_DISTANCE &&
227       start_position.y < scroll_y + Y_OFFSCREEN_DISTANCE) {
228     dir = RIGHT;
229     set_state(STATE_ACTIVE);
230     activate();
231   } else if (start_position.x > scroll_x &&
232       start_position.x < scroll_x + X_OFFSCREEN_DISTANCE &&
233       start_position.y > scroll_y - Y_OFFSCREEN_DISTANCE &&
234       start_position.y < scroll_y + Y_OFFSCREEN_DISTANCE) {
235     dir = LEFT;
236     set_state(STATE_ACTIVE);
237     activate();
238   } else if (start_position.x > scroll_x - X_OFFSCREEN_DISTANCE &&
239       start_position.x < scroll_x + X_OFFSCREEN_DISTANCE &&
240       ((start_position.y > scroll_y &&
241         start_position.y < scroll_y + Y_OFFSCREEN_DISTANCE) ||
242        (start_position.y > scroll_y - Y_OFFSCREEN_DISTANCE &&
243         start_position.y < scroll_y))) {
244     dir = start_position.x < scroll_x ? RIGHT : LEFT;
245     set_state(STATE_ACTIVE);
246     activate();
247   } else if(state == STATE_INIT
248       && start_position.x > scroll_x - X_OFFSCREEN_DISTANCE
249       && start_position.x < scroll_x + X_OFFSCREEN_DISTANCE
250       && start_position.y > scroll_y - Y_OFFSCREEN_DISTANCE
251       && start_position.y < scroll_y + Y_OFFSCREEN_DISTANCE) {
252     dir = LEFT;
253     set_state(STATE_ACTIVE);
254     activate();
255   } 
256 }