some cleanups memory leak fixes and moving of source files
[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)
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.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   SoundManager::get()->play_sound(IDToSound(SND_FALL), this,
168       Sector::current()->player->get_pos());
169   physic.set_velocity_y(0);
170   physic.enable_gravity(true);
171   set_state(STATE_FALLING);
172 }
173
174 void
175 BadGuy::set_state(State state)
176 {
177   if(this->state == state)
178     return;
179
180   State laststate = this->state;
181   this->state = state;
182   switch(state) {
183     case STATE_SQUISHED:
184       state_timer.start(SQUISH_TIME);
185       break;
186     case STATE_ACTIVE:
187       flags &= ~FLAG_NO_COLLDET;
188       bbox.set_pos(start_position);
189       break;
190     case STATE_INACTIVE:
191       // was the badguy dead anyway?
192       if(laststate == STATE_SQUISHED || laststate == STATE_SQUISHED) {
193         remove_me();
194       }
195       flags |= FLAG_NO_COLLDET;
196       break;
197     case STATE_FALLING:
198       flags |= FLAG_NO_COLLDET;
199       break;
200     default:
201       break;
202   }
203 }
204
205 bool
206 BadGuy::is_offscreen()
207 {
208   float scroll_x = Sector::current()->camera->get_translation().x;
209   float scroll_y = Sector::current()->camera->get_translation().y;
210      
211   if(bbox.p2.x < scroll_x - X_OFFSCREEN_DISTANCE
212       || bbox.p1.x > scroll_x + X_OFFSCREEN_DISTANCE
213       || bbox.p2.y < scroll_y - Y_OFFSCREEN_DISTANCE
214       || bbox.p1.y > scroll_y + Y_OFFSCREEN_DISTANCE)
215     return true;
216
217   return false;
218 }
219
220 void
221 BadGuy::try_activate()
222 {
223   float scroll_x = Sector::current()->camera->get_translation().x;
224   float scroll_y = Sector::current()->camera->get_translation().y;
225
226   /* Activate badguys if they're just around the screen to avoid
227    * the effect of having badguys suddenly popping up from nowhere.
228    */
229   if (start_position.x > scroll_x - X_OFFSCREEN_DISTANCE &&
230       start_position.x < scroll_x - bbox.get_width() &&
231       start_position.y > scroll_y - Y_OFFSCREEN_DISTANCE &&
232       start_position.y < scroll_y + Y_OFFSCREEN_DISTANCE) {
233     dir = RIGHT;
234     set_state(STATE_ACTIVE);
235     activate();
236   } else if (start_position.x > scroll_x &&
237       start_position.x < scroll_x + X_OFFSCREEN_DISTANCE &&
238       start_position.y > scroll_y - Y_OFFSCREEN_DISTANCE &&
239       start_position.y < scroll_y + Y_OFFSCREEN_DISTANCE) {
240     dir = LEFT;
241     set_state(STATE_ACTIVE);
242     activate();
243   } else if (start_position.x > scroll_x - X_OFFSCREEN_DISTANCE &&
244       start_position.x < scroll_x + X_OFFSCREEN_DISTANCE &&
245       ((start_position.y > scroll_y &&
246         start_position.y < scroll_y + Y_OFFSCREEN_DISTANCE) ||
247        (start_position.y > scroll_y - Y_OFFSCREEN_DISTANCE &&
248         start_position.y < scroll_y))) {
249     dir = start_position.x < scroll_x ? RIGHT : LEFT;
250     set_state(STATE_ACTIVE);
251     activate();
252   } else if(state == STATE_INIT
253       && start_position.x > scroll_x - X_OFFSCREEN_DISTANCE
254       && start_position.x < scroll_x + X_OFFSCREEN_DISTANCE
255       && start_position.y > scroll_y - Y_OFFSCREEN_DISTANCE
256       && start_position.y < scroll_y + Y_OFFSCREEN_DISTANCE) {
257     dir = LEFT;
258     set_state(STATE_ACTIVE);
259     activate();
260   } 
261 }