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