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