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