fixed broken 1-time animations in sprites, fixed collision code returning no-collisio...
[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 #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 "tilemap.h"
35 #include "camera.h"
36 #include "gameobjs.h"
37 #include "resources.h"
38 #include "video/screen.h"
39 #include "statistics.h"
40 #include "gameloop.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           physic.get_velocity_x(), dir))
666       shooting_timer.start(SHOOTING_TIME);
667     input.old_fire = DOWN;
668   }
669
670   /* Duck! */
671   if (input.down == DOWN && size == BIG && !duck 
672       && physic.get_velocity_y() == 0 && on_ground())
673     {
674       duck = true;
675       bbox.move(Vector(0, 32));
676       bbox.set_height(31.8);
677     }
678   else if(input.down == UP && size == BIG && duck)
679     {
680       // try if we can really unduck
681       bbox.move(Vector(0, -32));
682       bbox.set_height(63.8);
683       duck = false;
684       // FIXME
685 #if 0
686       // when unducking in air we need some space to do so
687       if(on_ground() || !collision_object_map(bbox)) {
688         duck = false;
689       } else {
690         // undo the ducking changes
691         bbox.move(Vector(0, 32));
692         bbox.set_height(31.8);
693       }
694 #endif
695     }
696 }
697
698 void
699 Player::grow(bool animate)
700 {
701   if(size == BIG)
702     return;
703   
704   size = BIG;
705   bbox.set_height(63.8);
706   bbox.move(Vector(0, -32));
707
708   if(animate)
709     growing_timer.start(GROWING_TIME);
710 }
711
712 void
713 Player::grabdistros()
714 {
715 }
716
717 void
718 Player::draw(DrawingContext& context)
719 {
720   TuxBodyParts* tux_body;
721           
722   if (size == SMALL)
723     tux_body = small_tux;
724   else if (got_power == FIRE_POWER)
725     tux_body = fire_tux;
726   else if (got_power == ICE_POWER)
727     tux_body = ice_tux;
728   else
729     tux_body = big_tux;
730
731   int layer = LAYER_OBJECTS + 10;
732
733   /* Set Tux sprite action */
734   if (duck && size == BIG)
735     {
736     if(dir == LEFT)
737       tux_body->set_action("duck-left");
738     else // dir == RIGHT
739       tux_body->set_action("duck-right");
740     }
741   else if (skidding_timer.started() && !skidding_timer.check())
742     {
743     if(dir == LEFT)
744       tux_body->set_action("skid-left");
745     else // dir == RIGHT
746       tux_body->set_action("skid-right");
747     }
748   else if (kick_timer.started() && !kick_timer.check())
749     {
750     if(dir == LEFT)
751       tux_body->set_action("kick-left");
752     else // dir == RIGHT
753       tux_body->set_action("kick-right");
754     }
755   else if (butt_jump && size == BIG)
756     {
757     if(dir == LEFT)
758       tux_body->set_action("buttjump-left");
759     else // dir == RIGHT
760       tux_body->set_action("buttjump-right");
761     }
762   else if (physic.get_velocity_y() != 0)
763     {
764     if(dir == LEFT)
765       tux_body->set_action("jump-left");
766     else // dir == RIGHT
767       tux_body->set_action("jump-right");
768     }
769   else
770     {
771     if (fabsf(physic.get_velocity_x()) < 1.0f) // standing
772       {
773       if(dir == LEFT)
774         tux_body->set_action("stand-left");
775       else // dir == RIGHT
776         tux_body->set_action("stand-right");
777       }
778     else // moving
779       {
780       if(dir == LEFT)
781         tux_body->set_action("walk-left");
782       else // dir == RIGHT
783         tux_body->set_action("walk-right");
784       }
785     }
786
787   if(idle_timer.check())
788     {
789     if(size == BIG)
790       {
791       if(dir == LEFT)
792         tux_body->head->set_action("idle-left", 1);
793       else // dir == RIGHT
794         tux_body->head->set_action("idle-right", 1);
795       }
796
797     }
798
799   // Tux is holding something
800   if ((holding_something && physic.get_velocity_y() == 0) ||
801       (shooting_timer.get_timeleft() > 0 && !shooting_timer.check()))
802     {
803     if (duck)
804       {
805       if(dir == LEFT)
806         tux_body->arms->set_action("duck+grab-left");
807       else // dir == RIGHT
808         tux_body->arms->set_action("duck+grab-right");
809       }
810     else
811       {
812       if(dir == LEFT)
813         tux_body->arms->set_action("grab-left");
814       else // dir == RIGHT
815         tux_body->arms->set_action("grab-right");
816       }
817     }
818
819   /* Draw Tux */
820   if (dying == DYING_SQUISHED) {
821     smalltux_gameover->draw(context, get_pos(), LAYER_FOREGROUNDTILES+1);
822   } else if(growing_timer.get_timeleft() > 0) {
823     if(size == SMALL)
824       {
825       if (dir == RIGHT)
826         context.draw_surface(growingtux_right[GROWING_FRAMES-1 - 
827                  int((growing_timer.get_timegone() *
828                  GROWING_FRAMES) / GROWING_TIME)], get_pos(), layer);
829       else
830         context.draw_surface(growingtux_left[GROWING_FRAMES-1 - 
831                 int((growing_timer.get_timegone() *
832                 GROWING_FRAMES) / GROWING_TIME)], get_pos(), layer);
833       }
834     else
835       {
836       if (dir == RIGHT)
837         context.draw_surface(growingtux_right[
838             int((growing_timer.get_timegone() *
839                 GROWING_FRAMES) / GROWING_TIME)], get_pos(), layer);
840       else
841         context.draw_surface(growingtux_left[
842             int((growing_timer.get_timegone() *
843                              GROWING_FRAMES) / GROWING_TIME)],
844             get_pos(), layer);
845       }
846     }
847   else if (safe_timer.started() && size_t(global_time*40)%2)
848     ;  // don't draw Tux
849   else
850     tux_body->draw(context, get_pos(), layer);
851
852   // Draw blinking star overlay
853   if (invincible_timer.started() &&
854      (invincible_timer.get_timeleft() > TUX_INVINCIBLE_TIME_WARNING
855       || size_t(global_time*20)%2)
856      && !dying)
857   {
858     if (size == SMALL || duck)
859       smalltux_star->draw(context, get_pos(), layer + 5);
860     else
861       bigtux_star->draw(context, get_pos(), layer + 5);
862   }
863  
864   if (debug_mode)
865     context.draw_filled_rect(get_pos(),
866         Vector(bbox.get_width(), bbox.get_height()),
867         Color(75,75,75, 150), LAYER_OBJECTS+20);
868 }
869
870 HitResponse
871 Player::collision(GameObject& other, const CollisionHit& hit)
872 {
873   if(other.get_flags() & FLAG_SOLID) {
874     if(hit.normal.y < 0) { // landed on floor?
875       if (physic.get_velocity_y() < 0)
876         physic.set_velocity_y(0);
877       on_ground_flag = true;
878     } else if(hit.normal.y > 0) { // bumped against the roof
879       physic.set_velocity_y(.1);
880     }
881     
882     if(fabsf(hit.normal.x) > .9) { // hit on the side?
883       physic.set_velocity_x(0);
884     }
885
886     return CONTINUE;
887   }
888
889   TriggerBase* trigger = dynamic_cast<TriggerBase*> (&other);
890   if(trigger) {
891     if(input.up == DOWN && input.old_up == UP)
892       trigger->event(*this, TriggerBase::EVENT_ACTIVATE);
893   }
894
895   return FORCE_MOVE;
896 }
897
898 void
899 Player::make_invincible()
900 {
901   SoundManager::get()->play_sound(IDToSound(SND_HERRING));
902   invincible_timer.start(TUX_INVINCIBLE_TIME);
903   Sector::current()->play_music(HERRING_MUSIC);               
904 }
905
906 /* Kill Player! */
907 void
908 Player::kill(HurtMode mode)
909 {
910   if(dying)
911     return;
912
913   if(safe_timer.get_timeleft() > 0 || invincible_timer.get_timeleft() > 0)
914     return;                          
915   
916   SoundManager::get()->play_sound(IDToSound(SND_HURT));
917
918   physic.set_velocity_x(0);
919
920   if (mode == SHRINK && size == BIG)
921     {
922       if (got_power != NONE_POWER)
923         {
924           safe_timer.start(TUX_SAFE_TIME);
925           got_power = NONE_POWER;
926         }
927       else
928         {
929           growing_timer.start(GROWING_TIME);
930           safe_timer.start(TUX_SAFE_TIME + GROWING_TIME);
931           size = SMALL;
932           bbox.set_height(31.8);
933           duck = false;
934         }
935     }
936   else
937     {
938       physic.enable_gravity(true);
939       physic.set_acceleration(0, 0);
940       physic.set_velocity(0, 700);
941       --player_status.lives;
942       dying = DYING_SQUISHED;
943       dying_timer.start(3.0);
944       flags |= FLAG_NO_COLLDET;
945     }
946 }
947
948 /* Remove Tux's power ups */
949 void
950 Player::remove_powerups()
951 {
952   got_power = NONE_POWER;
953   size = SMALL;
954   bbox.set_height(31.8);
955 }
956
957 void
958 Player::move(const Vector& vector)
959 {
960   bbox.set_pos(vector);
961 }
962
963 void
964 Player::check_bounds(Camera* camera)
965 {
966   /* Keep tux in bounds: */
967   if (get_pos().x < 0)
968     { // Lock Tux to the size of the level, so that he doesn't fall of
969       // on the left side
970       bbox.set_pos(Vector(0, get_pos().y));
971     }
972
973   /* Keep in-bounds, vertically: */
974   if (get_pos().y > Sector::current()->solids->get_height() * 32)
975     {
976       kill(KILL);
977       return;
978     }
979
980   bool adjust = false;
981   // can happen if back scrolling is disabled
982   if(get_pos().x < camera->get_translation().x) {
983     bbox.set_pos(Vector(camera->get_translation().x, get_pos().y));
984     adjust = true;
985   }
986   if(get_pos().x >= camera->get_translation().x + screen->w - bbox.get_width())
987   {
988     bbox.set_pos(Vector(
989           camera->get_translation().x + screen->w - bbox.get_width(),
990           get_pos().y));
991     adjust = true;
992   }
993
994   if(adjust) {
995     // FIXME
996 #if 0
997     // squished now?
998     if(collision_object_map(bbox)) {
999       kill(KILL);
1000       return;
1001     }
1002 #endif
1003   }
1004 }
1005
1006 void
1007 Player::bounce(BadGuy& )
1008 {
1009   //Make sure we stopped flapping
1010   flapping = false;
1011   falling_from_flap = false;
1012   
1013   if (input.jump)
1014     physic.set_velocity_y(520);
1015   else
1016     physic.set_velocity_y(200);
1017 }
1018