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