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