-renamed ViewPort to Camera
[supertux.git] / src / badguy.cpp
1 //  $Id$
2 // 
3 //  SuperTux
4 //  Copyright (C) 2000 Bill Kendrick <bill@newbreedsoftware.com>
5 //  Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
6 //  Copyright (C) 2004 Matthias Braun <matze@braunis.de>
7 //
8 //  This program is free software; you can redistribute it and/or
9 //  modify it under the terms of the GNU General Public License
10 //  as published by the Free Software Foundation; either version 2
11 //  of the License, or (at your option) any later version.
12 //
13 //  This program is distributed in the hope that it will be useful,
14 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 //  GNU General Public License for more details.
17 // 
18 //  You should have received a copy of the GNU General Public License
19 //  along with this program; if not, write to the Free Software
20 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
21 //  02111-1307, USA.
22
23 #include <iostream>
24 #include <math.h>
25
26 #include "globals.h"
27 #include "defines.h"
28 #include "badguy.h"
29 #include "scene.h"
30 #include "screen.h"
31 #include "world.h"
32 #include "tile.h"
33 #include "resources.h"
34 #include "sprite_manager.h"
35 #include "gameloop.h"
36 #include "display_manager.h"
37 #include "lispwriter.h"
38 #include "camera.h"
39
40 Sprite* img_mriceblock_flat_left;
41 Sprite* img_mriceblock_flat_right;
42 Sprite* img_mriceblock_falling_left;
43 Sprite* img_mriceblock_falling_right;
44 Sprite* img_mriceblock_left;
45 Sprite* img_mriceblock_right;
46 Sprite* img_jumpy_left_up;
47 Sprite* img_jumpy_left_down;
48 Sprite* img_jumpy_left_middle;
49 Sprite* img_jumpy_left_iced;
50 Sprite* img_mrbomb_left;
51 Sprite* img_mrbomb_right;
52 Sprite* img_mrbomb_iced_left;
53 Sprite* img_mrbomb_iced_right;
54 Sprite* img_mrbomb_ticking_left;
55 Sprite* img_mrbomb_ticking_right;
56 Sprite* img_mrbomb_explosion;
57 Sprite* img_stalactite;
58 Sprite* img_stalactite_broken;
59 Sprite* img_flame;
60 Sprite* img_fish;
61 Sprite* img_fish_down;
62 Sprite* img_fish_iced;
63 Sprite* img_fish_iced_down;
64 Sprite* img_bouncingsnowball_left;
65 Sprite* img_bouncingsnowball_right;
66 Sprite* img_bouncingsnowball_squished;
67 Sprite* img_flyingsnowball;
68 Sprite* img_flyingsnowball_squished;
69 Sprite* img_spiky_left;
70 Sprite* img_spiky_right;
71 Sprite* img_spiky_iced_left;
72 Sprite* img_spiky_iced_right;
73 Sprite* img_snowball_left;
74 Sprite* img_snowball_right;
75 Sprite* img_snowball_squished_left;
76 Sprite* img_snowball_squished_right;
77
78 #define BADGUY_WALK_SPEED .8f
79
80 BadGuyKind  badguykind_from_string(const std::string& str)
81 {
82   if (str == "money" || str == "jumpy") // was money in old maps
83     return BAD_JUMPY;
84   else if (str == "laptop" || str == "mriceblock") // was laptop in old maps
85     return BAD_MRICEBLOCK;
86   else if (str == "mrbomb")
87     return BAD_MRBOMB;
88   else if (str == "stalactite")
89     return BAD_STALACTITE;
90   else if (str == "flame")
91     return BAD_FLAME;
92   else if (str == "fish")
93     return BAD_FISH;
94   else if (str == "bouncingsnowball")
95     return BAD_BOUNCINGSNOWBALL;
96   else if (str == "flyingsnowball")
97     return BAD_FLYINGSNOWBALL;
98   else if (str == "spiky")
99     return BAD_SPIKY;
100   else if (str == "snowball" || str == "bsod") // was bsod in old maps
101     return BAD_SNOWBALL;
102   else
103     {
104       printf("Couldn't convert badguy: '%s'\n", str.c_str());
105       return BAD_SNOWBALL;
106     }
107 }
108
109 std::string badguykind_to_string(BadGuyKind kind)
110 {
111   switch(kind)
112     {
113     case BAD_JUMPY:
114       return "jumpy";
115       break;
116     case BAD_MRICEBLOCK:
117       return "mriceblock";
118       break;
119     case BAD_MRBOMB:
120       return "mrbomb";
121       break;
122     case BAD_STALACTITE:
123       return "stalactite";
124       break;
125     case BAD_FLAME:
126       return "flame";
127       break;
128     case BAD_FISH:
129       return "fish";
130       break;
131     case BAD_BOUNCINGSNOWBALL:
132       return "bouncingsnowball";
133       break;
134     case BAD_FLYINGSNOWBALL:
135       return "flyingsnowball";
136       break;
137     case BAD_SPIKY:
138       return "spiky";
139       break;
140     case BAD_SNOWBALL:
141       return "snowball";
142       break;
143     default:
144       return "snowball";
145     }
146 }
147
148 BadGuy::BadGuy(DisplayManager& display_manager, BadGuyKind kind_,
149     LispReader& lispreader)
150   : removable(false), squishcount(0)
151 {
152   display_manager.add_drawable(this, LAYER_OBJECTS);
153
154   base.x = 0;
155   base.y = 0;
156   lispreader.read_float("x", &base.x);
157   lispreader.read_float("y", &base.y);
158   base.width  = 0;
159   base.height = 0;
160
161   kind     = kind_;
162
163   stay_on_platform = false;
164   lispreader.read_bool("stay-on-platform", &stay_on_platform);  
165
166   init();
167 }
168
169 BadGuy::BadGuy(DisplayManager& display_manager, BadGuyKind kind_,
170     float x, float y)
171 {
172   display_manager.add_drawable(this, LAYER_OBJECTS);
173
174   base.x = x;
175   base.y = y;
176   base.width  = 0;
177   base.height = 0;
178   stay_on_platform = false;
179
180   kind     = kind_;
181   
182   init();
183 }
184
185 BadGuy::~BadGuy()
186 {
187 }
188
189 void
190 BadGuy::init()
191 {
192   mode     = NORMAL;
193   dying    = DYING_NOT;
194   old_base = base;
195   dir      = LEFT;
196   seen     = false;
197   frozen_timer.init(true);
198   animation_offset = 0;
199   sprite_left = sprite_right = 0;
200   physic.reset();
201   timer.init(true);
202
203   if(kind == BAD_MRBOMB) {
204     physic.set_velocity(-BADGUY_WALK_SPEED, 0);
205     set_sprite(img_mrbomb_left, img_mrbomb_right);
206   } else if (kind == BAD_MRICEBLOCK) {
207     physic.set_velocity(-BADGUY_WALK_SPEED, 0);
208     set_sprite(img_mriceblock_left, img_mriceblock_right);
209   } else if(kind == BAD_JUMPY) {
210     set_sprite(img_jumpy_left_up, img_jumpy_left_up);
211   } else if(kind == BAD_BOMB) {
212     set_sprite(img_mrbomb_ticking_left, img_mrbomb_ticking_right);
213     // hack so that the bomb doesn't hurt until it expldes...
214     dying = DYING_SQUISHED;
215   } else if(kind == BAD_FLAME) {
216     angle = 0;
217     physic.enable_gravity(false);
218     set_sprite(img_flame, img_flame);
219   } else if(kind == BAD_BOUNCINGSNOWBALL) {
220     physic.set_velocity(-1.3, 0);
221     set_sprite(img_bouncingsnowball_left, img_bouncingsnowball_right);
222   } else if(kind == BAD_STALACTITE) {
223     physic.enable_gravity(false);
224     set_sprite(img_stalactite, img_stalactite);
225   } else if(kind == BAD_FISH) {
226     set_sprite(img_fish, img_fish);
227     physic.enable_gravity(true);
228   } else if(kind == BAD_FLYINGSNOWBALL) {
229     set_sprite(img_flyingsnowball, img_flyingsnowball);
230     physic.enable_gravity(false);
231   } else if(kind == BAD_SPIKY) {
232     physic.set_velocity(-BADGUY_WALK_SPEED, 0);
233     set_sprite(img_spiky_left, img_spiky_right);
234   } else if(kind == BAD_SNOWBALL) {
235     physic.set_velocity(-BADGUY_WALK_SPEED, 0);
236     set_sprite(img_snowball_left, img_snowball_right);
237   }
238
239   // if we're in a solid tile at start correct that now
240   if(kind != BAD_FLAME && kind != BAD_FISH && collision_object_map(base)) 
241     {
242       std::cout << "Warning: badguy started in wall: kind: " << badguykind_to_string(kind) 
243                 << " pos: (" << base.x << ", " << base.y << ")" << std::endl;
244       while(collision_object_map(base))
245         --base.y;
246     }
247 }
248
249 void
250 BadGuy::write(LispWriter& writer)
251 {
252   writer.start_list(badguykind_to_string(kind));
253
254   writer.write_float("x", base.x);
255   writer.write_float("y", base.y);
256   writer.write_bool("stay-on-platform", stay_on_platform);  
257
258   writer.end_list(badguykind_to_string(kind));
259 }
260
261 void
262 BadGuy::action_mriceblock(double elapsed_time)
263 {
264   Player& tux = *World::current()->get_tux();
265
266   if(mode != HELD)
267     fall();
268   
269   /* Move left/right: */
270   if (mode != HELD)
271     {
272       // move
273       physic.apply(elapsed_time, base.x, base.y);
274       if (dying != DYING_FALLING)
275         collision_swept_object_map(&old_base,&base);
276     }
277   else if (mode == HELD)
278     { /* FIXME: The pbad object shouldn't know about pplayer objects. */
279       /* If we're holding the iceblock */
280       dir = tux.dir;
281       if(dir==RIGHT)
282         {
283           base.x = tux.base.x + 16;
284           base.y = tux.base.y + tux.base.height/1.5 - base.height;
285         }
286       else /* facing left */
287         {
288           base.x = tux.base.x - 16;
289           base.y = tux.base.y + tux.base.height/1.5 - base.height;
290         }
291       if(collision_object_map(base))
292         {
293           base.x = tux.base.x;
294           base.y = tux.base.y + tux.base.height/1.5 - base.height;
295         }
296
297       if(tux.input.fire != DOWN) /* SHOOT! */
298         {
299           if(dir == LEFT)
300             base.x -= 24;
301           else
302             base.x += 24;
303           old_base = base;
304
305           mode=KICK;
306           tux.kick_timer.start(KICKING_TIME);
307           set_sprite(img_mriceblock_flat_left, img_mriceblock_flat_right);
308           physic.set_velocity_x((dir == LEFT) ? -3.5 : 3.5);
309           play_sound(sounds[SND_KICK],SOUND_CENTER_SPEAKER);
310         }
311     }
312
313   if (!dying)
314     {
315       int changed = dir;
316       check_horizontal_bump();
317       if(mode == KICK && changed != dir)
318         {
319           float scroll_x = World::current()->camera->get_translation().x;
320           
321           /* handle stereo sound (number 10 should be tweaked...)*/
322           if (base.x < scroll_x + screen->w/2 - 10)
323             play_sound(sounds[SND_RICOCHET], SOUND_LEFT_SPEAKER);
324           else if (base.x > scroll_x + screen->w/2 + 10)
325             play_sound(sounds[SND_RICOCHET], SOUND_RIGHT_SPEAKER);
326           else
327             play_sound(sounds[SND_RICOCHET], SOUND_CENTER_SPEAKER);
328         }
329     }
330
331   /* Handle mode timer: */
332   if (mode == FLAT)
333     {
334       if(!timer.check())
335         {
336           mode = NORMAL;
337           set_sprite(img_mriceblock_left, img_mriceblock_right);
338           physic.set_velocity( (dir == LEFT) ? -.8 : .8, 0);
339         }
340     }
341 }
342
343 void
344 BadGuy::check_horizontal_bump(bool checkcliff)
345 {
346     float halfheight = base.height / 2;
347     if (dir == LEFT && issolid( base.x, (int) base.y + halfheight))
348     {
349         if (kind == BAD_MRICEBLOCK && mode == KICK)
350             World::current()->trybreakbrick(base.x, (int) base.y + halfheight, false);
351             
352         dir = RIGHT;
353         physic.set_velocity(-physic.get_velocity_x(), physic.get_velocity_y());
354         return;
355     }
356     if (dir == RIGHT && issolid( base.x + base.width, (int)base.y + halfheight))
357     {
358         if (kind == BAD_MRICEBLOCK && mode == KICK)
359             World::current()->trybreakbrick(base.x + base.width, (int) base.y + halfheight, false);
360             
361         dir = LEFT;
362         physic.set_velocity(-physic.get_velocity_x(), physic.get_velocity_y());
363         return;
364     }
365
366     // don't check for cliffs when we're falling
367     if(!checkcliff)
368         return;
369     if(!issolid(base.x + base.width/2, base.y + base.height))
370         return;
371     
372     if(dir == LEFT && !issolid(base.x, (int) base.y + base.height + halfheight))
373     {
374         dir = RIGHT;
375         physic.set_velocity(-physic.get_velocity_x(), physic.get_velocity_y());
376         return;
377     }
378     if(dir == RIGHT && !issolid(base.x + base.width,
379                 (int) base.y + base.height + halfheight))
380     {
381         dir = LEFT;
382         physic.set_velocity(-physic.get_velocity_x(), physic.get_velocity_y());
383         return;
384     }
385 }
386
387 void
388 BadGuy::fall()
389 {
390   /* Fall if we get off the ground: */
391   if (dying != DYING_FALLING)
392     {
393       if (!issolid(base.x+base.width/2, base.y + base.height))
394         {
395           // not solid below us? enable gravity
396           physic.enable_gravity(true);
397         }
398       else
399         {
400           /* Land: */
401           if (physic.get_velocity_y() < 0)
402             {
403               base.y = int((base.y + base.height)/32) * 32 - base.height;
404               physic.set_velocity_y(0);
405             }
406           // no gravity anymore please
407           physic.enable_gravity(false);
408
409           if (stay_on_platform && mode == NORMAL)
410             {
411               if (!issolid(base.x + ((dir == LEFT) ? 0 : base.width),
412                            base.y + base.height))
413                 {
414                   if (dir == LEFT)
415                   {
416                     dir = RIGHT;
417                     physic.set_velocity_x(fabsf(physic.get_velocity_x()));
418                   } 
419                   else
420                   {
421                     dir = LEFT;
422                     physic.set_velocity_x(-fabsf(physic.get_velocity_x()));
423                   }
424                 }
425             }
426         }
427     }
428   else
429     {
430       physic.enable_gravity(true);
431     }
432 }
433
434 void
435 BadGuy::action_jumpy(double elapsed_time)
436 {
437   if(frozen_timer.check())
438     {
439     set_sprite(img_jumpy_left_iced, img_jumpy_left_iced);
440     return;
441     }
442
443   const float vy = physic.get_velocity_y();
444
445   // XXX: These tests *should* use location from ground, not velocity
446   if (fabsf(vy) > 5.6f)
447     set_sprite(img_jumpy_left_down, img_jumpy_left_down);
448   else if (fabsf(vy) > 5.3f)
449     set_sprite(img_jumpy_left_middle, img_jumpy_left_middle);
450   else
451     set_sprite(img_jumpy_left_up, img_jumpy_left_up);
452
453   Player& tux = *World::current()->get_tux();
454
455   static const float JUMPV = 6;
456     
457   fall();
458   // jump when on ground
459   if(dying == DYING_NOT && issolid(base.x, base.y+32))
460     {
461       physic.set_velocity_y(JUMPV);
462       physic.enable_gravity(true);
463
464       mode = JUMPY_JUMP;
465     }
466   else if(mode == JUMPY_JUMP)
467     {
468       mode = NORMAL;
469     }
470
471   // set direction based on tux
472   if(tux.base.x > base.x)
473     dir = RIGHT;
474   else
475     dir = LEFT;
476
477   // move
478   physic.apply(elapsed_time, base.x, base.y);
479   if(dying == DYING_NOT)
480     collision_swept_object_map(&old_base, &base);
481 }
482
483 void
484 BadGuy::action_mrbomb(double elapsed_time)
485 {
486   if(frozen_timer.check())
487     {
488     set_sprite(img_mrbomb_iced_left, img_mrbomb_iced_right);
489     return;
490     }
491
492   if (dying == DYING_NOT)
493     check_horizontal_bump(true);
494
495   fall();
496
497   physic.apply(elapsed_time, base.x, base.y);
498   if (dying != DYING_FALLING)
499     collision_swept_object_map(&old_base,&base); 
500 }
501
502 void
503 BadGuy::action_bomb(double elapsed_time)
504 {
505   static const int TICKINGTIME = 1000;
506   static const int EXPLODETIME = 1000;
507     
508   fall();
509
510   if(mode == NORMAL) {
511     mode = BOMB_TICKING;
512     timer.start(TICKINGTIME);
513   } else if(!timer.check()) {
514     if(mode == BOMB_TICKING) {
515       mode = BOMB_EXPLODE;
516       set_sprite(img_mrbomb_explosion, img_mrbomb_explosion);
517       dying = DYING_NOT; // now the bomb hurts
518       timer.start(EXPLODETIME);
519
520       float scroll_x = World::current()->camera->get_translation().x;                 
521
522       /* play explosion sound */  // FIXME: is the stereo all right? maybe we should use player cordinates...
523       if (base.x < scroll_x + screen->w/2 - 10)
524         play_sound(sounds[SND_EXPLODE], SOUND_LEFT_SPEAKER);
525       else if (base.x > scroll_x + screen->w/2 + 10)
526         play_sound(sounds[SND_EXPLODE], SOUND_RIGHT_SPEAKER);
527       else
528         play_sound(sounds[SND_EXPLODE], SOUND_CENTER_SPEAKER);
529
530     } else if(mode == BOMB_EXPLODE) {
531       remove_me();
532       return;
533     }
534   }
535
536   // move
537   physic.apply(elapsed_time, base.x, base.y);                 
538   collision_swept_object_map(&old_base,&base);
539 }
540
541 void
542 BadGuy::action_stalactite(double elapsed_time)
543 {
544   Player& tux = *World::current()->get_tux();
545
546   static const int SHAKETIME = 800;
547   static const int RANGE = 40;
548     
549   if(mode == NORMAL) {
550     // start shaking when tux is below the stalactite and at least 40 pixels
551     // near
552     if(tux.base.x + 32 > base.x - RANGE && tux.base.x < base.x + 32 + RANGE
553             && tux.base.y + tux.base.height > base.y) {
554       timer.start(SHAKETIME);
555       mode = STALACTITE_SHAKING;
556     }
557   } if(mode == STALACTITE_SHAKING) {
558     base.x = old_base.x + (rand() % 6) - 3; // TODO this could be done nicer...
559     if(!timer.check()) {
560       mode = STALACTITE_FALL;
561     }
562   } else if(mode == STALACTITE_FALL) {
563     fall();
564     /* Destroy if we collides with land */
565     if(issolid(base.x+base.width/2, base.y+base.height))
566     {
567       timer.start(2000);
568       dying = DYING_SQUISHED;
569       mode = FLAT;
570       set_sprite(img_stalactite_broken, img_stalactite_broken);
571     }
572   } else if(mode == FLAT) {
573     fall();
574   }
575
576   // move
577   physic.apply(elapsed_time, base.x, base.y);
578
579   if(dying == DYING_SQUISHED && !timer.check())
580     remove_me();
581 }
582
583 void
584 BadGuy::action_flame(double elapsed_time)
585 {
586     static const float radius = 100;
587     static const float speed = 0.02;
588     base.x = old_base.x + cos(angle) * radius;
589     base.y = old_base.y + sin(angle) * radius;
590
591     angle = fmodf(angle + elapsed_time * speed, 2*M_PI);
592 }
593
594 void
595 BadGuy::action_fish(double elapsed_time)
596 {
597   if(frozen_timer.check())
598     {
599     if(physic.get_velocity_y() < 0)
600       set_sprite(img_fish_iced_down, img_fish_iced_down);
601     else
602       set_sprite(img_fish_iced, img_fish_iced);
603
604     return;
605     }
606
607   static const float JUMPV = 6;
608   static const int WAITTIME = 1000;
609     
610   // go in wait mode when back in water
611   if(dying == DYING_NOT && gettile(base.x, base.y+ base.height)->water
612         && physic.get_velocity_y() <= 0 && mode == NORMAL)
613     {
614       mode = FISH_WAIT;
615       set_sprite(0, 0);
616       physic.set_velocity(0, 0);
617       physic.enable_gravity(false);
618       timer.start(WAITTIME);
619     }
620   else if(mode == FISH_WAIT && !timer.check())
621     {
622       // jump again
623       set_sprite(img_fish, img_fish);
624       mode = NORMAL;
625       physic.set_velocity(0, JUMPV);
626       physic.enable_gravity(true);
627     }
628
629   physic.apply(elapsed_time, base.x, base.y);
630   if(dying == DYING_NOT)
631     collision_swept_object_map(&old_base, &base);
632
633   if(physic.get_velocity_y() < 0)
634     set_sprite(img_fish_down, img_fish_down);
635 }
636
637 void
638 BadGuy::action_bouncingsnowball(double elapsed_time)
639 {
640   static const float JUMPV = 4.5;
641     
642   fall();
643
644   // jump when on ground
645   if(dying == DYING_NOT && issolid(base.x, base.y+32))
646     {
647       physic.set_velocity_y(JUMPV);
648       physic.enable_gravity(true);
649     }                                                     
650   else
651     {
652       mode = NORMAL;
653     }
654
655   // check for right/left collisions
656   check_horizontal_bump();
657
658   physic.apply(elapsed_time, base.x, base.y);
659   if(dying == DYING_NOT)
660     collision_swept_object_map(&old_base, &base);
661
662   // Handle dying timer:
663   if (dying == DYING_SQUISHED && !timer.check())
664     {
665       /* Remove it if time's up: */
666       remove_me();
667       return;
668     }
669 }
670
671 void
672 BadGuy::action_flyingsnowball(double elapsed_time)
673 {
674   static const float FLYINGSPEED = 1;
675   static const int DIRCHANGETIME = 1000;
676     
677   // go into flyup mode if none specified yet
678   if(dying == DYING_NOT && mode == NORMAL) {
679     mode = FLY_UP;
680     physic.set_velocity_y(FLYINGSPEED);
681     timer.start(DIRCHANGETIME/2);
682   }
683
684   if(dying == DYING_NOT && !timer.check()) {
685     if(mode == FLY_UP) {
686       mode = FLY_DOWN;
687       physic.set_velocity_y(-FLYINGSPEED);
688     } else if(mode == FLY_DOWN) {
689       mode = FLY_UP;
690       physic.set_velocity_y(FLYINGSPEED);
691     }
692     timer.start(DIRCHANGETIME);
693   }
694
695   if(dying != DYING_NOT)
696     physic.enable_gravity(true);
697
698   physic.apply(elapsed_time, base.x, base.y);
699   if(dying == DYING_NOT || dying == DYING_SQUISHED)
700     collision_swept_object_map(&old_base, &base);
701
702   // Handle dying timer:
703   if (dying == DYING_SQUISHED && !timer.check())
704     {
705       /* Remove it if time's up: */
706       remove_me();
707       return;
708     }                                                          
709 }
710
711 void
712 BadGuy::action_spiky(double elapsed_time)
713 {
714   if(frozen_timer.check())
715     {
716     set_sprite(img_spiky_iced_left, img_spiky_iced_right);
717     return;
718     }
719
720   if (dying == DYING_NOT)
721     check_horizontal_bump();
722
723   fall();
724 #if 0
725   // jump when we're about to fall
726   if (physic.get_velocity_y() == 0 && 
727           !issolid(base.x+base.width/2, base.y + base.height)) {
728     physic.enable_gravity(true);
729     physic.set_velocity_y(2);
730   }
731 #endif
732
733   physic.apply(elapsed_time, base.x, base.y);
734   if (dying != DYING_FALLING)
735     collision_swept_object_map(&old_base,&base);   
736 }
737
738 void
739 BadGuy::action_snowball(double elapsed_time)
740 {
741   if (dying == DYING_NOT)
742     check_horizontal_bump();
743
744   fall();
745
746   physic.apply(elapsed_time, base.x, base.y);
747   if (dying != DYING_FALLING)
748     collision_swept_object_map(&old_base,&base);
749 }
750
751 void
752 BadGuy::action(float elapsed_time)
753 {
754   float scroll_x = World::current()->camera->get_translation().x;
755   float scroll_y = World::current()->camera->get_translation().y;
756   
757   // BadGuy fall below the ground
758   if (base.y > World::current()->get_level()->height * 32) {
759     remove_me();
760     return;
761   }
762
763   // Kill us if we landed on spikes
764   if (dying == DYING_NOT
765       && (kind != BAD_STALACTITE && kind != BAD_FLAME && kind != BAD_BOMB)
766       && (isspike(base.x, base.y) || isspike(base.x + base.width, base.y)
767       ||  isspike(base.x, base.y + base.height)
768       ||  isspike(base.x + base.width, base.y + base.height)))
769       {
770          physic.set_velocity_y(3);
771          kill_me(0);
772       }
773
774   // Once it's on screen, it's activated!
775   if (base.x > scroll_x - X_OFFSCREEN_DISTANCE &&
776        base.x < scroll_x + screen->w + X_OFFSCREEN_DISTANCE &&
777        base.y > scroll_y - Y_OFFSCREEN_DISTANCE &&
778        base.y < scroll_y + screen->h + Y_OFFSCREEN_DISTANCE)
779     seen = true;
780
781   if(!seen)
782     return;
783
784   switch (kind)
785     {
786     case BAD_MRICEBLOCK:
787       action_mriceblock(elapsed_time);
788       break;
789   
790     case BAD_JUMPY:
791       action_jumpy(elapsed_time);
792       break;
793
794     case BAD_MRBOMB:
795       action_mrbomb(elapsed_time);
796       break;
797     
798     case BAD_BOMB:
799       action_bomb(elapsed_time);
800       break;
801
802     case BAD_STALACTITE:
803       action_stalactite(elapsed_time);
804       break;
805
806     case BAD_FLAME:
807       action_flame(elapsed_time);
808       break;
809
810     case BAD_FISH:
811       action_fish(elapsed_time);
812       break;
813
814     case BAD_BOUNCINGSNOWBALL:
815       action_bouncingsnowball(elapsed_time);
816       break;
817
818     case BAD_FLYINGSNOWBALL:
819       action_flyingsnowball(elapsed_time);
820       break;
821
822     case BAD_SPIKY:
823       action_spiky(elapsed_time);
824       break;
825
826     case BAD_SNOWBALL:
827       action_snowball(elapsed_time);
828       break;
829     default:
830       break;
831     }
832 }
833
834 void
835 BadGuy::draw(Camera& viewport, int)
836 {
837   float scroll_x = viewport.get_translation().x;
838   float scroll_y = viewport.get_translation().y;
839
840   // Don't try to draw stuff that is outside of the screen
841   if(base.x <= scroll_x - base.width || base.x >= scroll_x + screen->w)
842     return;
843   
844   if(sprite_left == 0 || sprite_right == 0)
845     {
846       return;
847     }
848
849   Sprite* sprite = (dir == LEFT) ? sprite_left : sprite_right;
850   sprite->draw(viewport.world2screen(Vector(base.x, base.y)));
851
852   if (debug_mode)
853     fillrect(base.x - scroll_x, base.y - scroll_y, base.width, base.height, 75,0,75, 150);
854 }
855
856 void
857 BadGuy::set_sprite(Sprite* left, Sprite* right) 
858 {
859   if (1)
860     {
861       base.width = 32;
862       base.height = 32;
863     }
864   else
865     {
866       // FIXME: Using the image size for the physics and collision is
867       // a bad idea, since images should always overlap there physical
868       // representation
869       if(left != 0) {
870         if(base.width == 0 && base.height == 0) {
871           base.width  = left->get_width();
872           base.height = left->get_height();
873         } else if(base.width != left->get_width() || base.height != left->get_height()) {
874           base.x -= (left->get_width() - base.width) / 2;
875           base.y -= left->get_height() - base.height;
876           base.width = left->get_width();
877           base.height = left->get_height();
878           old_base = base;
879         }
880       } else {
881         base.width = base.height = 0;
882       }
883     }
884
885   animation_offset = 0;
886   sprite_left  = left;
887   sprite_right = right;
888 }
889
890 void
891 BadGuy::bump()
892 {
893   // these can't be bumped
894   if(kind == BAD_FLAME || kind == BAD_BOMB || kind == BAD_FISH
895       || kind == BAD_FLYINGSNOWBALL)
896     return;
897
898   physic.set_velocity_y(3);
899   kill_me(25);
900 }
901
902 void
903 BadGuy::make_player_jump(Player* player)
904 {
905   player->physic.set_velocity_y(2);
906   player->base.y = base.y - player->base.height - 2;
907 }
908
909 void
910 BadGuy::squish_me(Player* player)
911 {
912   make_player_jump(player);
913     
914   World::current()->add_score(Vector(base.x, base.y),
915                               50 * player_status.score_multiplier);
916   play_sound(sounds[SND_SQUISH], SOUND_CENTER_SPEAKER);
917   player_status.score_multiplier++;
918
919   dying = DYING_SQUISHED;
920   timer.start(2000);
921   physic.set_velocity(0, 0);
922 }
923
924 void
925 BadGuy::squish(Player* player)
926 {
927   static const int MAX_ICEBLOCK_SQUICHES = 10;
928     
929   if(kind == BAD_MRBOMB) {
930     // mrbomb transforms into a bomb now
931     explode();
932     
933     make_player_jump(player);
934     World::current()->add_score(Vector(base.x, base.y),
935                                 50 * player_status.score_multiplier);
936     play_sound(sounds[SND_SQUISH], SOUND_CENTER_SPEAKER);
937     player_status.score_multiplier++;
938     return;
939
940   } else if (kind == BAD_MRICEBLOCK) {
941     if (mode == NORMAL || mode == KICK)
942       {
943         /* Flatten! */
944         play_sound(sounds[SND_STOMP], SOUND_CENTER_SPEAKER);
945         mode = FLAT;
946         set_sprite(img_mriceblock_flat_left, img_mriceblock_flat_right);
947         physic.set_velocity_x(0);
948
949         timer.start(4000);
950       } else if (mode == FLAT) {
951         /* Kick! */
952         play_sound(sounds[SND_KICK], SOUND_CENTER_SPEAKER);
953
954         if (player->base.x < base.x + (base.width/2)) {
955           physic.set_velocity_x(5);
956           dir = RIGHT;
957         } else {
958           physic.set_velocity_x(-5);
959           dir = LEFT;
960         }
961
962         mode = KICK;
963         player->kick_timer.start(KICKING_TIME);
964         set_sprite(img_mriceblock_flat_left, img_mriceblock_flat_right);
965       }
966
967     make_player_jump(player);
968
969     player_status.score_multiplier++;
970
971     // check for maximum number of squiches
972     squishcount++;
973     if(squishcount >= MAX_ICEBLOCK_SQUICHES) {
974       kill_me(50);
975       return;
976     }
977     
978     return;
979   } else if(kind == BAD_FISH) {
980     // fish can only be killed when falling down
981     if(physic.get_velocity_y() >= 0)
982       return;
983       
984     make_player_jump(player);
985               
986     World::current()->add_score(Vector(base.x, base.y),
987                                 25 * player_status.score_multiplier);
988     player_status.score_multiplier++;
989      
990     // simply remove the fish...
991     remove_me();
992     return;
993   } else if(kind == BAD_BOUNCINGSNOWBALL) {
994     squish_me(player);
995     set_sprite(img_bouncingsnowball_squished,img_bouncingsnowball_squished);
996     return;
997   } else if(kind == BAD_FLYINGSNOWBALL) {
998     squish_me(player);
999     set_sprite(img_flyingsnowball_squished,img_flyingsnowball_squished);
1000     return;
1001   } else if(kind == BAD_SNOWBALL) {
1002     squish_me(player);
1003     set_sprite(img_snowball_squished_left, img_snowball_squished_right);
1004     return;
1005   }
1006 }
1007
1008 void
1009 BadGuy::kill_me(int score)
1010 {
1011   if(kind == BAD_BOMB)
1012     return;
1013
1014   dying = DYING_FALLING;
1015   if(kind == BAD_MRICEBLOCK) {
1016     set_sprite(img_mriceblock_falling_left, img_mriceblock_falling_right);
1017     if(mode == HELD) {
1018       mode = NORMAL;
1019       Player& tux = *World::current()->get_tux();  
1020       tux.holding_something = false;
1021     }
1022   }
1023
1024   physic.enable_gravity(true);
1025
1026   /* Gain some points: */
1027   if (score != 0)
1028     World::current()->add_score(Vector(base.x, base.y),
1029                                 score * player_status.score_multiplier);
1030
1031   /* Play death sound: */
1032   play_sound(sounds[SND_FALL], SOUND_CENTER_SPEAKER);
1033 }
1034
1035 void
1036 BadGuy::explode()
1037 {
1038   World::current()->add_bad_guy(base.x, base.y, BAD_BOMB);
1039   remove_me();
1040 }
1041
1042 void
1043 BadGuy::collision(const MovingObject&, int)
1044 {
1045   // later
1046 }
1047
1048 void
1049 BadGuy::collision(void *p_c_object, int c_object, CollisionType type)
1050 {
1051   BadGuy* pbad_c    = NULL;
1052   Bullet* pbullet_c = NULL;
1053
1054   if(type == COLLISION_BUMP) {
1055     bump();
1056     return;
1057   }
1058
1059   if(type == COLLISION_SQUISH) {
1060     Player* player = static_cast<Player*>(p_c_object);
1061     squish(player);
1062     return;
1063   }
1064
1065   /* COLLISION_NORMAL */
1066   switch (c_object)
1067     {
1068     case CO_BULLET:
1069       pbullet_c = (Bullet*) p_c_object;
1070
1071       if(pbullet_c->kind == FIRE_BULLET)
1072         {
1073         if (kind != BAD_BOMB && kind != BAD_STALACTITE && kind != BAD_FLAME)
1074           kill_me(10);
1075         }
1076       else if(pbullet_c->kind == ICE_BULLET)
1077         {
1078         //if(kind == BAD_FLAME)
1079         //  kill_me(10);
1080         //else
1081           frozen_timer.start(FROZEN_TIME);
1082         }
1083       break;
1084
1085     case CO_BADGUY:
1086       pbad_c = (BadGuy*) p_c_object;
1087
1088       /* If we're a kicked mriceblock, kill [almost] any badguys we hit */
1089       if(kind == BAD_MRICEBLOCK && mode == KICK &&
1090          kind != BAD_FLAME && kind != BAD_BOMB && kind != BAD_STALACTITE)
1091         {
1092           pbad_c->kill_me(25);
1093         }
1094
1095       // a held mriceblock kills the enemy too but falls to ground then
1096       else if(kind == BAD_MRICEBLOCK && mode == HELD)
1097         {
1098           pbad_c->kill_me(25);
1099           kill_me(0);
1100         }
1101
1102       /* Kill badguys that run into exploding bomb */
1103       else if (kind == BAD_BOMB && dying == DYING_NOT)
1104       {
1105         if (pbad_c->kind == BAD_MRBOMB)
1106         {
1107           // mrbomb transforms into a bomb now
1108           pbad_c->explode();
1109           return;
1110         }
1111         else if (pbad_c->kind != BAD_MRBOMB)
1112         {
1113           pbad_c->kill_me(50);
1114         }
1115       }
1116
1117       /* Kill any badguys that get hit by stalactite */
1118       else if (kind == BAD_STALACTITE && dying == DYING_NOT)
1119       {
1120         if (pbad_c->kind == BAD_MRBOMB)
1121         {
1122           // mrbomb transforms into a bomb now
1123           pbad_c->explode();
1124           return;
1125         }
1126         else
1127           pbad_c->kill_me(50);
1128       }
1129
1130       /* When enemies run into eachother, make them change directions */
1131       else
1132       {
1133         // Jumpy, fish, flame, stalactites are exceptions
1134         if (pbad_c->kind == BAD_JUMPY || pbad_c->kind == BAD_FLAME
1135             || pbad_c->kind == BAD_STALACTITE || pbad_c->kind == BAD_FISH)
1136           break;
1137
1138         // Bounce off of other badguy if we land on top of him
1139         if (base.y + base.height < pbad_c->base.y + pbad_c->base.height)
1140         {
1141           if (pbad_c->dir == LEFT)
1142           {
1143             dir = RIGHT;
1144             physic.set_velocity(fabsf(physic.get_velocity_x()), 2);
1145           }
1146           else if (pbad_c->dir == RIGHT)
1147           {
1148             dir = LEFT;
1149             physic.set_velocity(-fabsf(physic.get_velocity_x()), 2);
1150           }
1151
1152           break;
1153         }
1154         else if (base.y + base.height > pbad_c->base.y + pbad_c->base.height)
1155           break;
1156
1157         if (pbad_c->kind != BAD_FLAME)
1158           {
1159             if (dir == LEFT)
1160             {
1161               dir = RIGHT;
1162               physic.set_velocity_x(fabsf(physic.get_velocity_x()));
1163
1164               // in case badguys get "jammed"
1165               if (physic.get_velocity_x() != 0)
1166                 base.x = pbad_c->base.x + pbad_c->base.width;
1167             }
1168             else if (dir == RIGHT)
1169             {
1170               dir = LEFT;
1171               physic.set_velocity_x(-fabsf(physic.get_velocity_x()));
1172             }
1173
1174           }
1175       }
1176       
1177       break;
1178
1179     case CO_PLAYER:
1180       Player* player = static_cast<Player*>(p_c_object);
1181       /* Get kicked if were flat */
1182       if (mode == FLAT && !dying)
1183       {
1184         play_sound(sounds[SND_KICK], SOUND_CENTER_SPEAKER);
1185
1186         // Hit from left side
1187         if (player->base.x < base.x) {
1188           physic.set_velocity_x(5);
1189           dir = RIGHT;
1190         }
1191         // Hit from right side
1192         else {
1193           physic.set_velocity_x(-5);
1194           dir = LEFT;
1195         }
1196
1197         mode = KICK;
1198         player->kick_timer.start(KICKING_TIME);
1199         set_sprite(img_mriceblock_flat_left, img_mriceblock_flat_right);
1200       }
1201       break;
1202
1203     }
1204 }
1205
1206
1207 //---------------------------------------------------------------------------
1208
1209 void load_badguy_gfx()
1210 {
1211   img_mriceblock_flat_left = sprite_manager->load("mriceblock-flat-left");
1212   img_mriceblock_flat_right = sprite_manager->load("mriceblock-flat-right");
1213   img_mriceblock_falling_left = sprite_manager->load("mriceblock-falling-left");
1214   img_mriceblock_falling_right = sprite_manager->load("mriceblock-falling-right");
1215   img_mriceblock_left = sprite_manager->load("mriceblock-left");
1216   img_mriceblock_right = sprite_manager->load("mriceblock-right");
1217   img_jumpy_left_up = sprite_manager->load("jumpy-left-up");
1218   img_jumpy_left_down = sprite_manager->load("jumpy-left-down");
1219   img_jumpy_left_middle = sprite_manager->load("jumpy-left-middle");
1220   img_jumpy_left_iced = sprite_manager->load("jumpy-left-iced");
1221   img_mrbomb_left = sprite_manager->load("mrbomb-left");
1222   img_mrbomb_right = sprite_manager->load("mrbomb-right");
1223   img_mrbomb_iced_left = sprite_manager->load("mrbomb-iced-left");
1224   img_mrbomb_iced_right = sprite_manager->load("mrbomb-iced-right");
1225   img_mrbomb_ticking_left = sprite_manager->load("mrbomb-ticking-left");
1226   img_mrbomb_ticking_right = sprite_manager->load("mrbomb-ticking-right");
1227   img_mrbomb_explosion = sprite_manager->load("mrbomb-explosion");
1228   img_stalactite = sprite_manager->load("stalactite");
1229   img_stalactite_broken = sprite_manager->load("stalactite-broken");
1230   img_flame = sprite_manager->load("flame");
1231   img_fish = sprite_manager->load("fish");
1232   img_fish_down = sprite_manager->load("fish-down");
1233   img_fish_iced = sprite_manager->load("fish-iced");
1234   img_fish_iced_down = sprite_manager->load("fish-iced-down");
1235   img_bouncingsnowball_left = sprite_manager->load("bouncingsnowball-left");
1236   img_bouncingsnowball_right = sprite_manager->load("bouncingsnowball-right");
1237   img_bouncingsnowball_squished = sprite_manager->load("bouncingsnowball-squished");
1238   img_flyingsnowball = sprite_manager->load("flyingsnowball");
1239   img_flyingsnowball_squished = sprite_manager->load("flyingsnowball-squished");
1240   img_spiky_left = sprite_manager->load("spiky-left");
1241   img_spiky_right = sprite_manager->load("spiky-right");
1242   img_spiky_iced_left = sprite_manager->load("spiky-iced-left");
1243   img_spiky_iced_right = sprite_manager->load("spiky-iced-right");
1244   img_snowball_left = sprite_manager->load("snowball-left");
1245   img_snowball_right = sprite_manager->load("snowball-right");
1246   img_snowball_squished_left = sprite_manager->load("snowball-squished-left");
1247   img_snowball_squished_right = sprite_manager->load("snowball-squished-right");
1248 }
1249
1250 void free_badguy_gfx()
1251 {
1252 }
1253
1254 // EOF //