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