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