611ddcfd0ddd860676b263fd9b25c7a1556306cb
[supertux.git] / src / badguy.cpp
1 //
2 // C Implementation: badguy
3 //
4 // Description:
5 //
6 //
7 // Author: Tobias Glaesser <tobi.web@gmx.de> & Bill Kendrick, (C) 2004
8 //
9 // Copyright: See COPYING file that comes with this distribution
10 //
11 //
12
13 #include "globals.h"
14 #include "defines.h"
15 #include "badguy.h"
16 #include "scene.h"
17 #include "screen.h"
18
19 texture_type img_bsod_squished_left;
20 texture_type img_bsod_squished_right;
21 texture_type img_bsod_falling_left;
22 texture_type img_bsod_falling_right;
23 texture_type img_laptop_flat_left;
24 texture_type img_laptop_flat_right;
25 texture_type img_laptop_falling_left;
26 texture_type img_laptop_falling_right;
27 texture_type img_bsod_left[4];
28 texture_type img_bsod_right[4];
29 texture_type img_laptop_left[3];
30 texture_type img_laptop_right[3];
31 texture_type img_money_left[2];
32 texture_type img_money_right[2];
33 texture_type img_mrbomb_left[4];
34 texture_type img_mrbomb_right[4];
35 texture_type img_stalactite;
36 texture_type img_stalactite_broken;
37
38 BadGuyKind  badguykind_from_string(const std::string& str)
39 {
40   if (str == "money")
41     return BAD_MONEY;
42   else if (str == "laptop")
43     return BAD_LAPTOP;
44   else if (str == "bsod")
45     return BAD_BSOD;
46   else if (str == "mrbomb")
47     return BAD_MRBOMB;
48   else if (str == "stalactite")
49     return BAD_STALACTITE;
50   else
51     {
52       printf("Couldn't convert badguy: %s\n", str.c_str());
53       return BAD_BSOD;
54     }
55 }
56
57 std::string badguykind_to_string(BadGuyKind kind)
58 {
59   switch(kind)
60     {
61     case BAD_MONEY:
62       return "money";
63       break;
64     case BAD_LAPTOP:
65       return "laptop";
66       break;
67     case BAD_BSOD:
68       return "bsod";
69       break;
70     case BAD_MRBOMB:
71       return "mrbomb";
72       break;
73     case BAD_STALACTITE:
74       return "stalactite";
75       break;
76     default:
77       return "bsod";
78     }
79 }
80
81 void
82 BadGuy::init(float x, float y, BadGuyKind kind_)
83 {
84   base.width  = 32;
85   base.height = 32;
86   mode     = NORMAL;
87   dying    = DYING_NOT;
88   kind     = kind_;
89   base.x   = x;
90   base.y   = y;
91   base.xm  = -1.3;
92   base.ym  = 4.8;
93   old_base = base;
94   dir      = LEFT;
95   seen     = false;
96   timer_init(&timer, true);
97   physic_init(&physic);
98
99   if(kind == BAD_BOMB) {
100     timer_start(&timer, 1000);
101     mode = BOMB_TICKING;
102     // hack so that the bomb doesn't hurt until it expldes...
103     dying = DYING_SQUISHED;
104   }
105 }
106
107 void BadGuy::action_bsod()
108 {
109   /* --- BLUE SCREEN OF DEATH MONSTER: --- */
110
111   /* Move left/right: */
112   if (dying == DYING_NOT ||
113       dying == DYING_FALLING)
114     {
115       base.x += base.xm * frame_ratio;
116     }
117
118   /* Move vertically: */
119   base.y = base.y + base.ym * frame_ratio;
120
121   if (dying != DYING_FALLING)
122     collision_swept_object_map(&old_base,&base);
123                 
124   if (!dying)
125     check_horizontal_bump();
126
127   fall();
128
129   // Handle dying timer:
130   if (dying == DYING_SQUISHED)       
131     {
132       /* Remove it if time's up: */
133       if(!timer_check(&timer)) {
134         remove_me();
135         return;
136       }
137     }
138 }
139
140 void BadGuy::action_laptop()
141 {
142   /* Move left/right: */
143   if (mode == NORMAL || mode == KICK)
144     {
145       if (dying == DYING_NOT ||
146           dying == DYING_FALLING)
147         {
148           base.x += base.xm * frame_ratio;
149         }
150     }
151   else if (mode == HELD)
152     { /* FIXME: The pbad object shouldn't know about pplayer objects. */
153       /* If we're holding the laptop */
154       dir=tux.dir;
155       if(dir==RIGHT)
156         {
157           base.x = tux.base.x + 16;
158           base.y = tux.base.y + tux.base.height/1.5 - base.height;
159         }
160       else /* facing left */
161         {
162           base.x = tux.base.x - 16;
163           base.y = tux.base.y + tux.base.height/1.5 - base.height;
164         }
165       if(collision_object_map(&base))
166         {
167           base.x = tux.base.x;
168           base.y = tux.base.y + tux.base.height/1.5 - base.height;
169         }
170
171       if(tux.input.fire != DOWN) /* SHOOT! */
172         {
173           if(dir == LEFT)
174             base.x -= 24;
175           else
176             base.x += 24;
177
178           mode=KICK;
179           base.xm = 8;
180           base.ym = 8;
181           play_sound(sounds[SND_KICK],SOUND_CENTER_SPEAKER);
182         }
183     }
184
185
186   /* Move vertically: */
187   if(mode != HELD)
188     base.y = base.y + base.ym * frame_ratio;
189
190   if (dying != DYING_FALLING)
191     collision_swept_object_map(&old_base,&base);
192   /* Bump into things horizontally: */
193
194   if (!dying)
195     {
196       int changed = dir;
197       check_horizontal_bump();
198       if(mode == KICK && changed != dir)
199         {
200           /* handle stereo sound (number 10 should be tweaked...)*/
201           if (base.x < scroll_x - 10)
202             play_sound(sounds[SND_RICOCHET], SOUND_LEFT_SPEAKER);
203           else if (base.x > scroll_x + 10)
204             play_sound(sounds[SND_RICOCHET], SOUND_RIGHT_SPEAKER);
205           else
206             play_sound(sounds[SND_RICOCHET], SOUND_CENTER_SPEAKER);
207         }
208     }
209
210   fall();
211
212   /* Handle mode timer: */
213   if (mode == FLAT && mode != HELD)
214     {
215       if(!timer_check(&timer))
216         {
217           mode = NORMAL;
218           base.xm = 4;
219         }
220     }
221   else if (mode == KICK)
222     {
223       timer_check(&timer);
224     }
225 }
226
227 void BadGuy::check_horizontal_bump(bool checkcliff)
228 {
229     if (dir == LEFT && issolid( base.x, (int) base.y + 16))
230     {
231         dir = RIGHT;
232         base.xm = -base.xm;
233         return;
234     }
235     if (dir == RIGHT && issolid( base.x + base.width, (int) base.y + 16))
236     {
237         dir = LEFT;
238         base.xm = -base.xm;
239         return;
240     }
241
242     // don't check for cliffs when we're falling
243     if(!checkcliff)
244         return;
245     
246     if(dir == LEFT && !issolid(base.x, (int) base.y + base.height + 16))
247     {
248         printf("Cliffcol left\n");
249         dir = RIGHT;
250         base.xm = -base.xm;
251         return;
252     }
253     if(dir == RIGHT && !issolid(base.x + base.width,
254                 (int) base.y + base.height + 16))
255     {
256         printf("Cliffcol right\n");
257         dir = LEFT;
258         base.xm = -base.xm;
259         return;
260     }
261 }
262
263 void BadGuy::fall()
264 {
265   /* Fall if we get off the ground: */
266   if (dying != DYING_FALLING)
267     {
268       if (!issolid(base.x+16, base.y + 32))
269         {
270           if(!physic_is_set(&physic))
271             {
272               physic_set_state(&physic,PH_VT);
273               physic_set_start_vy(&physic,0.);
274             }
275
276           if(mode != HELD)
277             {
278               base.ym = physic_get_velocity(&physic);
279             }
280         }
281       else
282         {
283           /* Land: */
284
285           if (base.ym > 0)
286             {
287               base.y = (int)(base.y / 32) * 32;
288               base.ym = 0;
289             }
290           physic_init(&physic);
291         }
292     }
293   else
294     {
295       if(!physic_is_set(&physic))
296         {                                                
297           physic_set_state(&physic,PH_VT);
298           physic_set_start_vy(&physic,0.);
299         }
300       base.ym = physic_get_velocity(&physic);
301     }
302
303   // BadGuy fall below the ground
304   if (base.y > screen->h) {
305     remove_me();
306     return;
307   }
308 }
309
310 void BadGuy::remove_me()
311 {
312   std::vector<BadGuy>::iterator i;
313   for(i = bad_guys.begin(); i != bad_guys.end(); ++i) {
314     if( & (*i) == this) {
315       bad_guys.erase(i);
316       return;
317     }
318   }
319 }
320
321 void BadGuy::action_money()
322 {
323   /* Move vertically: */
324   base.y = base.y + base.ym * frame_ratio;
325
326   if (dying != DYING_FALLING)
327     collision_swept_object_map(&old_base,&base);
328
329   if (base.y > screen->h) {
330     remove_me();
331     return;
332   }
333
334   if(physic_get_state(&physic) == -1)
335     {
336       physic_set_state(&physic,PH_VT);
337       physic_set_start_vy(&physic,0.);
338     }
339
340   if (dying != DYING_FALLING)
341     {
342       
343       if(issolid(base.x, base.y + 32))
344         {
345           physic_set_state(&physic,PH_VT);
346           physic_set_start_vy(&physic,6.);
347           base.ym = physic_get_velocity(&physic);
348         }
349       /* // matze: is this code needed?
350       else if(issolid(base.x, base.y))
351         { // This works, but isn't the best solution imagineable 
352           physic_set_state(&physic,PH_VT);
353           physic_set_start_vy(&physic,0.);
354           base.ym = physic_get_velocity(&physic);
355           ++base.y;
356         }*/
357       else
358         {
359           base.ym = physic_get_velocity(&physic);
360         }
361     }
362   else
363     {
364       if(!physic_is_set(&physic))
365         {
366           physic_set_state(&physic,PH_VT);
367           physic_set_start_vy(&physic,0.);
368         }
369       base.ym = physic_get_velocity(&physic);
370     } 
371 }
372
373 void BadGuy::action_mrbomb()
374 {
375   if(mode == NORMAL) {
376     base.x += base.xm * frame_ratio;
377   }
378
379   /* Move vertically: */
380   base.y += base.ym * frame_ratio;
381
382   if (dying != DYING_FALLING)
383     collision_swept_object_map(&old_base,&base);
384
385   check_horizontal_bump(true);
386   fall();
387 }
388
389 void BadGuy::action_bomb()
390 {
391   // eventually fall down
392   base.y += base.ym * frame_ratio;
393   collision_swept_object_map(&old_base,&base);
394   fall();
395
396   if(!timer_check(&timer)) {
397     if(mode == BOMB_TICKING) {
398       mode = BOMB_EXPLODE;
399       dying = DYING_NOT; // now the bomb hurts
400       timer_start(&timer, 1000);
401     } else if(mode == BOMB_EXPLODE) {
402       remove_me();
403       return;
404     }
405   }
406 }
407
408 void BadGuy::action_stalactite()
409 {
410   if(mode == NORMAL) {
411     if(tux.base.x + 32 > base.x - 40 && tux.base.x < base.x + 32 + 40) {
412       timer_start(&timer, 800);
413       mode = STALACTITE_SHAKING;
414     }
415   } if(mode == STALACTITE_SHAKING) {
416     base.x = old_base.x + (rand() % 6) - 3; // TODO this could be done nicer...
417     if(!timer_check(&timer)) {
418       mode = STALACTITE_FALL;
419     }
420   } else if(mode == STALACTITE_FALL) {
421     base.y += base.ym * frame_ratio;   
422     /* Destroy if collides land */
423     if(issolid(base.x+16, base.y+32))
424     {
425       timer_start(&timer, 3000);
426       dying = DYING_SQUISHED;
427       mode = FLAT;
428     }
429   }
430 }
431
432 void
433 BadGuy::action()
434
435   if (seen)
436     {
437       switch (kind)
438         {
439         case BAD_BSOD:
440           action_bsod();
441           break;
442     
443         case BAD_LAPTOP:
444           action_laptop();
445           break;
446       
447         case BAD_MONEY:
448           action_money();
449           break;
450
451         case BAD_MRBOMB:
452           action_mrbomb();
453           break;
454         
455         case BAD_BOMB:
456           action_bomb();
457           break;
458
459         case BAD_STALACTITE:
460           action_stalactite();
461           break;
462
463         }
464     }
465
466   // Remove if it's far off the screen:
467   if (base.x < scroll_x - OFFSCREEN_DISTANCE)
468     {
469       remove_me();
470       return;
471     }
472   else /* !seen */
473     {
474       // Once it's on screen, it's activated!
475       if (base.x <= scroll_x + screen->w + OFFSCREEN_DISTANCE)
476         seen = true;
477     }
478 }
479
480 void
481 BadGuy::draw_bsod()
482 {
483   texture_type* texture = 0;
484   float y = base.y;
485   if(dying == DYING_NOT) {
486     size_t frame = (global_frame_counter / 5) % 4;
487     texture = (dir == LEFT) ? &img_bsod_left[frame] : &img_bsod_right[frame];
488   } else if(dying == DYING_FALLING) {
489     texture = (dir == LEFT) ? &img_bsod_falling_left : &img_bsod_falling_right;
490   } else if(dying == DYING_SQUISHED) {
491     texture = (dir == LEFT) 
492         ? &img_bsod_squished_left : &img_bsod_squished_right;
493     y += 24;
494   }
495   
496   texture_draw(texture, base.x - scroll_x, y);
497 }
498
499 void
500 BadGuy::draw_laptop()
501 {
502   texture_type* texture;
503   size_t frame = (global_frame_counter / 5) % 3;
504   
505   if(dying == DYING_NOT) {
506     if(mode == NORMAL) {
507       if(dir == LEFT)
508         texture = &img_laptop_left[frame];
509       else
510         texture = &img_laptop_right[frame];
511     } else {
512       texture = (dir == LEFT) ? &img_laptop_flat_left : &img_laptop_flat_right;
513     }
514   } else {
515     texture = (dir == LEFT) 
516         ? &img_laptop_falling_left : &img_laptop_falling_right;
517   }
518
519   texture_draw(texture, base.x - scroll_x, base.y);
520 }
521
522 void
523 BadGuy::draw_money()
524 {
525   texture_type* texture;
526   size_t frame = (base.ym != 300) ? 0 : 1;
527
528   if(tux.base.x + tux.base.width < base.x) {
529     texture = &img_money_left[frame];
530   } else {
531     texture = &img_money_right[frame];
532   }
533
534   texture_draw(texture, base.x - scroll_x, base.y);
535 }
536   
537 void
538 BadGuy::draw_mrbomb()
539 {
540   texture_type* texture;
541   size_t frame = (global_frame_counter/5) % 4;
542
543   if(dir == LEFT)
544     texture = &img_mrbomb_left[frame];
545   else
546     texture = &img_mrbomb_right[frame];
547
548   texture_draw(texture, base.x - scroll_x, base.y);
549 }
550
551 void
552 BadGuy::draw_bomb()
553 {
554   texture_type* texture;
555   
556   // TODO add real bomb graphics
557   if(mode == BOMB_TICKING) {
558     texture = &img_bsod_squished_right;
559   } else {
560     texture = &img_bsod_squished_left;
561   }
562   
563   texture_draw(texture, base.x - scroll_x, base.y);
564 }
565
566 void
567 BadGuy::draw_stalactite()
568 {
569   texture_type* texture;
570   if(mode != FLAT)
571     texture = &img_stalactite;
572   else
573     texture = &img_stalactite_broken;
574
575   texture_draw(texture, base.x - scroll_x, base.y);
576 }
577
578 void
579 BadGuy::draw()
580 {
581   // Don't try to draw stuff that is outside of the screen
582   if (base.x > scroll_x - 32 &&
583       base.x < scroll_x + screen->w)
584     {
585       switch (kind)
586         {
587         case BAD_BSOD:
588           draw_bsod();
589           break;
590     
591         case BAD_LAPTOP:
592           draw_laptop();
593           break;
594     
595         case BAD_MONEY:
596           draw_money();
597           break;
598
599         case BAD_MRBOMB:
600           draw_mrbomb();
601           break;
602
603         case BAD_BOMB:
604           draw_bomb();
605           break;
606
607         case BAD_STALACTITE:
608           draw_stalactite();
609           break;
610         }
611     }
612 }
613
614 void
615 BadGuy::bump()
616 {
617   if(kind == BAD_BSOD || kind == BAD_LAPTOP || kind == BAD_BOMB) {
618     dying = DYING_FALLING;
619     base.ym = -8;
620     play_sound(sounds[SND_FALL], SOUND_CENTER_SPEAKER);
621   }
622 }
623
624 void
625 BadGuy::make_player_jump(Player* player)
626 {
627     physic_set_state(&player->vphysic,PH_VT);
628     physic_set_start_vy(&player->vphysic,2.);
629     player->base.y = base.y - player->base.height - 1;
630 }
631
632 void
633 BadGuy::squich(Player* player)
634 {
635   if(kind == BAD_MRBOMB) {
636       // mrbomb transforms into a bomb now
637       add_bad_guy(base.x, base.y, BAD_BOMB);
638       
639       make_player_jump(player);
640       add_score(base.x - scroll_x, base.y, 50 * score_multiplier);
641       play_sound(sounds[SND_SQUISH], SOUND_CENTER_SPEAKER);
642       score_multiplier++;
643       
644       remove_me();
645       return;
646
647   } else if(kind == BAD_BSOD) {
648       dying = DYING_SQUISHED;
649       timer_start(&timer,4000);
650
651       make_player_jump(player);
652
653       add_score(base.x - scroll_x, base.y, 50 * score_multiplier);
654       play_sound(sounds[SND_SQUISH], SOUND_CENTER_SPEAKER);
655       score_multiplier++;
656       return;
657       
658   } else if (kind == BAD_LAPTOP) {
659       if (mode == NORMAL || mode == KICK)
660       {
661           /* Flatten! */
662           play_sound(sounds[SND_STOMP], SOUND_CENTER_SPEAKER);
663           mode = FLAT;
664           base.xm = 4;
665
666           timer_start(&timer, 4000);
667       } else if (mode == FLAT) {
668           /* Kick! */
669           play_sound(sounds[SND_KICK], SOUND_CENTER_SPEAKER);
670
671           if (player->base.x < base.x + (base.width/2))
672               dir = RIGHT;
673           else
674               dir = LEFT;
675
676           base.xm = 5;
677           mode = KICK;
678
679           timer_start(&timer,5000);
680       }
681
682       make_player_jump(player);
683               
684       add_score(base.x - scroll_x,
685               base.y,
686               25 * score_multiplier);
687       score_multiplier++;
688       return;
689   }
690 }
691
692 void
693 BadGuy::collision(void *p_c_object, int c_object, CollisionType type)
694 {
695   BadGuy* pbad_c    = NULL;
696
697   if(type == COLLISION_BUMP) {
698     bump();
699     return;
700   }
701   if(type == COLLISION_SQUICH) {
702     Player* player = static_cast<Player*>(p_c_object);
703     squich(player);
704     return;
705   }
706
707   switch (c_object)
708     {
709     case CO_BULLET:
710       if(kind == BAD_BOMB || kind == BAD_STALACTITE)
711         return;
712
713       dying = DYING_FALLING;
714       base.ym = -8;
715
716       /* Gain some points: */
717       if (kind == BAD_BSOD)
718         add_score(base.x - scroll_x, base.y,
719                   50 * score_multiplier);
720       else if (kind == BAD_LAPTOP)
721         add_score(base.x - scroll_x, base.y,
722                   25 * score_multiplier);
723       else if (kind == BAD_MONEY)
724         add_score(base.x - scroll_x, base.y,
725                   50 * score_multiplier);
726
727       /* Play death sound: */
728       play_sound(sounds[SND_FALL], SOUND_CENTER_SPEAKER);
729       break;
730
731     case CO_BADGUY:
732       pbad_c = (BadGuy*) p_c_object;
733       if (mode == NORMAL)
734       {
735       /* do nothing */
736       }
737       else if(mode == KICK)
738         {
739           /* We're in kick mode, kill the other guy
740              and yourself(wuahaha) : */
741
742           pbad_c->dying = DYING_FALLING;
743           pbad_c->base.ym = -8;
744           play_sound(sounds[SND_FALL], SOUND_CENTER_SPEAKER);
745
746           add_score(base.x - scroll_x,
747                     base.y, 100);
748                   pbad_c->dying = DYING_FALLING;
749                   
750           dying = DYING_FALLING;
751           base.ym = -8;
752
753           add_score(pbad_c->base.x - scroll_x,
754                     pbad_c->base.y, 100);
755         }
756       break;
757     }
758 }
759
760 // EOF //