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