fcbe6584996acf02604068d370e92532a58dde2c
[supertux.git] / src / object / 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 #include <config.h>
20
21 #include <typeinfo>
22 #include <cmath>
23 #include <iostream>
24 #include <cassert>
25
26 #include "app/globals.h"
27 #include "app/gettext.h"
28 #include "player.h"
29 #include "defines.h"
30 #include "scene.h"
31 #include "tile.h"
32 #include "special/sprite.h"
33 #include "sector.h"
34 #include "resources.h"
35 #include "video/screen.h"
36 #include "statistics.h"
37 #include "gameloop.h"
38 #include "object/tilemap.h"
39 #include "object/camera.h"
40 #include "object/gameobjs.h"
41 #include "trigger/trigger_base.h"
42
43 static const int TILES_FOR_BUTTJUMP = 3;
44 static const float SHOOTING_TIME = .150;
45 /// time before idle animation starts
46 static const float IDLE_TIME = 2.5;
47
48 static const float WALK_ACCELERATION_X = 300;
49 static const float RUN_ACCELERATION_X = 400;
50 static const float SKID_XM = 200;
51 static const float SKID_TIME = .3;
52 static const float MAX_WALK_XM = 230;
53 static const float MAX_RUN_XM = 320;
54 static const float WALK_SPEED = 100;
55
56 // growing animation
57 Surface* growingtux_left[GROWING_FRAMES];
58 Surface* growingtux_right[GROWING_FRAMES];
59
60 Surface* tux_life = 0;
61
62 Sprite* smalltux_gameover = 0;
63 Sprite* smalltux_star = 0;
64 Sprite* bigtux_star = 0;
65
66 TuxBodyParts* small_tux = 0;
67 TuxBodyParts* big_tux = 0;
68 TuxBodyParts* fire_tux = 0;
69 TuxBodyParts* ice_tux = 0;
70
71 PlayerKeymap keymap;
72
73 PlayerKeymap::PlayerKeymap()
74 {
75   keymap.up    = SDLK_UP;
76   keymap.down  = SDLK_DOWN;
77   keymap.left  = SDLK_LEFT;
78   keymap.right = SDLK_RIGHT;
79
80   keymap.power = SDLK_LCTRL;
81   keymap.jump  = SDLK_SPACE;
82 }
83
84 void player_input_init(player_input_type* pplayer_input)
85 {
86   pplayer_input->up = UP;
87   pplayer_input->old_up = UP;
88   pplayer_input->down = UP;
89   pplayer_input->fire = UP;
90   pplayer_input->left = UP;
91   pplayer_input->old_fire = UP;
92   pplayer_input->right = UP;
93   pplayer_input->jump = UP;
94   pplayer_input->old_jump = UP;
95   pplayer_input->activate = UP;
96 }
97
98 void
99 TuxBodyParts::set_action(std::string action, int loops)
100 {
101   if(head != NULL)
102     head->set_action(action, loops);
103   if(body != NULL)
104     body->set_action(action, loops);
105   if(arms != NULL)
106     arms->set_action(action, loops);
107   if(feet != NULL)
108     feet->set_action(action, loops);
109 }
110
111 void
112 TuxBodyParts::draw(DrawingContext& context, const Vector& pos, int layer,
113                   Uint32 drawing_effect)
114 {
115   if(head != NULL)
116     head->draw(context, pos, layer-1, drawing_effect);
117   if(body != NULL)
118     body->draw(context, pos, layer-3, drawing_effect);
119   if(arms != NULL)
120     arms->draw(context, pos, layer,   drawing_effect);
121   if(feet != NULL)
122     feet->draw(context, pos, layer-2, drawing_effect);
123 }
124
125 Player::Player()
126 {
127   init();
128 }
129
130 Player::~Player()
131 {
132 }
133
134 void
135 Player::init()
136 {
137   holding_something = false;
138
139   bbox.set_size(31.8, 31.8);
140
141   size = SMALL;
142   got_power = NONE_POWER;
143
144   dir = RIGHT;
145   old_dir = dir;
146   duck = false;
147   dead = false;
148
149   dying   = DYING_NOT;
150   last_ground_y = 0;
151   fall_mode = ON_GROUND;
152   jumping = false;
153   flapping = false;
154   can_jump = true;
155   can_flap = false;
156   falling_from_flap = false;
157   enable_hover = false;
158   butt_jump = false;
159   
160   flapping_velocity = 0;
161
162   // temporary to help player's choosing a flapping
163   flapping_mode = MAREK_FLAP;
164
165   // Ricardo's flapping
166   flaps_nb = 0;
167
168   on_ground_flag = false;
169
170   player_input_init(&input);
171
172   physic.reset();
173 }
174
175 int
176 Player::key_event(SDLKey key, int state)
177 {
178   idle_timer.start(IDLE_TIME, true);
179
180   if(key == keymap.right)
181     {
182       input.right = state;
183       return true;
184     }
185   else if(key == keymap.left)
186     {
187       input.left = state;
188       return true;
189     }
190   else if(key == keymap.up)
191     {
192       if(state == UP)
193         input.old_up = UP;
194       input.up = state;
195       /* Up key also opens activates stuff */
196       input.activate = state;
197       return true;
198     }
199   else if(key == keymap.down)
200     {
201       input.down = state;
202       return true;
203     }
204   else if(key == keymap.power)
205     {
206       if (state == UP)
207         input.old_fire = UP;
208       input.fire = state;
209
210       return true;
211     }
212   else if(key == keymap.jump)
213     {
214       if (state == UP)
215         input.old_jump = UP;
216       input.jump = state;
217       return true;
218     }
219   else
220     return false;
221 }
222
223 void
224 Player::level_begin()
225 {
226   move(Vector(100, 170));
227   duck = false;
228
229   dying = DYING_NOT;
230
231   player_input_init(&input);
232
233   on_ground_flag = false;
234
235   physic.reset();
236 }
237
238 PlayerStatus&
239 Player::get_status()
240 {
241   return player_status;
242 }
243
244 void
245 Player::action(float elapsed_time)
246 {
247   if(dying && dying_timer.check()) {
248     dead = true;
249     return;
250   }
251
252   if (input.fire == UP)
253     holding_something = false;
254
255   if(dying == DYING_NOT)
256     handle_input();
257
258   movement = physic.get_movement(elapsed_time);
259
260 #if 0
261       // special exception for cases where we're stuck under tiles after
262       // being ducked. In this case we drift out
263       if(!duck && on_ground() && old_base.x == base.x && old_base.y == base.y
264          && collision_object_map(base))
265         {
266           base.x += elapsed_time * WALK_SPEED * (dir ? 1: -1);
267           previous_base = old_base = base;
268         }
269
270       /* Reset score multiplier (for multi-hits): */
271       if (!invincible_timer.started())
272             {
273             if(player_status.score_multiplier > player_status.max_score_multiplier)
274               {
275               player_status.max_score_multiplier = player_status.score_multiplier;
276
277               // show a message
278               char str[124];
279               sprintf(str, _("New max combo: %d"), player_status.max_score_multiplier-1);
280               Sector::current()->add_floating_text(base, str);
281               }
282             player_status.score_multiplier = 1;
283             }
284         }
285
286     }
287 #endif
288
289   on_ground_flag = false;
290 }
291
292 bool
293 Player::on_ground()
294 {
295   return on_ground_flag;
296 }
297
298 void
299 Player::handle_horizontal_input()
300 {
301   float vx = physic.get_velocity_x();
302   float vy = physic.get_velocity_y();
303   float ax = physic.get_acceleration_x();
304   float ay = physic.get_acceleration_y();
305
306   float dirsign = 0;
307   if(input.left == DOWN && input.right == UP && (!duck || physic.get_velocity_y() != 0)) {
308       old_dir = dir;
309       dir = LEFT;
310       dirsign = -1;
311   } else if(input.left == UP && input.right == DOWN && (!duck || physic.get_velocity_y() != 0)) {
312       old_dir = dir;
313       dir = RIGHT;
314       dirsign = 1;
315   }
316
317   if (input.fire == UP) {
318       ax = dirsign * WALK_ACCELERATION_X;
319       // limit speed
320       if(vx >= MAX_WALK_XM && dirsign > 0) {
321         vx = MAX_WALK_XM;
322         ax = 0;
323       } else if(vx <= -MAX_WALK_XM && dirsign < 0) {
324         vx = -MAX_WALK_XM;
325         ax = 0;
326       }
327   } else {
328       ax = dirsign * RUN_ACCELERATION_X;
329       // limit speed
330       if(vx >= MAX_RUN_XM && dirsign > 0) {
331         vx = MAX_RUN_XM;
332         ax = 0;
333       } else if(vx <= -MAX_RUN_XM && dirsign < 0) {
334         vx = -MAX_RUN_XM;
335         ax = 0;
336       }
337   }
338
339   // we can reach WALK_SPEED without any acceleration
340   if(dirsign != 0 && fabs(vx) < WALK_SPEED) {
341     vx = dirsign * WALK_SPEED;
342   }
343
344   // changing directions?
345   if(on_ground() && ((vx < 0 && dirsign >0) || (vx>0 && dirsign<0)))
346     {
347       // let's skid!
348       if(fabs(vx)>SKID_XM && !skidding_timer.started())
349         {
350           skidding_timer.start(SKID_TIME);
351           SoundManager::get()->play_sound(IDToSound(SND_SKID));
352           // dust some partcles
353           Sector::current()->add_object(
354               new Particles(
355                 Vector(bbox.p1.x + (dir == RIGHT ? bbox.get_width() : 0),
356                 bbox.p2.y),
357               dir == RIGHT ? 270+20 : 90-40, dir == RIGHT ? 270+40 : 90-20,
358               Vector(280,-260), Vector(0,0.030), 3, Color(100,100,100), 3, .8,
359               LAYER_OBJECTS+1));
360
361           ax *= 2.5;
362         }
363       else
364         {
365           ax *= 2;
366         }
367     }
368
369   // we get slower when not pressing any keys
370   if(dirsign == 0) {
371       if(fabs(vx) < WALK_SPEED) {
372           vx = 0;
373           ax = 0;
374       } else if(vx < 0) {
375           ax = WALK_ACCELERATION_X * 1.5;
376       } else {
377           ax = WALK_ACCELERATION_X * -1.5;
378       }
379   }
380
381 #if 0
382   // if we're on ice slow down acceleration or deceleration
383   if (isice(base.x, base.y + base.height))
384   {
385     /* the acceleration/deceleration rate on ice is inversely proportional to
386      * the current velocity.
387      */
388
389     // increasing 1 will increase acceleration/deceleration rate
390     // decreasing 1 will decrease acceleration/deceleration rate
391     //  must stay above zero, though
392     if (ax != 0) ax *= 1 / fabs(vx);
393   }
394 #endif
395
396   // extend/shrink tux collision rectangle so that we fall through/walk over 1
397   // tile holes
398   if(fabsf(vx) > MAX_WALK_XM) {
399     bbox.set_width(33);
400   } else {
401     bbox.set_width(31.8);
402   }
403
404   physic.set_velocity(vx, vy);
405   physic.set_acceleration(ax, ay);
406 }
407
408 void
409 Player::handle_vertical_input()
410 {
411   // set fall mode...
412   if(on_ground()) {
413     fall_mode = ON_GROUND;
414     last_ground_y = get_pos().y;
415   } else {
416     if(get_pos().y > last_ground_y)
417       fall_mode = FALLING;
418     else if(fall_mode == ON_GROUND)
419       fall_mode = JUMPING;
420   }
421
422   if(on_ground()) { /* Make sure jumping is off. */
423     jumping = false;
424     flapping = false;
425     falling_from_flap = false;
426     if (flapping_timer.started()) {
427       flapping_timer.start(0);
428     }
429
430     physic.set_acceleration_y(0); //for flapping
431   }
432
433   // Press jump key
434   if(input.jump == DOWN && can_jump && on_ground())
435     {
436       if(duck) { // only jump a little bit when in duck mode {
437         physic.set_velocity_y(300);
438       } else {
439         // jump higher if we are running
440         if (fabs(physic.get_velocity_x()) > MAX_WALK_XM)
441           physic.set_velocity_y(580);
442         else
443           physic.set_velocity_y(520);
444       }
445
446       //bbox.move(Vector(0, -1));
447       jumping = true;
448       flapping = false;
449       can_jump = false;
450       can_flap = false;
451       flaps_nb = 0; // Ricardo's flapping
452       if (size == SMALL)
453         SoundManager::get()->play_sound(IDToSound(SND_JUMP));
454       else
455         SoundManager::get()->play_sound(IDToSound(SND_BIGJUMP));
456     }
457   // Let go of jump key
458   else if(input.jump == UP)
459     {
460       if (!flapping && !duck && !falling_from_flap && !on_ground())
461          {
462             can_flap = true;
463          }
464       if (jumping && physic.get_velocity_y() > 0)
465          {
466             jumping = false;
467             physic.set_velocity_y(0);
468          }
469     }
470
471  // temporary to help player's choosing a flapping
472  if(flapping_mode == RICARDO_FLAP)
473    {
474    // Flapping, Ricardo's version
475    // similar to SM3 Fox
476    if(input.jump == DOWN && input.old_jump == UP && can_flap &&
477      flaps_nb < 3)
478      {
479        physic.set_velocity_y(350);
480        physic.set_velocity_x(physic.get_velocity_x() * 35);
481        flaps_nb++;
482      }
483    }
484   else if(flapping_mode == MAREK_FLAP)
485    {
486    // Flapping, Marek's version
487    if (input.jump == DOWN && can_flap)
488      {
489          if (!flapping_timer.started())
490             {
491                flapping_timer.start(TUX_FLAPPING_TIME);
492                flapping_velocity = physic.get_velocity_x();
493             }
494          if (flapping_timer.check()) 
495             {
496                can_flap = false;
497                falling_from_flap = true;
498             }
499          jumping = true;
500          flapping = true;
501          if (!flapping_timer.check()) {
502            float cv = flapping_velocity * sqrt(
503                TUX_FLAPPING_TIME - flapping_timer.get_timegone() 
504                / TUX_FLAPPING_TIME);
505
506            //Handle change of direction while flapping
507            if (((dir == LEFT) && (cv > 0)) || (dir == RIGHT) && (cv < 0)) {
508              cv *= (-1);
509            }
510            physic.set_velocity_x(cv);
511            physic.set_velocity_y(
512                flapping_timer.get_timegone()/.850);
513          }
514      }
515    }
516   else if(flapping_mode == RYAN_FLAP)
517    {
518    // Flapping, Ryan's version
519    if (input.jump == DOWN && can_flap)
520      {
521          if (!flapping_timer.started())
522             {
523                flapping_timer.start(TUX_FLAPPING_TIME);
524             }
525          if (flapping_timer.check()) 
526             {
527                can_flap = false;
528                falling_from_flap = true;
529             }
530          jumping = true;
531          flapping = true;
532          if (flapping && flapping_timer.get_timegone() <= TUX_FLAPPING_TIME
533                 && physic.get_velocity_y() < 0)
534             {
535                float gravity = Sector::current()->gravity;
536                (void)gravity;
537                float xr = (fabsf(physic.get_velocity_x()) / MAX_RUN_XM);
538
539                // XXX: magic numbers. should be a percent of gravity
540                //      gravity is (by default) -0.1f
541                physic.set_acceleration_y(12 + 1*xr);
542
543 #if 0
544                // To slow down x-vel when flapping (not working)
545                if (fabsf(physic.get_velocity_x()) > MAX_WALK_XM)
546                {
547                    if (physic.get_velocity_x() < 0)
548                        physic.set_acceleration_x(1.0f);
549                    else if (physic.get_velocity_x() > 0)
550                        physic.set_acceleration_x(-1.0f);
551                }
552 #endif
553             }
554      }
555     else
556      {
557         physic.set_acceleration_y(0);
558      }
559    }
560
561    // Hover
562    //(disabled by default, use cheat code "hover" to toggle on/off)
563    //TODO: needs some tweaking, especially when used together with double jump and jumping off badguys
564    if (enable_hover && input.jump == DOWN && !jumping && !butt_jump && physic.get_velocity_y() <= 0)
565       {
566          physic.set_velocity_y(-100);
567       }
568
569 #if 0
570    /* In case the player has pressed Down while in a certain range of air,
571       enable butt jump action */
572   if (input.down == DOWN && !butt_jump && !duck)
573     if(tiles_on_air(TILES_FOR_BUTTJUMP) && jumping)
574       butt_jump = true;
575 #endif
576
577    /* When Down is not held anymore, disable butt jump */
578   if(butt_jump && input.down == UP)
579     butt_jump = false;
580
581   // Do butt jump
582   if (butt_jump && on_ground() && size == BIG)
583   {
584     // Add a smoke cloud
585     if (duck) 
586       Sector::current()->add_smoke_cloud(Vector(get_pos().x - 32, get_pos().y));
587     else 
588       Sector::current()->add_smoke_cloud(
589           Vector(get_pos().x - 32, get_pos().y + 32));
590     
591     butt_jump = false;
592
593 #if 0
594     // Break bricks beneath Tux
595     if(Sector::current()->trybreakbrick(
596           Vector(base.x + 1, base.y + base.height), false)
597         || Sector::current()->trybreakbrick(
598            Vector(base.x + base.width - 1, base.y + base.height), false))
599     {
600       physic.set_velocity_y(2);
601       butt_jump = true;
602     }
603 #endif
604
605 #if 0
606     // Kill nearby badguys
607     std::vector<GameObject*> gameobjects = Sector::current()->gameobjects;
608     for (std::vector<GameObject*>::iterator i = gameobjects.begin();
609          i != gameobjects.end();
610          i++)
611     {
612       BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
613       if(badguy)
614       {
615         // don't kill when badguys are already dying or in a certain mode
616         if(badguy->dying == DYING_NOT && badguy->mode != BadGuy::BOMB_TICKING &&
617            badguy->mode != BadGuy::BOMB_EXPLODE)
618           {
619             if (fabsf(base.x - badguy->base.x) < 96 &&
620                 fabsf(base.y - badguy->base.y) < 64)
621               badguy->kill_me(25);
622           }
623       }
624     }
625 #endif
626   }
627
628   /** jumping is only allowed if we're about to touch ground soon and if the
629    * button has been up in between the last jump
630    */
631   // FIXME
632 #if 0
633   if ( (issolid(get_pos().x + bbox.get_width() / 2,
634           get_pos().y + bbox.get_height() + 64) ||
635         issolid(get_pos().x + 1, get_pos().y + bbox.get_height() + 64) ||
636         issolid(get_pos().x + bbox.get_width() - 1,
637           get_pos().y + bbox.get_height() + 64))
638        && jumping  == false
639        && can_jump == false
640        && input.jump == DOWN
641        && input.old_jump == UP)
642     {
643       can_jump = true;
644     }
645 #endif
646
647   input.old_jump = input.jump;
648 }
649
650 void
651 Player::handle_input()
652 {
653   /* Handle horizontal movement: */
654   handle_horizontal_input();
655
656   /* Jump/jumping? */
657   if (on_ground() && input.jump == UP)
658     can_jump = true;
659   handle_vertical_input();
660
661   /* Shoot! */
662   if (input.fire == DOWN && input.old_fire == UP && got_power != NONE_POWER) {
663     if(Sector::current()->add_bullet(
664 //           get_pos() + Vector(0, bbox.get_height()/2),
665            get_pos() + ((dir == LEFT)? Vector(0, bbox.get_height()/2) 
666            : Vector(32, bbox.get_height()/2)),
667           physic.get_velocity_x(), dir))
668       shooting_timer.start(SHOOTING_TIME);
669     input.old_fire = DOWN;
670   }
671
672   /* Duck! */
673   if (input.down == DOWN && size == BIG && !duck 
674       && physic.get_velocity_y() == 0 && on_ground())
675     {
676       duck = true;
677       bbox.move(Vector(0, 32));
678       bbox.set_height(31.8);
679     }
680   else if(input.down == UP && size == BIG && duck)
681     {
682       // try if we can really unduck
683       bbox.move(Vector(0, -32));
684       bbox.set_height(63.8);
685       duck = false;
686       // FIXME
687 #if 0
688       // when unducking in air we need some space to do so
689       if(on_ground() || !collision_object_map(bbox)) {
690         duck = false;
691       } else {
692         // undo the ducking changes
693         bbox.move(Vector(0, 32));
694         bbox.set_height(31.8);
695       }
696 #endif
697     }
698 }
699
700 void
701 Player::grow(bool animate)
702 {
703   if(size == BIG)
704     return;
705   
706   size = BIG;
707   bbox.set_height(63.8);
708   bbox.move(Vector(0, -32));
709
710   if(animate)
711     growing_timer.start(GROWING_TIME);
712 }
713
714 void
715 Player::grabdistros()
716 {
717 }
718
719 void
720 Player::draw(DrawingContext& context)
721 {
722   TuxBodyParts* tux_body;
723           
724   if (size == SMALL)
725     tux_body = small_tux;
726   else if (got_power == FIRE_POWER)
727     tux_body = fire_tux;
728   else if (got_power == ICE_POWER)
729     tux_body = ice_tux;
730   else
731     tux_body = big_tux;
732
733   int layer = LAYER_OBJECTS + 10;
734
735   /* Set Tux sprite action */
736   if (duck && size == BIG)
737     {
738     if(dir == LEFT)
739       tux_body->set_action("duck-left");
740     else // dir == RIGHT
741       tux_body->set_action("duck-right");
742     }
743   else if (skidding_timer.started() && !skidding_timer.check())
744     {
745     if(dir == LEFT)
746       tux_body->set_action("skid-left");
747     else // dir == RIGHT
748       tux_body->set_action("skid-right");
749     }
750   else if (kick_timer.started() && !kick_timer.check())
751     {
752     if(dir == LEFT)
753       tux_body->set_action("kick-left");
754     else // dir == RIGHT
755       tux_body->set_action("kick-right");
756     }
757   else if (butt_jump && size == BIG)
758     {
759     if(dir == LEFT)
760       tux_body->set_action("buttjump-left");
761     else // dir == RIGHT
762       tux_body->set_action("buttjump-right");
763     }
764   else if (physic.get_velocity_y() != 0)
765     {
766     if(dir == LEFT)
767       tux_body->set_action("jump-left");
768     else // dir == RIGHT
769       tux_body->set_action("jump-right");
770     }
771   else
772     {
773     if (fabsf(physic.get_velocity_x()) < 1.0f) // standing
774       {
775       if(dir == LEFT)
776         tux_body->set_action("stand-left");
777       else // dir == RIGHT
778         tux_body->set_action("stand-right");
779       }
780     else // moving
781       {
782       if(dir == LEFT)
783         tux_body->set_action("walk-left");
784       else // dir == RIGHT
785         tux_body->set_action("walk-right");
786       }
787     }
788
789   if(idle_timer.check())
790     {
791     if(size == BIG)
792       {
793       if(dir == LEFT)
794         tux_body->head->set_action("idle-left", 1);
795       else // dir == RIGHT
796         tux_body->head->set_action("idle-right", 1);
797       }
798
799     }
800
801   // Tux is holding something
802   if ((holding_something && physic.get_velocity_y() == 0) ||
803       (shooting_timer.get_timeleft() > 0 && !shooting_timer.check()))
804     {
805     if (duck)
806       {
807       if(dir == LEFT)
808         tux_body->arms->set_action("duck+grab-left");
809       else // dir == RIGHT
810         tux_body->arms->set_action("duck+grab-right");
811       }
812     else
813       {
814       if(dir == LEFT)
815         tux_body->arms->set_action("grab-left");
816       else // dir == RIGHT
817         tux_body->arms->set_action("grab-right");
818       }
819     }
820
821   /* Draw Tux */
822   if (dying == DYING_SQUISHED) {
823     smalltux_gameover->draw(context, get_pos(), LAYER_FOREGROUNDTILES+1);
824   } else if(growing_timer.get_timeleft() > 0) {
825     if(size == SMALL)
826       {
827       if (dir == RIGHT)
828         context.draw_surface(growingtux_right[GROWING_FRAMES-1 - 
829                  int((growing_timer.get_timegone() *
830                  GROWING_FRAMES) / GROWING_TIME)], get_pos(), layer);
831       else
832         context.draw_surface(growingtux_left[GROWING_FRAMES-1 - 
833                 int((growing_timer.get_timegone() *
834                 GROWING_FRAMES) / GROWING_TIME)], get_pos(), layer);
835       }
836     else
837       {
838       if (dir == RIGHT)
839         context.draw_surface(growingtux_right[
840             int((growing_timer.get_timegone() *
841                 GROWING_FRAMES) / GROWING_TIME)], get_pos(), layer);
842       else
843         context.draw_surface(growingtux_left[
844             int((growing_timer.get_timegone() *
845                              GROWING_FRAMES) / GROWING_TIME)],
846             get_pos(), layer);
847       }
848     }
849   else if (safe_timer.started() && size_t(global_time*40)%2)
850     ;  // don't draw Tux
851   else
852     tux_body->draw(context, get_pos(), layer);
853
854   // Draw blinking star overlay
855   if (invincible_timer.started() &&
856      (invincible_timer.get_timeleft() > TUX_INVINCIBLE_TIME_WARNING
857       || size_t(global_time*20)%2)
858      && !dying)
859   {
860     if (size == SMALL || duck)
861       smalltux_star->draw(context, get_pos(), layer + 5);
862     else
863       bigtux_star->draw(context, get_pos(), layer + 5);
864   }
865  
866   if (debug_mode)
867     context.draw_filled_rect(get_pos(),
868         Vector(bbox.get_width(), bbox.get_height()),
869         Color(75,75,75, 150), LAYER_OBJECTS+20);
870 }
871
872 HitResponse
873 Player::collision(GameObject& other, const CollisionHit& hit)
874 {
875   if(other.get_flags() & FLAG_SOLID) {
876     if(hit.normal.y < 0) { // landed on floor?
877       if (physic.get_velocity_y() < 0)
878         physic.set_velocity_y(0);
879       on_ground_flag = true;
880     } else if(hit.normal.y > 0) { // bumped against the roof
881       physic.set_velocity_y(.1);
882     }
883     
884     if(fabsf(hit.normal.x) > .9) { // hit on the side?
885       physic.set_velocity_x(0);
886     }
887
888     return CONTINUE;
889   }
890
891   TriggerBase* trigger = dynamic_cast<TriggerBase*> (&other);
892   if(trigger) {
893     if(input.up == DOWN && input.old_up == UP)
894       trigger->event(*this, TriggerBase::EVENT_ACTIVATE);
895   }
896
897   return FORCE_MOVE;
898 }
899
900 void
901 Player::make_invincible()
902 {
903   SoundManager::get()->play_sound(IDToSound(SND_HERRING));
904   invincible_timer.start(TUX_INVINCIBLE_TIME);
905   Sector::current()->play_music(HERRING_MUSIC);               
906 }
907
908 /* Kill Player! */
909 void
910 Player::kill(HurtMode mode)
911 {
912   if(dying)
913     return;
914
915   if(safe_timer.get_timeleft() > 0 || invincible_timer.get_timeleft() > 0)
916     return;                          
917   
918   SoundManager::get()->play_sound(IDToSound(SND_HURT));
919
920   physic.set_velocity_x(0);
921
922   if (mode == SHRINK && size == BIG)
923     {
924       if (got_power != NONE_POWER)
925         {
926           safe_timer.start(TUX_SAFE_TIME);
927           got_power = NONE_POWER;
928         }
929       else
930         {
931           growing_timer.start(GROWING_TIME);
932           safe_timer.start(TUX_SAFE_TIME + GROWING_TIME);
933           size = SMALL;
934           bbox.set_height(31.8);
935           duck = false;
936         }
937     }
938   else
939     {
940       physic.enable_gravity(true);
941       physic.set_acceleration(0, 0);
942       physic.set_velocity(0, 700);
943       --player_status.lives;
944       dying = DYING_SQUISHED;
945       dying_timer.start(3.0);
946       flags |= FLAG_NO_COLLDET;
947     }
948 }
949
950 /* Remove Tux's power ups */
951 void
952 Player::remove_powerups()
953 {
954   got_power = NONE_POWER;
955   size = SMALL;
956   bbox.set_height(31.8);
957 }
958
959 void
960 Player::move(const Vector& vector)
961 {
962   bbox.set_pos(vector);
963 }
964
965 void
966 Player::check_bounds(Camera* camera)
967 {
968   /* Keep tux in bounds: */
969   if (get_pos().x < 0)
970     { // Lock Tux to the size of the level, so that he doesn't fall of
971       // on the left side
972       bbox.set_pos(Vector(0, get_pos().y));
973     }
974
975   /* Keep in-bounds, vertically: */
976   if (get_pos().y > Sector::current()->solids->get_height() * 32)
977     {
978       kill(KILL);
979       return;
980     }
981
982   bool adjust = false;
983   // can happen if back scrolling is disabled
984   if(get_pos().x < camera->get_translation().x) {
985     bbox.set_pos(Vector(camera->get_translation().x, get_pos().y));
986     adjust = true;
987   }
988   if(get_pos().x >= camera->get_translation().x + screen->w - bbox.get_width())
989   {
990     bbox.set_pos(Vector(
991           camera->get_translation().x + screen->w - bbox.get_width(),
992           get_pos().y));
993     adjust = true;
994   }
995
996   if(adjust) {
997     // FIXME
998 #if 0
999     // squished now?
1000     if(collision_object_map(bbox)) {
1001       kill(KILL);
1002       return;
1003     }
1004 #endif
1005   }
1006 }
1007
1008 void
1009 Player::bounce(BadGuy& )
1010 {
1011   //Make sure we stopped flapping
1012   flapping = false;
1013   falling_from_flap = false;
1014   
1015   if (input.jump)
1016     physic.set_velocity_y(520);
1017   else
1018     physic.set_velocity_y(200);
1019 }
1020