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