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