Added an effect when skidding to check if a similar effect is what Marek was looking...
[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     {
493       // let's skid!
494       if(fabs(vx)>SKID_XM && !skidding_timer.check())
495         {
496           skidding_timer.start(SKID_TIME);
497           SoundManager::get()->play_sound(IDToSound(SND_SKID));
498           // dust some partcles
499           Sector::current()->add_particles(
500               Vector(base.x + (dir == LEFT ? base.width : 0), base.y+base.height),
501               dir == LEFT ? 270+20 : 90-40, dir == LEFT ? 270+40 : 90-20,
502               Vector(2.8,-2.6), Vector(0,0.030), 3, Color(100,100,100), 3, 800);
503
504           ax *= 2.5;
505         }
506       else
507         {
508           ax *= 2;
509         }
510     }
511
512   // we get slower when not pressing any keys
513   if(dirsign == 0) {
514       if(fabs(vx) < WALK_SPEED) {
515           vx = 0;
516           ax = 0;
517       } else if(vx < 0) {
518           ax = WALK_ACCELERATION_X * 1.5;
519       } else {
520           ax = WALK_ACCELERATION_X * -1.5;
521       }
522   }
523
524   // if we're on ice slow down acceleration or deceleration
525   if (isice(base.x, base.y + base.height))
526   {
527     /* the acceleration/deceleration rate on ice is inversely proportional to
528      * the current velocity.
529      */
530
531     // increasing 1 will increase acceleration/deceleration rate
532     // decreasing 1 will decrease acceleration/deceleration rate
533     //  must stay above zero, though
534     if (ax != 0) ax *= 1 / fabs(vx);
535   }
536
537   physic.set_velocity(vx, vy);
538   physic.set_acceleration(ax, ay);
539 }
540
541 void
542 Player::handle_vertical_input()
543 {
544   
545   // set fall mode...
546   if(on_ground()) {
547     fall_mode = ON_GROUND;
548     last_ground_y = base.y;
549   } else {
550     if(base.y > last_ground_y)
551       fall_mode = FALLING;
552     else if(fall_mode == ON_GROUND)
553       fall_mode = JUMPING;
554   }
555
556   // Press jump key
557   if(input.jump == DOWN && can_jump && on_ground())
558     {
559       if(duck) { // only jump a little bit when in duck mode {
560         physic.set_velocity_y(3);
561       } else {
562         // jump higher if we are running
563         if (fabs(physic.get_velocity_x()) > MAX_WALK_XM)
564           physic.set_velocity_y(5.8);
565         else
566           physic.set_velocity_y(5.2);
567       }
568
569       --base.y;
570       jumping = true;
571       flapping = false;
572       can_jump = false;
573       can_flap = false;
574       if (size == SMALL)
575         SoundManager::get()->play_sound(IDToSound(SND_JUMP));
576       else
577         SoundManager::get()->play_sound(IDToSound(SND_BIGJUMP));
578     }
579   // Let go of jump key
580   else if(input.jump == UP)
581     {
582       if (!flapping && !duck && !falling_from_flap && !on_ground())
583          {
584             can_flap = true;
585          }
586       if (jumping && physic.get_velocity_y() > 0)
587          {
588             jumping = false;
589             physic.set_velocity_y(0);
590          }
591     }
592
593    
594    // Flapping, Marek's version
595    if (input.jump == DOWN && can_flap)
596      {
597          if (!flapping_timer.started())
598             {
599                flapping_timer.start(TUX_FLAPPING_TIME);
600                flapping_velocity = physic.get_velocity_x();
601             }
602          if (!flapping_timer.check()) 
603             {
604                can_flap = false;
605                falling_from_flap = true;
606             }
607          jumping = true;
608          flapping = true;
609          if (flapping_timer.get_gone() <= TUX_FLAPPING_TIME)
610             {
611                float cv;
612                if (flapping_velocity == 0) {cv = 0;}
613                else {cv = flapping_velocity*(sqrt(TUX_FLAPPING_TIME-(float)flapping_timer.get_gone()))/sqrt(TUX_FLAPPING_TIME);}
614                //Handle change of direction while flapping
615                if (((dir == LEFT) && (cv > 0)) || (dir == RIGHT) && (cv < 0)) {cv *= (-1);}
616                physic.set_velocity_x(cv);
617                physic.set_velocity_y((float)flapping_timer.get_gone()/850);
618             }
619      }
620      
621    /* // Flapping, Ryan's version
622    if (input.jump == DOWN && can_flap)
623      {
624          if (!flapping_timer.started())
625             {
626                flapping_timer.start(TUX_FLAPPING_TIME);
627             }
628          if (!flapping_timer.check()) 
629             {
630                can_flap = false;
631                falling_from_flap = true;
632             }
633          jumping = true;
634          flapping = true;
635          if (flapping && flapping_timer.get_gone() <= TUX_FLAPPING_TIME
636                 && physic.get_velocity_y() < 0)
637             {
638                float gravity = Sector::current()->gravity;
639                float xr = (fabsf(physic.get_velocity_x()) / MAX_RUN_XM);
640
641                // XXX: magic numbers. should be a percent of gravity
642                //      gravity is (by default) -0.1f
643                physic.set_acceleration_y(.12 + .01f*xr);
644
645 #if 0
646                // To slow down x-vel when flapping (not working)
647                if (fabsf(physic.get_velocity_x()) > MAX_WALK_XM)
648                {
649                    if (physic.get_velocity_x() < 0)
650                        physic.set_acceleration_x(1.0f);
651                    else if (physic.get_velocity_x() > 0)
652                        physic.set_acceleration_x(-1.0f);
653                }
654 #endif
655             }
656      }
657     else
658      {
659         physic.set_acceleration_y(0);
660      }
661    */
662
663    // Hover
664    //(disabled by default, use cheat code "hover" to toggle on/off)
665    //TODO: needs some tweaking, especially when used together with double jump and jumping off badguys
666    if (enable_hover && input.jump == DOWN && !jumping && !butt_jump && physic.get_velocity_y() <= 0)
667       {
668          physic.set_velocity_y(-1);
669       }
670
671    /* In case the player has pressed Down while in a certain range of air,
672       enable butt jump action */
673   if (input.down == DOWN && !butt_jump && !duck)
674     if(tiles_on_air(TILES_FOR_BUTTJUMP) && jumping)
675       butt_jump = true;
676
677    /* When Down is not held anymore, disable butt jump */
678   if(butt_jump && input.down == UP)
679     butt_jump = false;
680
681   // Do butt jump
682   if (butt_jump && on_ground() && size == BIG)
683   {
684     // Add a smoke cloud
685     if (duck) 
686       Sector::current()->add_smoke_cloud(Vector(base.x - 32, base.y));
687     else 
688       Sector::current()->add_smoke_cloud(Vector(base.x - 32, base.y + 32));
689     
690     butt_jump = false;
691
692     // Break bricks beneath Tux
693     if(Sector::current()->trybreakbrick(
694           Vector(base.x + 1, base.y + base.height), false)
695         || Sector::current()->trybreakbrick(
696            Vector(base.x + base.width - 1, base.y + base.height), false))
697     {
698       physic.set_velocity_y(2);
699       butt_jump = true;
700     }
701
702     // Kill nearby badguys
703     std::vector<GameObject*> gameobjects = Sector::current()->gameobjects;
704     for (std::vector<GameObject*>::iterator i = gameobjects.begin();
705          i != gameobjects.end();
706          i++)
707     {
708       BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
709       if(badguy)
710       {
711         // don't kill when badguys are already dying or in a certain mode
712         if(badguy->dying == DYING_NOT && badguy->mode != BadGuy::BOMB_TICKING &&
713            badguy->mode != BadGuy::BOMB_EXPLODE)
714           {
715             if (fabsf(base.x - badguy->base.x) < 150 &&
716               fabsf(base.y - badguy->base.y) < 60 &&
717               (issolid(badguy->base.x + 1, badguy->base.y + badguy->base.height) ||
718                issolid(badguy->base.x + badguy->base.width - 1, badguy->base.y + badguy->base.height)))
719               badguy->kill_me(25);
720           }
721       }
722     }
723   }
724
725   if ( (issolid(base.x + base.width / 2, base.y + base.height + 64) ||
726         issolid(base.x + 1, base.y + base.height + 64) ||
727         issolid(base.x + base.width - 1, base.y + base.height + 64))
728        && jumping  == false
729        && can_jump == false
730        && input.jump == DOWN
731        && input.old_jump == UP)
732     {
733       can_jump = true;
734     }
735
736   if(on_ground())   /* Make sure jumping is off. */
737     {
738       jumping = false;
739       flapping = false;
740       falling_from_flap = false;
741       if (flapping_timer.started()) {flapping_timer.stop();}
742
743       physic.set_acceleration_y(0); //for flapping
744     }
745
746   input.old_jump = input.jump;
747 }
748
749 void
750 Player::handle_input()
751 {
752   /* Handle horizontal movement: */
753     handle_horizontal_input();
754
755   /* Jump/jumping? */
756
757   if (on_ground() && input.jump == UP)
758     can_jump = true;
759   handle_vertical_input();
760
761   /* Shoot! */
762   if (input.fire == DOWN && input.old_fire == UP && got_power != NONE_POWER)
763     {
764       if(Sector::current()->add_bullet(Vector(base.x, base.y + (base.height/2)),
765           physic.get_velocity_x(), dir))
766         shooting_timer.start(SHOOTING_TIME);
767       input.old_fire = DOWN;
768     }
769
770   /* tux animations: */
771   if(!frame_timer.check())
772     {
773       frame_timer.start(25);
774       if (input.right == UP && input.left == UP)
775         {
776           frame_main = 1;
777           frame_ = 1;
778         }
779       else
780         {
781           if ((input.fire == DOWN && (global_frame_counter % 2) == 0) ||
782               (global_frame_counter % 4) == 0)
783             frame_main = (frame_main + 1) % 4;
784
785           frame_ = frame_main;
786
787           if (frame_ == 3)
788             frame_ = 1;
789         }
790     }
791
792   /* Duck! */
793   if (input.down == DOWN && size == BIG && !duck && physic.get_velocity_y() == 0 && on_ground())
794     {
795       duck = true;
796       base.height = 32;                             
797       base.y += 32;
798       // changing base size confuses collision otherwise
799       old_base = previous_base = base;
800     }
801   else if(input.down == UP && size == BIG && duck)
802     {
803       // try if we can really unduck
804       base.y -= 32;
805       base.height = 64;
806       // when unducking in air we need some space to do so
807       if(on_ground() || !collision_object_map(base)) {
808         duck = false;
809         // changing base size confuses collision otherwise
810         old_base = previous_base = base;                                
811       } else {
812         // undo the ducking changes
813         base.y += 32;
814         base.height = 32;
815       }   
816     }
817 }
818
819 void
820 Player::grow(bool animate)
821 {
822   if(size == BIG)
823     return;
824   
825   size = BIG;
826   base.height = 64;
827   base.y -= 32;
828
829   if(animate)
830     growing_timer.start(GROWING_TIME);
831
832   old_base = previous_base = base;
833 }
834
835 void
836 Player::grabdistros()
837 {
838   /* Grab distros: */
839   if (!dying)
840     {
841       Sector::current()->trygrabdistro(Vector(base.x, base.y), NO_BOUNCE);
842       Sector::current()->trygrabdistro(Vector(base.x+ 31, base.y), NO_BOUNCE);
843       Sector::current()->trygrabdistro(
844           Vector(base.x, base.y + base.height), NO_BOUNCE);
845       Sector::current()->trygrabdistro(
846           Vector(base.x+ 31, base.y + base.height), NO_BOUNCE);
847
848       if(size == BIG)
849         {
850           Sector::current()->trygrabdistro(
851               Vector(base.x, base.y + base.height / 2), NO_BOUNCE);
852           Sector::current()->trygrabdistro(
853               Vector(base.x+ 31, base.y + base.height / 2), NO_BOUNCE);
854         }
855
856     }
857
858   /* Enough distros for a One-up? */
859   if (player_status.distros >= DISTROS_LIFEUP)
860     {
861       player_status.distros = player_status.distros - DISTROS_LIFEUP;
862       if(player_status.lives < MAX_LIVES)
863         ++player_status.lives;
864       /*We want to hear the sound even, if MAX_LIVES is reached*/
865       SoundManager::get()->play_sound(IDToSound(SND_LIFEUP));
866     }
867 }
868
869 void
870 Player::draw(DrawingContext& context)
871 {
872   TuxBodyParts* tux_body;
873           
874   if (size == SMALL)
875     tux_body = small_tux;
876   else if (got_power == FIRE_POWER)
877     tux_body = fire_tux;
878   else if (got_power == ICE_POWER)
879     tux_body = ice_tux;
880   else
881     tux_body = big_tux;
882
883   int layer = LAYER_OBJECTS - 1;
884   Vector pos = Vector(base.x, base.y);
885
886   /* Set Tux sprite action */
887   if (duck && size == BIG)
888     {
889     if(dir == LEFT)
890       tux_body->set_action("duck-left");
891     else // dir == RIGHT
892       tux_body->set_action("duck-right");
893     }
894   else if (skidding_timer.started())
895     {
896     if(dir == LEFT)
897       tux_body->set_action("skid-left");
898     else // dir == RIGHT
899       tux_body->set_action("skid-right");
900     }
901   else if (kick_timer.started())
902     {
903     if(dir == LEFT)
904       tux_body->set_action("kick-left");
905     else // dir == RIGHT
906       tux_body->set_action("kick-right");
907     }
908   else if (butt_jump && size == BIG)
909     {
910     if(dir == LEFT)
911       tux_body->set_action("buttjump-left");
912     else // dir == RIGHT
913       tux_body->set_action("buttjump-right");
914     }
915   else if (physic.get_velocity_y() != 0)
916     {
917     if(dir == LEFT)
918       tux_body->set_action("jump-left");
919     else // dir == RIGHT
920       tux_body->set_action("jump-right");
921     }
922   else
923     {
924     if (fabsf(physic.get_velocity_x()) < 1.0f) // standing
925       {
926       if(dir == LEFT)
927         tux_body->set_action("stand-left");
928       else // dir == RIGHT
929         tux_body->set_action("stand-right");
930       }
931     else // moving
932       {
933       if(dir == LEFT)
934         tux_body->set_action("walk-left");
935       else // dir == RIGHT
936         tux_body->set_action("walk-right");
937       }
938     }
939
940   if(idle_timer.get_left() < 0)
941     {
942     if(size == BIG)
943       {
944       if(dir == LEFT)
945         tux_body->head->set_action("idle-left");
946       else // dir == RIGHT
947         tux_body->head->set_action("idle-right");
948
949       tux_body->head->start_animation(1);
950       }
951
952     idle_timer.start(IDLE_TIME);
953     }
954
955   // Tux is holding something
956   if ((holding_something && physic.get_velocity_y() == 0) ||
957       shooting_timer.check())
958     {
959     if (duck)
960       {
961       if(dir == LEFT)
962         tux_body->arms->set_action("duck+grab-left");
963       else // dir == RIGHT
964         tux_body->arms->set_action("duck+grab-right");
965       }
966     else
967       {
968       if(dir == LEFT)
969         tux_body->arms->set_action("grab-left");
970       else // dir == RIGHT
971         tux_body->arms->set_action("grab-right");
972       }
973     }
974
975   /* Draw Tux */
976   if (dying == DYING_SQUISHED)
977     {
978     smalltux_gameover->draw(context, pos, LAYER_FOREGROUNDTILES+1);
979     }
980   else if(growing_timer.check())
981     {
982     if(size == SMALL)
983       {
984       if (dir == RIGHT)
985         context.draw_surface(growingtux_right[GROWING_FRAMES-1 - 
986                  ((growing_timer.get_gone() *
987                  GROWING_FRAMES) / GROWING_TIME)], pos, layer);
988       else
989         context.draw_surface(growingtux_left[GROWING_FRAMES-1 - 
990                 ((growing_timer.get_gone() *
991                 GROWING_FRAMES) / GROWING_TIME)], pos, layer);
992       }
993     else
994       {
995       if (dir == RIGHT)
996         context.draw_surface(growingtux_right[(growing_timer.get_gone() *
997                 GROWING_FRAMES) / GROWING_TIME], pos, layer);
998       else
999         context.draw_surface(growingtux_left[(growing_timer.get_gone() *
1000                              GROWING_FRAMES) / GROWING_TIME], pos, layer);
1001       }
1002     }
1003   else if (safe_timer.started() && global_frame_counter%2)
1004     ;  // don't draw Tux
1005   else
1006     tux_body->draw(context, pos, layer);
1007
1008   // Draw blinking star overlay
1009   if (invincible_timer.started() &&
1010      (invincible_timer.get_left() > TUX_INVINCIBLE_TIME_WARNING || global_frame_counter % 3)
1011      && !dying)
1012   {
1013     if (size == SMALL || duck)
1014       smalltux_star->draw(context, pos, LAYER_OBJECTS + 2);
1015     else
1016       bigtux_star->draw(context, pos, LAYER_OBJECTS + 2);
1017   }
1018  
1019   if (debug_mode)
1020     context.draw_filled_rect(Vector(base.x, base.y),
1021         Vector(base.width, base.height), Color(75,75,75, 150), LAYER_OBJECTS+1);
1022 }
1023
1024 void
1025 Player::collision(const MovingObject& other, int collision_type)
1026 {
1027   (void) other;
1028   (void) collision_type;
1029   // will be implemented later
1030 }
1031
1032 void
1033 Player::collision(void* p_c_object, int c_object)
1034 {
1035   BadGuy* pbad_c = NULL;
1036   Trampoline* ptramp_c = NULL;
1037   FlyingPlatform* pplatform_c = NULL;
1038
1039   switch (c_object)
1040     {
1041     case CO_BADGUY:
1042       pbad_c = (BadGuy*) p_c_object;
1043
1044      /* Hurt player if he touches a badguy */
1045       if (!pbad_c->dying && !dying &&
1046           !safe_timer.started() &&
1047           pbad_c->mode != BadGuy::HELD)
1048         {
1049           if (pbad_c->mode == BadGuy::FLAT && input.fire == DOWN
1050                && !holding_something)
1051             {
1052               holding_something = true;
1053               pbad_c->mode = BadGuy::HELD;
1054               pbad_c->base.y-=8;
1055             }
1056           else if (pbad_c->mode == BadGuy::FLAT)
1057             {
1058               // Don't get hurt if we're kicking a flat badguy!
1059             }
1060           else if (pbad_c->mode == BadGuy::KICK)
1061             {
1062               /* Hurt if you get hit by kicked laptop: */
1063               if (!invincible_timer.started())
1064                 {
1065                   kill(SHRINK);
1066                 }
1067               else
1068                 pbad_c->kill_me(20);
1069             }
1070           else if (pbad_c->frozen_timer.check() && (pbad_c->kind == BAD_MRBOMB
1071               || pbad_c->kind == BAD_JUMPY || pbad_c->kind == BAD_FISH
1072               || pbad_c->kind == BAD_SPIKY))
1073                 pbad_c->kill_me(20);
1074           else
1075             {
1076               if (!invincible_timer.started())
1077                 {
1078                   kill(SHRINK);
1079                 }
1080               else
1081                 {
1082                   pbad_c->kill_me(25);
1083                 }
1084             }
1085           player_status.score_multiplier++;
1086         }
1087       break;
1088
1089     case CO_TRAMPOLINE:
1090       ptramp_c = (Trampoline*) p_c_object;
1091       
1092       // Pick up trampoline
1093       if (ptramp_c->mode != Trampoline::M_HELD && input.fire == DOWN && !holding_something && on_ground())
1094       {
1095         holding_something = true;
1096         ptramp_c->mode = Trampoline::M_HELD;
1097         ptramp_c->base.y -= 8;
1098       }
1099       // Set down trampoline
1100       else if (ptramp_c->mode == Trampoline::M_HELD && input.fire != DOWN)
1101       {
1102         holding_something = false;
1103         ptramp_c->mode = Trampoline::M_NORMAL;
1104         ptramp_c->base.y += 8;
1105         ptramp_c->physic.set_velocity(physic.get_velocity_x(), physic.get_velocity_y());
1106
1107         //if (dir == RIGHT)
1108         //  ptramp_c->base.x = base.x + base.width+1;
1109         //else /* LEFT */
1110         //  ptramp_c->base.x = base.x - base.width-1;
1111       }
1112 /*
1113       // Don't let tux walk through trampoline
1114       else if (ptramp_c->mode != Trampoline::M_HELD && on_ground())
1115       {
1116         if (physic.get_velocity_x() > 0) // RIGHT
1117         {
1118           physic.set_velocity_x(0);
1119           base.x = ptramp_c->base.x - base.width;
1120         }
1121         else if (physic.get_velocity_x() < 0) // LEFT
1122         {
1123           physic.set_velocity_x(0);
1124           base.x = ptramp_c->base.x + ptramp_c->base.width;
1125         }
1126       }
1127 */
1128       break;
1129     case CO_FLYING_PLATFORM:
1130       pplatform_c = (FlyingPlatform*) p_c_object;
1131       
1132       base.y = pplatform_c->base.y - base.height;
1133       physic.set_velocity_x(pplatform_c->get_vel_x());
1134       
1135       physic.enable_gravity(false);
1136       can_jump = true;
1137       fall_mode = ON_GROUND;
1138       break;
1139
1140     default:
1141       break;
1142     }
1143
1144 }
1145
1146 /* Kill Player! */
1147
1148 void
1149 Player::kill(HurtMode mode)
1150 {
1151   if(dying)
1152     return;
1153   
1154   SoundManager::get()->play_sound(IDToSound(SND_HURT));
1155
1156   physic.set_velocity_x(0);
1157
1158   if (mode == SHRINK && size == BIG)
1159     {
1160       if (got_power != NONE_POWER)
1161         {
1162           safe_timer.start(TUX_SAFE_TIME);
1163           got_power = NONE_POWER;
1164         }
1165       else
1166         {
1167           growing_timer.start(GROWING_TIME);
1168           safe_timer.start(TUX_SAFE_TIME + GROWING_TIME);
1169           size = SMALL;
1170           base.height = 32;
1171           duck = false;
1172         }
1173     }
1174   else
1175     {
1176       physic.enable_gravity(true);
1177       physic.set_acceleration(0, 0);
1178       physic.set_velocity(0, 7);
1179       --player_status.lives;
1180       dying = DYING_SQUISHED;
1181       dying_timer.start(3000);
1182     }
1183 }
1184
1185 /* Remove Tux's power ups */
1186 void
1187 Player::remove_powerups()
1188 {
1189   got_power = NONE_POWER;
1190   size = SMALL;
1191   base.height = 32;
1192 }
1193
1194 void
1195 Player::move(const Vector& vector)
1196 {
1197   base.x = vector.x;
1198   base.y = vector.y;
1199   old_base = previous_base = base;
1200 }
1201
1202 void
1203 Player::check_bounds(Camera* camera)
1204 {
1205   /* Keep tux in bounds: */
1206   if (base.x < 0)
1207     { // Lock Tux to the size of the level, so that he doesn't fall of
1208       // on the left side
1209       base.x = 0;
1210     }
1211
1212   /* Keep in-bounds, vertically: */
1213   if (base.y > Sector::current()->solids->get_height() * 32)
1214     {
1215       kill(KILL);
1216       return;
1217     }
1218
1219   bool adjust = false;
1220   // can happen if back scrolling is disabled
1221   if(base.x < camera->get_translation().x) {
1222     base.x = camera->get_translation().x;
1223     adjust = true;
1224   }
1225   if(base.x >= camera->get_translation().x + screen->w - base.width) {
1226     base.x = camera->get_translation().x + screen->w - base.width;
1227     adjust = true;
1228   }
1229
1230   if(adjust) {
1231     // squished now?
1232     if(collision_object_map(base)) {
1233       kill(KILL);
1234       return;
1235     }
1236   }
1237 }
1238
1239 void
1240 Player::bounce(BadGuy* badguy)
1241 {
1242   //Make sure we stopped flapping
1243   flapping = false;
1244   falling_from_flap = false;
1245   
1246   if(player_status.score_multiplier >= 5)
1247     {  // show a message
1248     char str[124];
1249 //      if (player_status.score_multiplier <= 4) {sprintf(str, _("Combo x%d"), player_status.score_multiplier);}
1250       if (player_status.score_multiplier == 5)
1251         sprintf(str, _("Good! x%d"), player_status.score_multiplier);
1252       else if (player_status.score_multiplier == 6)
1253         sprintf(str, _("Great! x%d"), player_status.score_multiplier);
1254       else if (player_status.score_multiplier == 7)
1255         sprintf(str, _("Awesome! x%d"), player_status.score_multiplier);
1256       else if (player_status.score_multiplier == 8)
1257         sprintf(str, _("Incredible! x%d"), player_status.score_multiplier);
1258       else if (player_status.score_multiplier == 9)
1259         sprintf(str, _("Godlike! ;-) x%d"), player_status.score_multiplier);
1260       else
1261         sprintf(str, _("Unbelievable!! x%d"), player_status.score_multiplier);
1262     Sector::current()->add_floating_text(base, str);
1263     }
1264
1265   if (input.jump)
1266     physic.set_velocity_y(5.2);
1267   else
1268     physic.set_velocity_y(2);
1269
1270   // Move the player a little bit above the badguy to avoid collision
1271   // between badguy and player directly after the bounce has happend
1272   base.y = badguy->base.y - base.height - 2;
1273 }
1274
1275 /* EOF */
1276