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