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