further tweaked flapping, finally found a speed setting that seems okay
[supertux.git] / src / player.cpp
1 //  $Id$
2 //
3 //  SuperTux -  A Jump'n Run
4 //  Copyright (C) 2003 Tobias Glaesser <tobi.web@gmx.de>
5 //
6 //  This program is free software; you can redistribute it and/or
7 //  modify it under the terms of the GNU General Public License
8 //  as published by the Free Software Foundation; either version 2
9 //  of the License, or (at your option) any later version.
10 //
11 //  This program is distributed in the hope that it will be useful,
12 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //  GNU General Public License for more details.
15 //
16 //  You should have received a copy of the GNU General Public License
17 //  along with this program; if not, write to the Free Software
18 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19
20 #include <cmath>
21 #include <iostream>
22 #include <cassert>
23
24 #include "app/globals.h"
25 #include "app/gettext.h"
26 #include "player.h"
27 #include "defines.h"
28 #include "scene.h"
29 #include "tile.h"
30 #include "special/sprite.h"
31 #include "sector.h"
32 #include "tilemap.h"
33 #include "camera.h"
34 #include "gameobjs.h"
35 #include "resources.h"
36 #include "interactive_object.h"
37 #include "video/screen.h"
38 #include "statistics.h"
39 #include "gameloop.h"
40
41 // behavior definitions:
42 #define TILES_FOR_BUTTJUMP 3
43 // animation times (in ms):
44 #define SHOOTING_TIME 320
45 // others stuff:
46 #define AUTOSCROLL_DEAD_INTERVAL 300
47
48 // time before idle animation starts
49 #define IDLE_TIME 2500
50
51 // growing animation
52 Surface* growingtux_left[GROWING_FRAMES];
53 Surface* growingtux_right[GROWING_FRAMES];
54
55 Surface* tux_life;
56
57 Sprite* smalltux_gameover;
58 Sprite* smalltux_star;
59 Sprite* bigtux_star;
60
61 TuxBodyParts* small_tux;
62 TuxBodyParts* big_tux;
63 TuxBodyParts* fire_tux;
64 TuxBodyParts* ice_tux;
65
66 PlayerKeymap keymap;
67
68 PlayerKeymap::PlayerKeymap()
69 {
70   keymap.up    = SDLK_UP;
71   keymap.down  = SDLK_DOWN;
72   keymap.left  = SDLK_LEFT;
73   keymap.right = SDLK_RIGHT;
74
75   keymap.power = SDLK_LCTRL;
76   keymap.jump  = SDLK_LALT;
77 }
78
79 void player_input_init(player_input_type* pplayer_input)
80 {
81   pplayer_input->up = UP;
82   pplayer_input->down = UP;
83   pplayer_input->fire = UP;
84   pplayer_input->left = UP;
85   pplayer_input->old_fire = UP;
86   pplayer_input->right = UP;
87   pplayer_input->jump = UP;
88   pplayer_input->old_jump = UP;
89   pplayer_input->activate = UP;
90 }
91
92 void
93 TuxBodyParts::set_action(std::string action)
94 {
95   if(head != NULL)
96     head->set_action(action);
97   if(body != NULL)
98     body->set_action(action);
99   if(arms != NULL)
100     arms->set_action(action);
101   if(feet != NULL)
102     feet->set_action(action);
103 }
104
105 void
106 TuxBodyParts::one_time_animation()
107 {
108   if(head != NULL)
109     head->start_animation(1);
110   if(body != NULL)
111     body->start_animation(1);
112   if(arms != NULL)
113     arms->start_animation(1);
114   if(feet != NULL)
115     feet->start_animation(1);
116 }
117
118 void
119 TuxBodyParts::draw(DrawingContext& context, const Vector& pos, int layer,
120                   Uint32 drawing_effect)
121 {
122   if(head != NULL)
123     head->draw(context, pos, layer-1, drawing_effect);
124   if(body != NULL)
125     body->draw(context, pos, layer-3, drawing_effect);
126   if(arms != NULL)
127     arms->draw(context, pos, layer,   drawing_effect);
128   if(feet != NULL)
129     feet->draw(context, pos, layer-2, drawing_effect);
130 }
131
132 Player::Player()
133 {
134   init();
135 }
136
137 Player::~Player()
138 {
139 }
140
141 void
142 Player::init()
143 {
144   holding_something = false;
145
146   base.width = 32;
147   base.height = 32;
148
149   size = SMALL;
150   got_power = NONE_POWER;
151
152   base.x = 0;
153   base.y = 0;
154   previous_base = old_base = base;
155   dir = RIGHT;
156   old_dir = dir;
157   duck = false;
158   dead = false;
159
160   dying   = DYING_NOT;
161   last_ground_y = 0;
162   fall_mode = ON_GROUND;
163   jumping = false;
164   flapping = false;
165   can_jump = true;
166   can_flap = false;
167   falling_from_flap = false;
168   enable_hover = false;
169   butt_jump = false;
170   
171   flapping_velocity = 0;
172   
173   frame_main = 0;
174   frame_ = 0;
175
176   player_input_init(&input);
177
178   invincible_timer.init(true);
179   skidding_timer.init(true);
180   safe_timer.init(true);
181   frame_timer.init(true);
182   kick_timer.init(true);
183   shooting_timer.init(true);
184   growing_timer.init(true);
185   idle_timer.init(true);
186   flapping_timer.init(true);
187
188   physic.reset();
189 }
190
191 int
192 Player::key_event(SDLKey key, int state)
193 {
194   idle_timer.start(IDLE_TIME);
195
196   if(key == keymap.right)
197     {
198       input.right = state;
199       return true;
200     }
201   else if(key == keymap.left)
202     {
203       input.left = state;
204       return true;
205     }
206   else if(key == keymap.up)
207     {
208       input.up = state;
209
210       /* Up key also opens activates stuff */
211       input.activate = state;
212
213       if(state == DOWN) {
214         /** check for interactive objects */
215         for(Sector::InteractiveObjects::iterator i 
216             = Sector::current()->interactive_objects.begin();
217             i != Sector::current()->interactive_objects.end(); ++i) {
218           if(rectcollision(base, (*i)->get_area())) {
219             (*i)->interaction(INTERACTION_ACTIVATE);
220           }
221         }
222       }
223
224       return true;
225     }
226   else if(key == keymap.down)
227     {
228       input.down = state;
229       return true;
230     }
231   else if(key == keymap.power)
232     {
233       if (state == UP)
234         input.old_fire = UP;
235       input.fire = state;
236
237       return true;
238     }
239   else if(key == keymap.jump)
240     {
241       if (state == UP)
242         input.old_jump = UP;
243       input.jump = state;
244       return true;
245     }
246   else
247     return false;
248 }
249
250 void
251 Player::level_begin()
252 {
253   base.x  = 100;
254   base.y  = 170;
255   previous_base = old_base = base;
256   duck = false;
257
258   dying = DYING_NOT;
259
260   player_input_init(&input);
261
262   invincible_timer.init(true);
263   skidding_timer.init(true);
264   safe_timer.init(true);
265   frame_timer.init(true);
266   growing_timer.init(true);
267   idle_timer.init(true);
268
269   physic.reset();
270 }
271
272 void
273 Player::action(float elapsed_time)
274 {
275   bool jumped_in_solid = false;
276
277   if(dying && !dying_timer.check()) {
278     dead = true;
279     return;
280   }
281
282   if (input.fire == UP)
283     holding_something = false;
284
285   /* Move tux: */
286   previous_base = base;
287
288   /* --- HANDLE TUX! --- */
289   if(dying == DYING_NOT)
290     handle_input();
291
292   physic.apply(elapsed_time, base.x, base.y, Sector::current()->gravity);
293
294   if(dying == DYING_NOT) 
295     {
296       base_type target = base;
297
298       collision_swept_object_map(&old_base, &base);
299
300       if ((!invincible_timer.started() && !safe_timer.started())
301           && (isspike(base.x, base.y) || isspike(base.x + base.width, base.y)
302           ||  isspike(base.x, base.y + base.height)
303           ||  isspike(base.x + base.width, base.y + base.height)))
304       {
305          kill(SHRINK);
306       }
307
308       // Don't accelerate Tux if he is running against a wall
309       if (target.x != base.x)
310         {
311           physic.set_velocity_x(0);
312         }
313
314       // special exception for cases where we're stuck under tiles after
315       // being ducked. In this case we drift out
316       if(!duck && on_ground() && old_base.x == base.x && old_base.y == base.y
317          && collision_object_map(base))
318         {
319           base.x += elapsed_time * WALK_SPEED * (dir ? 1: -1);
320           previous_base = old_base = base;
321         }
322
323       // Land:
324       if (!on_ground())
325         {
326           physic.enable_gravity(true);
327           if(under_solid())
328             {
329               // fall down
330               physic.set_velocity_y(0);
331               jumped_in_solid = true;
332               jumping = false;
333               flapping = false;
334             }
335         }
336       else
337         {
338           /* Land: */
339           if (physic.get_velocity_y() < 0)
340             {
341               base.y = (int)(((int)base.y / 32) * 32);
342               physic.set_velocity_y(0);
343             }
344
345           physic.enable_gravity(false);
346           /* Reset score multiplier (for multi-hits): */
347           if (!invincible_timer.started())
348             {
349             /*if(player_status.score_multiplier > 2)
350               {  // show a message
351               char str[124];
352               sprintf(str, _("%d bad guys in a row!"), player_status.score_multiplier-1);
353               Sector::current()->add_floating_text(base, str);
354               }*/
355             player_status.score_multiplier = 1;
356             }
357         }
358
359       if(jumped_in_solid)
360         {
361           if (isbrick(base.x, base.y) ||
362               isfullbox(base.x, base.y))
363             {
364               Sector::current()->trygrabdistro(
365                   Vector(base.x, base.y - 32), BOUNCE);
366               Sector::current()->trybumpbadguy(Vector(base.x, base.y - 64));
367
368               Sector::current()->trybreakbrick(
369                   Vector(base.x, base.y), size == SMALL);
370
371               bumpbrick(base.x, base.y);
372               Sector::current()->tryemptybox(Vector(base.x, base.y), RIGHT);
373             }
374
375           if (isbrick(base.x+ 31, base.y) ||
376               isfullbox(base.x+ 31, base.y))
377             {
378               Sector::current()->trygrabdistro(
379                   Vector(base.x+ 31, base.y - 32), BOUNCE);
380               Sector::current()->trybumpbadguy(Vector(base.x+ 31, base.y - 64));
381
382               if(size == BIG)
383                 Sector::current()->trybreakbrick(
384                     Vector(base.x+ 31, base.y), size == SMALL);
385
386               bumpbrick(base.x+ 31, base.y);
387               Sector::current()->tryemptybox(Vector(base.x+ 31, base.y), LEFT);
388             }
389         }
390
391       grabdistros();
392
393       if (jumped_in_solid)
394         {
395           ++base.y;
396           ++old_base.y;
397           if(on_ground())
398             {
399               /* Make sure jumping is off. */
400               jumping = false;
401               flapping = false;
402             }
403         }
404     }
405
406   /* ---- DONE HANDLING TUX! --- */
407
408   // check some timers
409   skidding_timer.check();
410   invincible_timer.check();
411   safe_timer.check();
412   kick_timer.check();
413 }
414
415 bool
416 Player::on_ground()
417 {
418   return ( issolid(base.x + base.width / 2, base.y + base.height) ||
419            issolid(base.x + 1, base.y + base.height) ||
420            issolid(base.x + base.width - 1, base.y + base.height));
421 }
422
423 bool
424 Player::under_solid()
425 {
426   return ( issolid(base.x + base.width / 2, base.y) ||
427            issolid(base.x + 1, base.y) ||
428            issolid(base.x + base.width - 1, base.y)  );
429 }
430
431 bool
432 Player::tiles_on_air(int tiles)
433 {
434   for(int t = 0; t != tiles; t++)
435      {
436      if(issolid(base.x + base.width / 2, base.y + base.height + (tiles*32)) ||
437          issolid(base.x + 1, base.y + base.height + (tiles*32)) ||
438          issolid(base.x + base.width - 1, base.y + base.height + (tiles*32)))
439        return false;
440      }
441   return true;
442 }
443
444 void
445 Player::handle_horizontal_input()
446 {
447   float vx = physic.get_velocity_x();
448   float vy = physic.get_velocity_y();
449   float ax = physic.get_acceleration_x();
450   float ay = physic.get_acceleration_y();
451
452   float dirsign = 0;
453   if(input.left == DOWN && input.right == UP && (!duck || physic.get_velocity_y() != 0)) {
454       old_dir = dir;
455       dir = LEFT;
456       dirsign = -1;
457   } else if(input.left == UP && input.right == DOWN && (!duck || physic.get_velocity_y() != 0)) {
458       old_dir = dir;
459       dir = RIGHT;
460       dirsign = 1;
461   }
462
463   if (input.fire == UP) {
464       ax = dirsign * WALK_ACCELERATION_X;
465       // limit speed
466       if(vx >= MAX_WALK_XM && dirsign > 0) {
467         vx = MAX_WALK_XM;
468         ax = 0;
469       } else if(vx <= -MAX_WALK_XM && dirsign < 0) {
470         vx = -MAX_WALK_XM;
471         ax = 0;
472       }
473   } else {
474       ax = dirsign * RUN_ACCELERATION_X;
475       // limit speed
476       if(vx >= MAX_RUN_XM && dirsign > 0) {
477         vx = MAX_RUN_XM;
478         ax = 0;
479       } else if(vx <= -MAX_RUN_XM && dirsign < 0) {
480         vx = -MAX_RUN_XM;
481         ax = 0;
482       }
483   }
484
485   // we can reach WALK_SPEED without any acceleration
486   if(dirsign != 0 && fabs(vx) < WALK_SPEED) {
487     vx = dirsign * WALK_SPEED;
488   }
489
490   // changing directions?
491   if(on_ground() && ((vx < 0 && dirsign >0) || (vx>0 && dirsign<0))) {
492       if(fabs(vx)>SKID_XM && !skidding_timer.check()) {
493           skidding_timer.start(SKID_TIME);
494           SoundManager::get()->play_sound(IDToSound(SND_SKID));
495           ax *= 2.5;
496       } else {
497           ax *= 2;
498       }
499   }
500
501   // we get slower when not pressing any keys
502   if(dirsign == 0) {
503       if(fabs(vx) < WALK_SPEED) {
504           vx = 0;
505           ax = 0;
506       } else if(vx < 0) {
507           ax = WALK_ACCELERATION_X * 1.5;
508       } else {
509           ax = WALK_ACCELERATION_X * -1.5;
510       }
511   }
512
513   // if we're on ice slow down acceleration or deceleration
514   if (isice(base.x, base.y + base.height))
515   {
516     /* the acceleration/deceleration rate on ice is inversely proportional to
517      * the current velocity.
518      */
519
520     // increasing 1 will increase acceleration/deceleration rate
521     // decreasing 1 will decrease acceleration/deceleration rate
522     //  must stay above zero, though
523     if (ax != 0) ax *= 1 / fabs(vx);
524   }
525
526   physic.set_velocity(vx, vy);
527   physic.set_acceleration(ax, ay);
528 }
529
530 void
531 Player::handle_vertical_input()
532 {
533   
534   // set fall mode...
535   if(on_ground()) {
536     fall_mode = ON_GROUND;
537     last_ground_y = base.y;
538   } else {
539     if(base.y > last_ground_y)
540       fall_mode = FALLING;
541     else if(fall_mode == ON_GROUND)
542       fall_mode = JUMPING;
543   }
544
545   // Press jump key
546   if(input.jump == DOWN && can_jump && on_ground())
547     {
548       if(duck) { // only jump a little bit when in duck mode {
549         physic.set_velocity_y(3);
550       } else {
551         // jump higher if we are running
552         if (fabs(physic.get_velocity_x()) > MAX_WALK_XM)
553           physic.set_velocity_y(5.8);
554         else
555           physic.set_velocity_y(5.2);
556       }
557
558       --base.y;
559       jumping = true;
560       flapping = false;
561       can_jump = false;
562       can_flap = false;
563       if (size == SMALL)
564         SoundManager::get()->play_sound(IDToSound(SND_JUMP));
565       else
566         SoundManager::get()->play_sound(IDToSound(SND_BIGJUMP));
567     }
568   // Let go of jump key
569   else if(input.jump == UP)
570     {
571       if (!flapping && !duck && !falling_from_flap && !on_ground())
572          {
573             can_flap = true;
574          }
575       if (jumping && physic.get_velocity_y() > 0)
576          {
577             jumping = false;
578             physic.set_velocity_y(0);
579          }
580     }
581
582    
583    // Flapping, Marek's version
584    if (input.jump == DOWN && can_flap)
585      {
586          if (!flapping_timer.started())
587             {
588                flapping_timer.start(TUX_FLAPPING_TIME);
589                flapping_velocity = physic.get_velocity_x();
590             }
591          if (!flapping_timer.check()) 
592             {
593                can_flap = false;
594                falling_from_flap = true;
595             }
596          jumping = true;
597          flapping = true;
598          if (flapping_timer.get_gone() <= TUX_FLAPPING_TIME)
599             {
600                float cv;
601                if (flapping_velocity == 0) {cv = 0;}
602                else {cv = flapping_velocity*(sqrt(TUX_FLAPPING_TIME-(float)flapping_timer.get_gone()))/sqrt(TUX_FLAPPING_TIME);}
603                //Handle change of direction while flapping
604                if (((dir == LEFT) && (cv > 0)) || (dir == RIGHT) && (cv < 0)) {cv *= (-1);}
605                physic.set_velocity_x(cv);
606                physic.set_velocity_y((float)flapping_timer.get_gone()/850);
607             }
608      }
609      
610    /* // Flapping, Ryan's version
611    if (input.jump == DOWN && can_flap)
612      {
613          if (!flapping_timer.started())
614             {
615                flapping_timer.start(TUX_FLAPPING_TIME);
616             }
617          if (!flapping_timer.check()) 
618             {
619                can_flap = false;
620                falling_from_flap = true;
621             }
622          jumping = true;
623          flapping = true;
624          if (flapping && flapping_timer.get_gone() <= TUX_FLAPPING_TIME
625                 && physic.get_velocity_y() < 0)
626             {
627                float gravity = Sector::current()->gravity;
628                float xr = (fabsf(physic.get_velocity_x()) / MAX_RUN_XM);
629
630                // XXX: magic numbers. should be a percent of gravity
631                //      gravity is (by default) -0.1f
632                physic.set_acceleration_y(.12 + .01f*xr);
633
634 #if 0
635                // To slow down x-vel when flapping (not working)
636                if (fabsf(physic.get_velocity_x()) > MAX_WALK_XM)
637                {
638                    if (physic.get_velocity_x() < 0)
639                        physic.set_acceleration_x(1.0f);
640                    else if (physic.get_velocity_x() > 0)
641                        physic.set_acceleration_x(-1.0f);
642                }
643 #endif
644             }
645      }
646     else
647      {
648         physic.set_acceleration_y(0);
649      }
650    */
651
652    // Hover
653    //(disabled by default, use cheat code "hover" to toggle on/off)
654    //TODO: needs some tweaking, especially when used together with double jump and jumping off badguys
655    if (enable_hover && input.jump == DOWN && !jumping && !butt_jump && physic.get_velocity_y() <= 0)
656       {
657          physic.set_velocity_y(-1);
658       }
659
660    /* In case the player has pressed Down while in a certain range of air,
661       enable butt jump action */
662   if (input.down == DOWN && !butt_jump && !duck)
663     if(tiles_on_air(TILES_FOR_BUTTJUMP) && jumping)
664       butt_jump = true;
665
666    /* When Down is not held anymore, disable butt jump */
667   if(butt_jump && input.down == UP)
668     butt_jump = false;
669
670   // Do butt jump
671   if (butt_jump && on_ground() && size == BIG)
672   {
673     // Add a smoke cloud
674     if (duck) 
675       Sector::current()->add_smoke_cloud(Vector(base.x - 32, base.y));
676     else 
677       Sector::current()->add_smoke_cloud(Vector(base.x - 32, base.y + 32));
678     
679     butt_jump = false;
680
681     // Break bricks beneath Tux
682     if(Sector::current()->trybreakbrick(
683           Vector(base.x + 1, base.y + base.height), false)
684         || Sector::current()->trybreakbrick(
685            Vector(base.x + base.width - 1, base.y + base.height), false))
686     {
687       physic.set_velocity_y(2);
688       butt_jump = true;
689     }
690
691     // Kill nearby badguys
692     std::vector<GameObject*> gameobjects = Sector::current()->gameobjects;
693     for (std::vector<GameObject*>::iterator i = gameobjects.begin();
694          i != gameobjects.end();
695          i++)
696     {
697       BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
698       if(badguy)
699       {
700         // don't kill when badguys are already dying or in a certain mode
701         if(badguy->dying == DYING_NOT && badguy->mode != BadGuy::BOMB_TICKING &&
702            badguy->mode != BadGuy::BOMB_EXPLODE)
703           {
704             if (fabsf(base.x - badguy->base.x) < 150 &&
705               fabsf(base.y - badguy->base.y) < 60 &&
706               (issolid(badguy->base.x + 1, badguy->base.y + badguy->base.height) ||
707                issolid(badguy->base.x + badguy->base.width - 1, badguy->base.y + badguy->base.height)))
708               badguy->kill_me(25);
709           }
710       }
711     }
712   }
713
714   if ( (issolid(base.x + base.width / 2, base.y + base.height + 64) ||
715         issolid(base.x + 1, base.y + base.height + 64) ||
716         issolid(base.x + base.width - 1, base.y + base.height + 64))
717        && jumping  == false
718        && can_jump == false
719        && input.jump == DOWN
720        && input.old_jump == UP)
721     {
722       can_jump = true;
723     }
724
725   if(on_ground())   /* Make sure jumping is off. */
726     {
727       jumping = false;
728       flapping = false;
729       falling_from_flap = false;
730       if (flapping_timer.started()) {flapping_timer.stop();}
731
732       physic.set_acceleration_y(0); //for flapping
733     }
734
735   input.old_jump = input.jump;
736 }
737
738 void
739 Player::handle_input()
740 {
741   /* Handle horizontal movement: */
742     handle_horizontal_input();
743
744   /* Jump/jumping? */
745
746   if (on_ground() && input.jump == UP)
747     can_jump = true;
748   handle_vertical_input();
749
750   /* Shoot! */
751   if (input.fire == DOWN && input.old_fire == UP && got_power != NONE_POWER)
752     {
753       if(Sector::current()->add_bullet(Vector(base.x, base.y + (base.height/2)),
754           physic.get_velocity_x(), dir))
755         shooting_timer.start(SHOOTING_TIME);
756       input.old_fire = DOWN;
757     }
758
759   /* tux animations: */
760   if(!frame_timer.check())
761     {
762       frame_timer.start(25);
763       if (input.right == UP && input.left == UP)
764         {
765           frame_main = 1;
766           frame_ = 1;
767         }
768       else
769         {
770           if ((input.fire == DOWN && (global_frame_counter % 2) == 0) ||
771               (global_frame_counter % 4) == 0)
772             frame_main = (frame_main + 1) % 4;
773
774           frame_ = frame_main;
775
776           if (frame_ == 3)
777             frame_ = 1;
778         }
779     }
780
781   /* Duck! */
782   if (input.down == DOWN && size == BIG && !duck && physic.get_velocity_y() == 0 && on_ground())
783     {
784       duck = true;
785       base.height = 32;                             
786       base.y += 32;
787       // changing base size confuses collision otherwise
788       old_base = previous_base = base;
789     }
790   else if(input.down == UP && size == BIG && duck)
791     {
792       // try if we can really unduck
793       base.y -= 32;
794       base.height = 64;
795       // when unducking in air we need some space to do so
796       if(on_ground() || !collision_object_map(base)) {
797         duck = false;
798         // changing base size confuses collision otherwise
799         old_base = previous_base = base;                                
800       } else {
801         // undo the ducking changes
802         base.y += 32;
803         base.height = 32;
804       }   
805     }
806 }
807
808 void
809 Player::grow(bool animate)
810 {
811   if(size == BIG)
812     return;
813   
814   size = BIG;
815   base.height = 64;
816   base.y -= 32;
817
818   if(animate)
819     growing_timer.start(GROWING_TIME);
820
821   old_base = previous_base = base;
822 }
823
824 void
825 Player::grabdistros()
826 {
827   /* Grab distros: */
828   if (!dying)
829     {
830       Sector::current()->trygrabdistro(Vector(base.x, base.y), NO_BOUNCE);
831       Sector::current()->trygrabdistro(Vector(base.x+ 31, base.y), NO_BOUNCE);
832       Sector::current()->trygrabdistro(
833           Vector(base.x, base.y + base.height), NO_BOUNCE);
834       Sector::current()->trygrabdistro(
835           Vector(base.x+ 31, base.y + base.height), NO_BOUNCE);
836
837       if(size == BIG)
838         {
839           Sector::current()->trygrabdistro(
840               Vector(base.x, base.y + base.height / 2), NO_BOUNCE);
841           Sector::current()->trygrabdistro(
842               Vector(base.x+ 31, base.y + base.height / 2), NO_BOUNCE);
843         }
844
845     }
846
847   /* Enough distros for a One-up? */
848   if (player_status.distros >= DISTROS_LIFEUP)
849     {
850       player_status.distros = player_status.distros - DISTROS_LIFEUP;
851       if(player_status.lives < MAX_LIVES)
852         ++player_status.lives;
853       /*We want to hear the sound even, if MAX_LIVES is reached*/
854       SoundManager::get()->play_sound(IDToSound(SND_LIFEUP));
855     }
856 }
857
858 void
859 Player::draw(DrawingContext& context)
860 {
861   TuxBodyParts* tux_body;
862           
863   if (size == SMALL)
864     tux_body = small_tux;
865   else if (got_power == FIRE_POWER)
866     tux_body = fire_tux;
867   else if (got_power == ICE_POWER)
868     tux_body = ice_tux;
869   else
870     tux_body = big_tux;
871
872   int layer = LAYER_OBJECTS - 1;
873   Vector pos = Vector(base.x, base.y);
874
875   /* Set Tux sprite action */
876   if (duck && size == BIG)
877     {
878     if(dir == LEFT)
879       tux_body->set_action("duck-left");
880     else // dir == RIGHT
881       tux_body->set_action("duck-right");
882     }
883   else if (skidding_timer.started())
884     {
885     if(dir == LEFT)
886       tux_body->set_action("skid-left");
887     else // dir == RIGHT
888       tux_body->set_action("skid-right");
889     }
890   else if (kick_timer.started())
891     {
892     if(dir == LEFT)
893       tux_body->set_action("kick-left");
894     else // dir == RIGHT
895       tux_body->set_action("kick-right");
896     }
897   else if (butt_jump)
898     {
899     if(dir == LEFT)
900       tux_body->set_action("buttjump-left");
901     else // dir == RIGHT
902       tux_body->set_action("buttjump-right");
903     }
904   else if (physic.get_velocity_y() != 0)
905     {
906     if(dir == LEFT)
907       tux_body->set_action("jump-left");
908     else // dir == RIGHT
909       tux_body->set_action("jump-right");
910     }
911   else
912     {
913     if (fabsf(physic.get_velocity_x()) < 1.0f) // standing
914       {
915       if(dir == LEFT)
916         tux_body->set_action("stand-left");
917       else // dir == RIGHT
918         tux_body->set_action("stand-right");
919       }
920     else // moving
921       {
922       if(dir == LEFT)
923         tux_body->set_action("walk-left");
924       else // dir == RIGHT
925         tux_body->set_action("walk-right");
926       }
927     }
928
929   if(idle_timer.get_left() < 0)
930     {
931     if(size == BIG)
932       {
933       if(dir == LEFT)
934         tux_body->head->set_action("idle-left");
935       else // dir == RIGHT
936         tux_body->head->set_action("idle-right");
937
938       tux_body->head->start_animation(1);
939       }
940
941     idle_timer.start(IDLE_TIME);
942     }
943
944   // Tux is holding something
945   if ((holding_something && physic.get_velocity_y() == 0) ||
946       shooting_timer.check())
947     {
948     if (duck)
949       {
950       if(dir == LEFT)
951         tux_body->arms->set_action("duck+grab-left");
952       else // dir == RIGHT
953         tux_body->arms->set_action("duck+grab-right");
954       }
955     else
956       {
957       if(dir == LEFT)
958         tux_body->arms->set_action("grab-left");
959       else // dir == RIGHT
960         tux_body->arms->set_action("grab-right");
961       }
962     }
963
964   /* Draw Tux */
965   if (dying == DYING_SQUISHED)
966     {
967     smalltux_gameover->draw(context, pos, LAYER_FOREGROUNDTILES+1);
968     }
969   else if(growing_timer.check())
970     {
971     if(size == SMALL)
972       {
973       if (dir == RIGHT)
974         context.draw_surface(growingtux_right[GROWING_FRAMES-1 - 
975                  ((growing_timer.get_gone() *
976                  GROWING_FRAMES) / GROWING_TIME)], pos, layer);
977       else
978         context.draw_surface(growingtux_left[GROWING_FRAMES-1 - 
979                 ((growing_timer.get_gone() *
980                 GROWING_FRAMES) / GROWING_TIME)], pos, layer);
981       }
982     else
983       {
984       if (dir == RIGHT)
985         context.draw_surface(growingtux_right[(growing_timer.get_gone() *
986                 GROWING_FRAMES) / GROWING_TIME], pos, layer);
987       else
988         context.draw_surface(growingtux_left[(growing_timer.get_gone() *
989                              GROWING_FRAMES) / GROWING_TIME], pos, layer);
990       }
991     }
992   else if (safe_timer.started() && global_frame_counter%2)
993     ;  // don't draw Tux
994   else
995     tux_body->draw(context, pos, layer, dir == LEFT ? HORIZONTAL_FLIP : NONE_EFFECT);
996
997   // Draw blinking star overlay
998   if (invincible_timer.started() &&
999      (invincible_timer.get_left() > TUX_INVINCIBLE_TIME_WARNING || global_frame_counter % 3)
1000      && !dying)
1001   {
1002     if (size == SMALL || duck)
1003       smalltux_star->draw(context, pos, LAYER_OBJECTS + 2);
1004     else
1005       bigtux_star->draw(context, pos, LAYER_OBJECTS + 2);
1006   }
1007  
1008   if (debug_mode)
1009     context.draw_filled_rect(Vector(base.x, base.y),
1010         Vector(base.width, base.height), Color(75,75,75, 150), LAYER_OBJECTS+1);
1011 }
1012
1013 void
1014 Player::collision(const MovingObject& other, int collision_type)
1015 {
1016   (void) other;
1017   (void) collision_type;
1018   // will be implemented later
1019 }
1020
1021 void
1022 Player::collision(void* p_c_object, int c_object)
1023 {
1024   BadGuy* pbad_c = NULL;
1025   Trampoline* ptramp_c = NULL;
1026   FlyingPlatform* pplatform_c = NULL;
1027
1028   switch (c_object)
1029     {
1030     case CO_BADGUY:
1031       pbad_c = (BadGuy*) p_c_object;
1032
1033      /* Hurt player if he touches a badguy */
1034       if (!pbad_c->dying && !dying &&
1035           !safe_timer.started() &&
1036           pbad_c->mode != BadGuy::HELD)
1037         {
1038           if (pbad_c->mode == BadGuy::FLAT && input.fire == DOWN
1039                && !holding_something)
1040             {
1041               holding_something = true;
1042               pbad_c->mode = BadGuy::HELD;
1043               pbad_c->base.y-=8;
1044             }
1045           else if (pbad_c->mode == BadGuy::FLAT)
1046             {
1047               // Don't get hurt if we're kicking a flat badguy!
1048             }
1049           else if (pbad_c->mode == BadGuy::KICK)
1050             {
1051               /* Hurt if you get hit by kicked laptop: */
1052               if (!invincible_timer.started())
1053                 {
1054                   kill(SHRINK);
1055                 }
1056               else
1057                 pbad_c->kill_me(20);
1058             }
1059           else if (pbad_c->frozen_timer.check() && (pbad_c->kind == BAD_MRBOMB
1060               || pbad_c->kind == BAD_JUMPY || pbad_c->kind == BAD_FISH
1061               || pbad_c->kind == BAD_SPIKY))
1062                 pbad_c->kill_me(20);
1063           else
1064             {
1065               if (!invincible_timer.started())
1066                 {
1067                   kill(SHRINK);
1068                 }
1069               else
1070                 {
1071                   pbad_c->kill_me(25);
1072                 }
1073             }
1074           player_status.score_multiplier++;
1075         }
1076       break;
1077
1078     case CO_TRAMPOLINE:
1079       ptramp_c = (Trampoline*) p_c_object;
1080       
1081       // Pick up trampoline
1082       if (ptramp_c->mode != Trampoline::M_HELD && input.fire == DOWN && !holding_something && on_ground())
1083       {
1084         holding_something = true;
1085         ptramp_c->mode = Trampoline::M_HELD;
1086         ptramp_c->base.y -= 8;
1087       }
1088       // Set down trampoline
1089       else if (ptramp_c->mode == Trampoline::M_HELD && input.fire != DOWN)
1090       {
1091         holding_something = false;
1092         ptramp_c->mode = Trampoline::M_NORMAL;
1093         ptramp_c->base.y += 8;
1094         ptramp_c->physic.set_velocity(physic.get_velocity_x(), physic.get_velocity_y());
1095
1096         //if (dir == RIGHT)
1097         //  ptramp_c->base.x = base.x + base.width+1;
1098         //else /* LEFT */
1099         //  ptramp_c->base.x = base.x - base.width-1;
1100       }
1101 /*
1102       // Don't let tux walk through trampoline
1103       else if (ptramp_c->mode != Trampoline::M_HELD && on_ground())
1104       {
1105         if (physic.get_velocity_x() > 0) // RIGHT
1106         {
1107           physic.set_velocity_x(0);
1108           base.x = ptramp_c->base.x - base.width;
1109         }
1110         else if (physic.get_velocity_x() < 0) // LEFT
1111         {
1112           physic.set_velocity_x(0);
1113           base.x = ptramp_c->base.x + ptramp_c->base.width;
1114         }
1115       }
1116 */
1117       break;
1118     case CO_FLYING_PLATFORM:
1119       pplatform_c = (FlyingPlatform*) p_c_object;
1120       
1121       base.y = pplatform_c->base.y - base.height;
1122       physic.set_velocity_x(pplatform_c->get_vel_x());
1123       
1124       physic.enable_gravity(false);
1125       can_jump = true;
1126       fall_mode = ON_GROUND;
1127       break;
1128
1129     default:
1130       break;
1131     }
1132
1133 }
1134
1135 /* Kill Player! */
1136
1137 void
1138 Player::kill(HurtMode mode)
1139 {
1140   if(dying)
1141     return;
1142   
1143   SoundManager::get()->play_sound(IDToSound(SND_HURT));
1144
1145   physic.set_velocity_x(0);
1146
1147   if (mode == SHRINK && size == BIG)
1148     {
1149       if (got_power != NONE_POWER)
1150         {
1151           safe_timer.start(TUX_SAFE_TIME);
1152           got_power = NONE_POWER;
1153         }
1154       else
1155         {
1156           growing_timer.start(GROWING_TIME);
1157           safe_timer.start(TUX_SAFE_TIME + GROWING_TIME);
1158           size = SMALL;
1159           base.height = 32;
1160           duck = false;
1161         }
1162     }
1163   else
1164     {
1165       physic.enable_gravity(true);
1166       physic.set_acceleration(0, 0);
1167       physic.set_velocity(0, 7);
1168       --player_status.lives;
1169       dying = DYING_SQUISHED;
1170       dying_timer.start(3000);
1171     }
1172 }
1173
1174 /* Remove Tux's power ups */
1175 void
1176 Player::remove_powerups()
1177 {
1178   got_power = NONE_POWER;
1179   size = SMALL;
1180   base.height = 32;
1181 }
1182
1183 void
1184 Player::move(const Vector& vector)
1185 {
1186   base.x = vector.x;
1187   base.y = vector.y;
1188   old_base = previous_base = base;
1189 }
1190
1191 void
1192 Player::check_bounds(Camera* camera)
1193 {
1194   /* Keep tux in bounds: */
1195   if (base.x < 0)
1196     { // Lock Tux to the size of the level, so that he doesn't fall of
1197       // on the left side
1198       base.x = 0;
1199     }
1200
1201   /* Keep in-bounds, vertically: */
1202   if (base.y > Sector::current()->solids->get_height() * 32)
1203     {
1204       kill(KILL);
1205       return;
1206     }
1207
1208   bool adjust = false;
1209   // can happen if back scrolling is disabled
1210   if(base.x < camera->get_translation().x) {
1211     base.x = camera->get_translation().x;
1212     adjust = true;
1213   }
1214   if(base.x >= camera->get_translation().x + screen->w - base.width) {
1215     base.x = camera->get_translation().x + screen->w - base.width;
1216     adjust = true;
1217   }
1218
1219   if(adjust) {
1220     // squished now?
1221     if(collision_object_map(base)) {
1222       kill(KILL);
1223       return;
1224     }
1225   }
1226 }
1227
1228 void
1229 Player::bounce(BadGuy* badguy)
1230 {
1231   //Make sure we stopped flapping
1232   flapping = false;
1233   falling_from_flap = false;
1234   
1235   if(player_status.score_multiplier >= 5)
1236     {  // show a message
1237     char str[124];
1238 //      if (player_status.score_multiplier <= 4) {sprintf(str, _("Combo x%d"), player_status.score_multiplier);}
1239       if (player_status.score_multiplier == 5)
1240         sprintf(str, _("Good! x%d"), player_status.score_multiplier);
1241       else if (player_status.score_multiplier == 6)
1242         sprintf(str, _("Great! x%d"), player_status.score_multiplier);
1243       else if (player_status.score_multiplier == 7)
1244         sprintf(str, _("Awesome! x%d"), player_status.score_multiplier);
1245       else if (player_status.score_multiplier == 8)
1246         sprintf(str, _("Incredible! x%d"), player_status.score_multiplier);
1247       else if (player_status.score_multiplier == 9)
1248         sprintf(str, _("Godlike! ;-) x%d"), player_status.score_multiplier);
1249       else
1250         sprintf(str, _("Unbelievable!! x%d"), player_status.score_multiplier);
1251     Sector::current()->add_floating_text(base, str);
1252     }
1253
1254   if (input.jump)
1255     physic.set_velocity_y(5.2);
1256   else
1257     physic.set_velocity_y(2);
1258
1259   // Move the player a little bit above the badguy to avoid collision
1260   // between badguy and player directly after the bounce has happend
1261   base.y = badguy->base.y - base.height - 2;
1262 }
1263
1264 /* EOF */
1265