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