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