0bd1337412b7ce35b20306cea2150390580f8a02
[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   // BadGuy fall below the ground
762   if (base.y > World::current()->get_level()->height * 32) {
763     remove_me();
764     return;
765   }
766
767   // Kill us if we landed on spikes
768   if (dying == DYING_NOT
769       && (kind != BAD_STALACTITE && kind != BAD_FLAME && kind != BAD_BOMB)
770       && (isspike(base.x, base.y) || isspike(base.x + base.width, base.y)
771       ||  isspike(base.x, base.y + base.height)
772       ||  isspike(base.x + base.width, base.y + base.height)))
773       {
774          physic.set_velocity_y(3);
775          kill_me(0);
776       }
777
778   // Once it's on screen, it's activated!
779   if (base.x > scroll_x - X_OFFSCREEN_DISTANCE &&
780        base.x < scroll_x + screen->w + X_OFFSCREEN_DISTANCE &&
781        base.y > scroll_y - Y_OFFSCREEN_DISTANCE &&
782        base.y < scroll_y + screen->h + Y_OFFSCREEN_DISTANCE)
783     seen = true;
784
785   if(!seen)
786     return;
787
788   switch (kind)
789     {
790     case BAD_MRICEBLOCK:
791       action_mriceblock(elapsed_time);
792       break;
793   
794     case BAD_JUMPY:
795       action_jumpy(elapsed_time);
796       break;
797
798     case BAD_MRBOMB:
799       action_mrbomb(elapsed_time);
800       break;
801     
802     case BAD_BOMB:
803       action_bomb(elapsed_time);
804       break;
805
806     case BAD_STALACTITE:
807       action_stalactite(elapsed_time);
808       break;
809
810     case BAD_FLAME:
811       action_flame(elapsed_time);
812       break;
813
814     case BAD_FISH:
815       action_fish(elapsed_time);
816       break;
817
818     case BAD_BOUNCINGSNOWBALL:
819       action_bouncingsnowball(elapsed_time);
820       break;
821
822     case BAD_FLYINGSNOWBALL:
823       action_flyingsnowball(elapsed_time);
824       break;
825
826     case BAD_SPIKY:
827       action_spiky(elapsed_time);
828       break;
829
830     case BAD_SNOWBALL:
831       action_snowball(elapsed_time);
832       break;
833     default:
834       break;
835     }
836 }
837
838 void
839 BadGuy::draw(ViewPort& viewport, int)
840 {
841   float scroll_x = viewport.get_translation().x;
842   float scroll_y = viewport.get_translation().y;
843
844   // Don't try to draw stuff that is outside of the screen
845   if(base.x <= scroll_x - base.width || base.x >= scroll_x + screen->w)
846     return;
847   
848   if(sprite_left == 0 || sprite_right == 0)
849     {
850       return;
851     }
852
853   Sprite* sprite = (dir == LEFT) ? sprite_left : sprite_right;
854   sprite->draw(viewport.world2screen(Vector(base.x, base.y)));
855
856   if (debug_mode)
857     fillrect(base.x - scroll_x, base.y - scroll_y, base.width, base.height, 75,0,75, 150);
858 }
859
860 void
861 BadGuy::set_sprite(Sprite* left, Sprite* right) 
862 {
863   if (1)
864     {
865       base.width = 32;
866       base.height = 32;
867     }
868   else
869     {
870       // FIXME: Using the image size for the physics and collision is
871       // a bad idea, since images should always overlap there physical
872       // representation
873       if(left != 0) {
874         if(base.width == 0 && base.height == 0) {
875           base.width  = left->get_width();
876           base.height = left->get_height();
877         } else if(base.width != left->get_width() || base.height != left->get_height()) {
878           base.x -= (left->get_width() - base.width) / 2;
879           base.y -= left->get_height() - base.height;
880           base.width = left->get_width();
881           base.height = left->get_height();
882           old_base = base;
883         }
884       } else {
885         base.width = base.height = 0;
886       }
887     }
888
889   animation_offset = 0;
890   sprite_left  = left;
891   sprite_right = right;
892 }
893
894 void
895 BadGuy::bump()
896 {
897   // these can't be bumped
898   if(kind == BAD_FLAME || kind == BAD_BOMB || kind == BAD_FISH
899       || kind == BAD_FLYINGSNOWBALL)
900     return;
901
902   physic.set_velocity_y(3);
903   kill_me(25);
904 }
905
906 void
907 BadGuy::make_player_jump(Player* player)
908 {
909   player->physic.set_velocity_y(2);
910   player->base.y = base.y - player->base.height - 2;
911 }
912
913 void
914 BadGuy::squish_me(Player* player)
915 {
916   make_player_jump(player);
917     
918   World::current()->add_score(Vector(base.x, base.y),
919                               50 * player_status.score_multiplier);
920   play_sound(sounds[SND_SQUISH], SOUND_CENTER_SPEAKER);
921   player_status.score_multiplier++;
922
923   dying = DYING_SQUISHED;
924   timer.start(2000);
925   physic.set_velocity(0, 0);
926 }
927
928 void
929 BadGuy::squish(Player* player)
930 {
931   static const int MAX_ICEBLOCK_SQUICHES = 10;
932     
933   if(kind == BAD_MRBOMB) {
934     // mrbomb transforms into a bomb now
935     explode();
936     
937     make_player_jump(player);
938     World::current()->add_score(Vector(base.x, base.y),
939                                 50 * player_status.score_multiplier);
940     play_sound(sounds[SND_SQUISH], SOUND_CENTER_SPEAKER);
941     player_status.score_multiplier++;
942     return;
943
944   } else if (kind == BAD_MRICEBLOCK) {
945     if (mode == NORMAL || mode == KICK)
946       {
947         /* Flatten! */
948         play_sound(sounds[SND_STOMP], SOUND_CENTER_SPEAKER);
949         mode = FLAT;
950         set_sprite(img_mriceblock_flat_left, img_mriceblock_flat_right);
951         physic.set_velocity_x(0);
952
953         timer.start(4000);
954       } else if (mode == FLAT) {
955         /* Kick! */
956         play_sound(sounds[SND_KICK], SOUND_CENTER_SPEAKER);
957
958         if (player->base.x < base.x + (base.width/2)) {
959           physic.set_velocity_x(5);
960           dir = RIGHT;
961         } else {
962           physic.set_velocity_x(-5);
963           dir = LEFT;
964         }
965
966         mode = KICK;
967         player->kick_timer.start(KICKING_TIME);
968         set_sprite(img_mriceblock_flat_left, img_mriceblock_flat_right);
969       }
970
971     make_player_jump(player);
972
973     player_status.score_multiplier++;
974
975     // check for maximum number of squiches
976     squishcount++;
977     if(squishcount >= MAX_ICEBLOCK_SQUICHES) {
978       kill_me(50);
979       return;
980     }
981     
982     return;
983   } else if(kind == BAD_FISH) {
984     // fish can only be killed when falling down
985     if(physic.get_velocity_y() >= 0)
986       return;
987       
988     make_player_jump(player);
989               
990     World::current()->add_score(Vector(base.x, base.y),
991                                 25 * player_status.score_multiplier);
992     player_status.score_multiplier++;
993      
994     // simply remove the fish...
995     remove_me();
996     return;
997   } else if(kind == BAD_BOUNCINGSNOWBALL) {
998     squish_me(player);
999     set_sprite(img_bouncingsnowball_squished,img_bouncingsnowball_squished);
1000     return;
1001   } else if(kind == BAD_FLYINGSNOWBALL) {
1002     squish_me(player);
1003     set_sprite(img_flyingsnowball_squished,img_flyingsnowball_squished);
1004     return;
1005   } else if(kind == BAD_SNOWBALL) {
1006     squish_me(player);
1007     set_sprite(img_snowball_squished_left, img_snowball_squished_right);
1008     return;
1009   }
1010 }
1011
1012 void
1013 BadGuy::kill_me(int score)
1014 {
1015   if(kind == BAD_BOMB)
1016     return;
1017
1018   dying = DYING_FALLING;
1019   if(kind == BAD_MRICEBLOCK) {
1020     set_sprite(img_mriceblock_falling_left, img_mriceblock_falling_right);
1021     if(mode == HELD) {
1022       mode = NORMAL;
1023       Player& tux = *World::current()->get_tux();  
1024       tux.holding_something = false;
1025     }
1026   }
1027
1028   physic.enable_gravity(true);
1029
1030   /* Gain some points: */
1031   if (score != 0)
1032     World::current()->add_score(Vector(base.x, base.y),
1033                                 score * player_status.score_multiplier);
1034
1035   /* Play death sound: */
1036   play_sound(sounds[SND_FALL], SOUND_CENTER_SPEAKER);
1037 }
1038
1039 void
1040 BadGuy::explode()
1041 {
1042   World::current()->add_bad_guy(base.x, base.y, BAD_BOMB);
1043   remove_me();
1044 }
1045
1046 void
1047 BadGuy::collision(const MovingObject&, int)
1048 {
1049   // later
1050 }
1051
1052 void
1053 BadGuy::collision(void *p_c_object, int c_object, CollisionType type)
1054 {
1055   BadGuy* pbad_c    = NULL;
1056   Bullet* pbullet_c = NULL;
1057
1058   if(type == COLLISION_BUMP) {
1059     bump();
1060     return;
1061   }
1062
1063   if(type == COLLISION_SQUISH) {
1064     Player* player = static_cast<Player*>(p_c_object);
1065     squish(player);
1066     return;
1067   }
1068
1069   /* COLLISION_NORMAL */
1070   switch (c_object)
1071     {
1072     case CO_BULLET:
1073       pbullet_c = (Bullet*) p_c_object;
1074
1075       if(pbullet_c->kind == FIRE_BULLET)
1076         {
1077         if (kind != BAD_BOMB && kind != BAD_STALACTITE && kind != BAD_FLAME)
1078           kill_me(10);
1079         }
1080       else if(pbullet_c->kind == ICE_BULLET)
1081         {
1082         //if(kind == BAD_FLAME)
1083         //  kill_me(10);
1084         //else
1085           frozen_timer.start(FROZEN_TIME);
1086         }
1087       break;
1088
1089     case CO_BADGUY:
1090       pbad_c = (BadGuy*) p_c_object;
1091
1092       /* If we're a kicked mriceblock, kill [almost] any badguys we hit */
1093       if(kind == BAD_MRICEBLOCK && mode == KICK &&
1094          kind != BAD_FLAME && kind != BAD_BOMB && kind != BAD_STALACTITE)
1095         {
1096           pbad_c->kill_me(25);
1097         }
1098
1099       // a held mriceblock kills the enemy too but falls to ground then
1100       else if(kind == BAD_MRICEBLOCK && mode == HELD)
1101         {
1102           pbad_c->kill_me(25);
1103           kill_me(0);
1104         }
1105
1106       /* Kill badguys that run into exploding bomb */
1107       else if (kind == BAD_BOMB && dying == DYING_NOT)
1108       {
1109         if (pbad_c->kind == BAD_MRBOMB)
1110         {
1111           // mrbomb transforms into a bomb now
1112           pbad_c->explode();
1113           return;
1114         }
1115         else if (pbad_c->kind != BAD_MRBOMB)
1116         {
1117           pbad_c->kill_me(50);
1118         }
1119       }
1120
1121       /* Kill any badguys that get hit by stalactite */
1122       else if (kind == BAD_STALACTITE && dying == DYING_NOT)
1123       {
1124         if (pbad_c->kind == BAD_MRBOMB)
1125         {
1126           // mrbomb transforms into a bomb now
1127           pbad_c->explode();
1128           return;
1129         }
1130         else
1131           pbad_c->kill_me(50);
1132       }
1133
1134       /* When enemies run into eachother, make them change directions */
1135       else
1136       {
1137         // Jumpy, fish, flame, stalactites are exceptions
1138         if (pbad_c->kind == BAD_JUMPY || pbad_c->kind == BAD_FLAME
1139             || pbad_c->kind == BAD_STALACTITE || pbad_c->kind == BAD_FISH)
1140           break;
1141
1142         // Bounce off of other badguy if we land on top of him
1143         if (base.y + base.height < pbad_c->base.y + pbad_c->base.height)
1144         {
1145           if (pbad_c->dir == LEFT)
1146           {
1147             dir = RIGHT;
1148             physic.set_velocity(fabsf(physic.get_velocity_x()), 2);
1149           }
1150           else if (pbad_c->dir == RIGHT)
1151           {
1152             dir = LEFT;
1153             physic.set_velocity(-fabsf(physic.get_velocity_x()), 2);
1154           }
1155
1156           break;
1157         }
1158         else if (base.y + base.height > pbad_c->base.y + pbad_c->base.height)
1159           break;
1160
1161         if (pbad_c->kind != BAD_FLAME)
1162           {
1163             if (dir == LEFT)
1164             {
1165               dir = RIGHT;
1166               physic.set_velocity_x(fabsf(physic.get_velocity_x()));
1167
1168               // in case badguys get "jammed"
1169               if (physic.get_velocity_x() != 0)
1170                 base.x = pbad_c->base.x + pbad_c->base.width;
1171             }
1172             else if (dir == RIGHT)
1173             {
1174               dir = LEFT;
1175               physic.set_velocity_x(-fabsf(physic.get_velocity_x()));
1176             }
1177
1178           }
1179       }
1180       
1181       break;
1182
1183     case CO_PLAYER:
1184       Player* player = static_cast<Player*>(p_c_object);
1185       /* Get kicked if were flat */
1186       if (mode == FLAT && !dying)
1187       {
1188         play_sound(sounds[SND_KICK], SOUND_CENTER_SPEAKER);
1189
1190         // Hit from left side
1191         if (player->base.x < base.x) {
1192           physic.set_velocity_x(5);
1193           dir = RIGHT;
1194         }
1195         // Hit from right side
1196         else {
1197           physic.set_velocity_x(-5);
1198           dir = LEFT;
1199         }
1200
1201         mode = KICK;
1202         player->kick_timer.start(KICKING_TIME);
1203         set_sprite(img_mriceblock_flat_left, img_mriceblock_flat_right);
1204       }
1205       break;
1206
1207     }
1208 }
1209
1210
1211 //---------------------------------------------------------------------------
1212
1213 void load_badguy_gfx()
1214 {
1215   img_mriceblock_flat_left = sprite_manager->load("mriceblock-flat-left");
1216   img_mriceblock_flat_right = sprite_manager->load("mriceblock-flat-right");
1217   img_mriceblock_falling_left = sprite_manager->load("mriceblock-falling-left");
1218   img_mriceblock_falling_right = sprite_manager->load("mriceblock-falling-right");
1219   img_mriceblock_left = sprite_manager->load("mriceblock-left");
1220   img_mriceblock_right = sprite_manager->load("mriceblock-right");
1221   img_jumpy_left_up = sprite_manager->load("jumpy-left-up");
1222   img_jumpy_left_down = sprite_manager->load("jumpy-left-down");
1223   img_jumpy_left_middle = sprite_manager->load("jumpy-left-middle");
1224   img_jumpy_left_iced = sprite_manager->load("jumpy-left-iced");
1225   img_mrbomb_left = sprite_manager->load("mrbomb-left");
1226   img_mrbomb_right = sprite_manager->load("mrbomb-right");
1227   img_mrbomb_iced_left = sprite_manager->load("mrbomb-iced-left");
1228   img_mrbomb_iced_right = sprite_manager->load("mrbomb-iced-right");
1229   img_mrbomb_ticking_left = sprite_manager->load("mrbomb-ticking-left");
1230   img_mrbomb_ticking_right = sprite_manager->load("mrbomb-ticking-right");
1231   img_mrbomb_explosion = sprite_manager->load("mrbomb-explosion");
1232   img_stalactite = sprite_manager->load("stalactite");
1233   img_stalactite_broken = sprite_manager->load("stalactite-broken");
1234   img_flame = sprite_manager->load("flame");
1235   img_fish = sprite_manager->load("fish");
1236   img_fish_down = sprite_manager->load("fish-down");
1237   img_fish_iced = sprite_manager->load("fish-iced");
1238   img_fish_iced_down = sprite_manager->load("fish-iced-down");
1239   img_bouncingsnowball_left = sprite_manager->load("bouncingsnowball-left");
1240   img_bouncingsnowball_right = sprite_manager->load("bouncingsnowball-right");
1241   img_bouncingsnowball_squished = sprite_manager->load("bouncingsnowball-squished");
1242   img_flyingsnowball = sprite_manager->load("flyingsnowball");
1243   img_flyingsnowball_squished = sprite_manager->load("flyingsnowball-squished");
1244   img_spiky_left = sprite_manager->load("spiky-left");
1245   img_spiky_right = sprite_manager->load("spiky-right");
1246   img_spiky_iced_left = sprite_manager->load("spiky-iced-left");
1247   img_spiky_iced_right = sprite_manager->load("spiky-iced-right");
1248   img_snowball_left = sprite_manager->load("snowball-left");
1249   img_snowball_right = sprite_manager->load("snowball-right");
1250   img_snowball_squished_left = sprite_manager->load("snowball-squished-left");
1251   img_snowball_squished_right = sprite_manager->load("snowball-squished-right");
1252 }
1253
1254 void free_badguy_gfx()
1255 {
1256 }
1257
1258 // EOF //