added some more non-45 degree triangle modes
[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(32, 32);
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   physic.set_velocity(vx, vy);
392   physic.set_acceleration(ax, ay);
393 }
394
395 void
396 Player::handle_vertical_input()
397 {
398   // set fall mode...
399   if(on_ground()) {
400     fall_mode = ON_GROUND;
401     last_ground_y = get_pos().y;
402   } else {
403     if(get_pos().y > last_ground_y)
404       fall_mode = FALLING;
405     else if(fall_mode == ON_GROUND)
406       fall_mode = JUMPING;
407   }
408
409   if(on_ground()) { /* Make sure jumping is off. */
410     jumping = false;
411     flapping = false;
412     falling_from_flap = false;
413     if (flapping_timer.started()) {
414       flapping_timer.start(0);
415     }
416
417     physic.set_acceleration_y(0); //for flapping
418   }
419
420   // Press jump key
421   if(input.jump == DOWN && can_jump && on_ground())
422     {
423       if(duck) { // only jump a little bit when in duck mode {
424         physic.set_velocity_y(300);
425       } else {
426         // jump higher if we are running
427         if (fabs(physic.get_velocity_x()) > MAX_WALK_XM)
428           physic.set_velocity_y(580);
429         else
430           physic.set_velocity_y(520);
431       }
432
433       //bbox.move(Vector(0, -1));
434       jumping = true;
435       flapping = false;
436       can_jump = false;
437       can_flap = false;
438       flaps_nb = 0; // Ricardo's flapping
439       if (size == SMALL)
440         SoundManager::get()->play_sound(IDToSound(SND_JUMP));
441       else
442         SoundManager::get()->play_sound(IDToSound(SND_BIGJUMP));
443     }
444   // Let go of jump key
445   else if(input.jump == UP)
446     {
447       if (!flapping && !duck && !falling_from_flap && !on_ground())
448          {
449             can_flap = true;
450          }
451       if (jumping && physic.get_velocity_y() > 0)
452          {
453             jumping = false;
454             physic.set_velocity_y(0);
455          }
456     }
457
458  // temporary to help player's choosing a flapping
459  if(flapping_mode == RICARDO_FLAP)
460    {
461    // Flapping, Ricardo's version
462    // similar to SM3 Fox
463    if(input.jump == DOWN && input.old_jump == UP && can_flap &&
464      flaps_nb < 3)
465      {
466        physic.set_velocity_y(350);
467        physic.set_velocity_x(physic.get_velocity_x() * 35);
468        flaps_nb++;
469      }
470    }
471   else if(flapping_mode == MAREK_FLAP)
472    {
473    // Flapping, Marek's version
474    if (input.jump == DOWN && can_flap)
475      {
476          if (!flapping_timer.started())
477             {
478                flapping_timer.start(TUX_FLAPPING_TIME);
479                flapping_velocity = physic.get_velocity_x();
480             }
481          if (flapping_timer.check()) 
482             {
483                can_flap = false;
484                falling_from_flap = true;
485             }
486          jumping = true;
487          flapping = true;
488          if (!flapping_timer.check()) {
489            float cv = flapping_velocity * sqrt(
490                TUX_FLAPPING_TIME - flapping_timer.get_timegone() 
491                / TUX_FLAPPING_TIME);
492
493            //Handle change of direction while flapping
494            if (((dir == LEFT) && (cv > 0)) || (dir == RIGHT) && (cv < 0)) {
495              cv *= (-1);
496            }
497            physic.set_velocity_x(cv);
498            physic.set_velocity_y(
499                flapping_timer.get_timegone()/.850);
500          }
501      }
502    }
503   else if(flapping_mode == RYAN_FLAP)
504    {
505    // Flapping, Ryan's version
506    if (input.jump == DOWN && can_flap)
507      {
508          if (!flapping_timer.started())
509             {
510                flapping_timer.start(TUX_FLAPPING_TIME);
511             }
512          if (flapping_timer.check()) 
513             {
514                can_flap = false;
515                falling_from_flap = true;
516             }
517          jumping = true;
518          flapping = true;
519          if (flapping && flapping_timer.get_timegone() <= TUX_FLAPPING_TIME
520                 && physic.get_velocity_y() < 0)
521             {
522                float gravity = Sector::current()->gravity;
523                (void)gravity;
524                float xr = (fabsf(physic.get_velocity_x()) / MAX_RUN_XM);
525
526                // XXX: magic numbers. should be a percent of gravity
527                //      gravity is (by default) -0.1f
528                physic.set_acceleration_y(12 + 1*xr);
529
530 #if 0
531                // To slow down x-vel when flapping (not working)
532                if (fabsf(physic.get_velocity_x()) > MAX_WALK_XM)
533                {
534                    if (physic.get_velocity_x() < 0)
535                        physic.set_acceleration_x(1.0f);
536                    else if (physic.get_velocity_x() > 0)
537                        physic.set_acceleration_x(-1.0f);
538                }
539 #endif
540             }
541      }
542     else
543      {
544         physic.set_acceleration_y(0);
545      }
546    }
547
548    // Hover
549    //(disabled by default, use cheat code "hover" to toggle on/off)
550    //TODO: needs some tweaking, especially when used together with double jump and jumping off badguys
551    if (enable_hover && input.jump == DOWN && !jumping && !butt_jump && physic.get_velocity_y() <= 0)
552       {
553          physic.set_velocity_y(-100);
554       }
555
556 #if 0
557    /* In case the player has pressed Down while in a certain range of air,
558       enable butt jump action */
559   if (input.down == DOWN && !butt_jump && !duck)
560     if(tiles_on_air(TILES_FOR_BUTTJUMP) && jumping)
561       butt_jump = true;
562 #endif
563
564    /* When Down is not held anymore, disable butt jump */
565   if(butt_jump && input.down == UP)
566     butt_jump = false;
567
568   // Do butt jump
569   if (butt_jump && on_ground() && size == BIG)
570   {
571     // Add a smoke cloud
572     if (duck) 
573       Sector::current()->add_smoke_cloud(Vector(get_pos().x - 32, get_pos().y));
574     else 
575       Sector::current()->add_smoke_cloud(
576           Vector(get_pos().x - 32, get_pos().y + 32));
577     
578     butt_jump = false;
579
580 #if 0
581     // Break bricks beneath Tux
582     if(Sector::current()->trybreakbrick(
583           Vector(base.x + 1, base.y + base.height), false)
584         || Sector::current()->trybreakbrick(
585            Vector(base.x + base.width - 1, base.y + base.height), false))
586     {
587       physic.set_velocity_y(2);
588       butt_jump = true;
589     }
590 #endif
591
592 #if 0
593     // Kill nearby badguys
594     std::vector<GameObject*> gameobjects = Sector::current()->gameobjects;
595     for (std::vector<GameObject*>::iterator i = gameobjects.begin();
596          i != gameobjects.end();
597          i++)
598     {
599       BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
600       if(badguy)
601       {
602         // don't kill when badguys are already dying or in a certain mode
603         if(badguy->dying == DYING_NOT && badguy->mode != BadGuy::BOMB_TICKING &&
604            badguy->mode != BadGuy::BOMB_EXPLODE)
605           {
606             if (fabsf(base.x - badguy->base.x) < 96 &&
607                 fabsf(base.y - badguy->base.y) < 64)
608               badguy->kill_me(25);
609           }
610       }
611     }
612 #endif
613   }
614
615   /** jumping is only allowed if we're about to touch ground soon and if the
616    * button has been up in between the last jump
617    */
618   // FIXME
619 #if 0
620   if ( (issolid(get_pos().x + bbox.get_width() / 2,
621           get_pos().y + bbox.get_height() + 64) ||
622         issolid(get_pos().x + 1, get_pos().y + bbox.get_height() + 64) ||
623         issolid(get_pos().x + bbox.get_width() - 1,
624           get_pos().y + bbox.get_height() + 64))
625        && jumping  == false
626        && can_jump == false
627        && input.jump == DOWN
628        && input.old_jump == UP)
629     {
630       can_jump = true;
631     }
632 #endif
633
634   input.old_jump = input.jump;
635 }
636
637 void
638 Player::handle_input()
639 {
640   /* Handle horizontal movement: */
641   handle_horizontal_input();
642
643   /* Jump/jumping? */
644   if (on_ground() && input.jump == UP)
645     can_jump = true;
646   handle_vertical_input();
647
648   /* Shoot! */
649   if (input.fire == DOWN && input.old_fire == UP && got_power != NONE_POWER) {
650     if(Sector::current()->add_bullet(
651           get_pos() + Vector(0, bbox.get_height()/2),
652           physic.get_velocity_x(), dir))
653       shooting_timer.start(SHOOTING_TIME);
654     input.old_fire = DOWN;
655   }
656
657   /* Duck! */
658   if (input.down == DOWN && size == BIG && !duck 
659       && physic.get_velocity_y() == 0 && on_ground())
660     {
661       duck = true;
662       bbox.move(Vector(0, 32));
663       bbox.set_height(32);
664     }
665   else if(input.down == UP && size == BIG && duck)
666     {
667       // try if we can really unduck
668       bbox.move(Vector(0, -32));
669       bbox.set_height(64);
670       duck = false;
671       // FIXME
672 #if 0
673       // when unducking in air we need some space to do so
674       if(on_ground() || !collision_object_map(bbox)) {
675         duck = false;
676       } else {
677         // undo the ducking changes
678         bbox.move(Vector(0, 32));
679         bbox.set_height(32);
680       }
681 #endif
682     }
683 }
684
685 void
686 Player::grow(bool animate)
687 {
688   if(size == BIG)
689     return;
690   
691   size = BIG;
692   bbox.set_height(64);
693   bbox.move(Vector(0, -32));
694
695   if(animate)
696     growing_timer.start(GROWING_TIME);
697 }
698
699 void
700 Player::grabdistros()
701 {
702 }
703
704 void
705 Player::draw(DrawingContext& context)
706 {
707   TuxBodyParts* tux_body;
708           
709   if (size == SMALL)
710     tux_body = small_tux;
711   else if (got_power == FIRE_POWER)
712     tux_body = fire_tux;
713   else if (got_power == ICE_POWER)
714     tux_body = ice_tux;
715   else
716     tux_body = big_tux;
717
718   int layer = LAYER_OBJECTS + 10;
719
720   /* Set Tux sprite action */
721   if (duck && size == BIG)
722     {
723     if(dir == LEFT)
724       tux_body->set_action("duck-left");
725     else // dir == RIGHT
726       tux_body->set_action("duck-right");
727     }
728   else if (skidding_timer.started() && !skidding_timer.check())
729     {
730     if(dir == LEFT)
731       tux_body->set_action("skid-left");
732     else // dir == RIGHT
733       tux_body->set_action("skid-right");
734     }
735   else if (kick_timer.started() && !kick_timer.check())
736     {
737     if(dir == LEFT)
738       tux_body->set_action("kick-left");
739     else // dir == RIGHT
740       tux_body->set_action("kick-right");
741     }
742   else if (butt_jump && size == BIG)
743     {
744     if(dir == LEFT)
745       tux_body->set_action("buttjump-left");
746     else // dir == RIGHT
747       tux_body->set_action("buttjump-right");
748     }
749   else if (physic.get_velocity_y() != 0)
750     {
751     if(dir == LEFT)
752       tux_body->set_action("jump-left");
753     else // dir == RIGHT
754       tux_body->set_action("jump-right");
755     }
756   else
757     {
758     if (fabsf(physic.get_velocity_x()) < 1.0f) // standing
759       {
760       if(dir == LEFT)
761         tux_body->set_action("stand-left");
762       else // dir == RIGHT
763         tux_body->set_action("stand-right");
764       }
765     else // moving
766       {
767       if(dir == LEFT)
768         tux_body->set_action("walk-left");
769       else // dir == RIGHT
770         tux_body->set_action("walk-right");
771       }
772     }
773
774   if(idle_timer.check())
775     {
776     if(size == BIG)
777       {
778       if(dir == LEFT)
779         tux_body->head->set_action("idle-left", 1);
780       else // dir == RIGHT
781         tux_body->head->set_action("idle-right", 1);
782       }
783
784     }
785
786   // Tux is holding something
787   if ((holding_something && physic.get_velocity_y() == 0) ||
788       (shooting_timer.get_timeleft() > 0 && !shooting_timer.check()))
789     {
790     if (duck)
791       {
792       if(dir == LEFT)
793         tux_body->arms->set_action("duck+grab-left");
794       else // dir == RIGHT
795         tux_body->arms->set_action("duck+grab-right");
796       }
797     else
798       {
799       if(dir == LEFT)
800         tux_body->arms->set_action("grab-left");
801       else // dir == RIGHT
802         tux_body->arms->set_action("grab-right");
803       }
804     }
805
806   /* Draw Tux */
807   if (dying == DYING_SQUISHED) {
808     smalltux_gameover->draw(context, get_pos(), LAYER_FOREGROUNDTILES+1);
809   } else if(growing_timer.get_timeleft() > 0) {
810     if(size == SMALL)
811       {
812       if (dir == RIGHT)
813         context.draw_surface(growingtux_right[GROWING_FRAMES-1 - 
814                  int((growing_timer.get_timegone() *
815                  GROWING_FRAMES) / GROWING_TIME)], get_pos(), layer);
816       else
817         context.draw_surface(growingtux_left[GROWING_FRAMES-1 - 
818                 int((growing_timer.get_timegone() *
819                 GROWING_FRAMES) / GROWING_TIME)], get_pos(), layer);
820       }
821     else
822       {
823       if (dir == RIGHT)
824         context.draw_surface(growingtux_right[
825             int((growing_timer.get_timegone() *
826                 GROWING_FRAMES) / GROWING_TIME)], get_pos(), layer);
827       else
828         context.draw_surface(growingtux_left[
829             int((growing_timer.get_timegone() *
830                              GROWING_FRAMES) / GROWING_TIME)],
831             get_pos(), layer);
832       }
833     }
834   else if (safe_timer.started() && size_t(global_time*40)%2)
835     ;  // don't draw Tux
836   else
837     tux_body->draw(context, get_pos(), layer);
838
839   // Draw blinking star overlay
840   if (invincible_timer.started() &&
841      (invincible_timer.get_timeleft() > TUX_INVINCIBLE_TIME_WARNING
842       || size_t(global_time*20)%2)
843      && !dying)
844   {
845     if (size == SMALL || duck)
846       smalltux_star->draw(context, get_pos(), layer + 5);
847     else
848       bigtux_star->draw(context, get_pos(), layer + 5);
849   }
850  
851   if (debug_mode)
852     context.draw_filled_rect(get_pos(),
853         Vector(bbox.get_width(), bbox.get_height()),
854         Color(75,75,75, 150), LAYER_OBJECTS+20);
855 }
856
857 HitResponse
858 Player::collision(GameObject& other, const CollisionHit& hit)
859 {
860   if(other.get_flags() & FLAG_SOLID) {
861     if(hit.normal.y < 0) { // landed on floor?
862       if (physic.get_velocity_y() < 0)
863         physic.set_velocity_y(0);
864       on_ground_flag = true;
865     } else if(hit.normal.y > 0) { // bumped against the roof
866       physic.set_velocity_y(.1);
867     }
868     
869     if(fabsf(hit.normal.x) > .9) { // hit on the side?
870       physic.set_velocity_x(0);
871     }
872
873     return CONTINUE;
874   }
875
876   TriggerBase* trigger = dynamic_cast<TriggerBase*> (&other);
877   if(trigger) {
878     if(input.up == DOWN && input.old_up == UP)
879       trigger->event(*this, TriggerBase::EVENT_ACTIVATE);
880   }
881
882   return FORCE_MOVE;
883 }
884
885 void
886 Player::make_invincible()
887 {
888   SoundManager::get()->play_sound(IDToSound(SND_HERRING));
889   invincible_timer.start(TUX_INVINCIBLE_TIME);
890   Sector::current()->play_music(HERRING_MUSIC);               
891 }
892
893 /* Kill Player! */
894 void
895 Player::kill(HurtMode mode)
896 {
897   if(dying)
898     return;
899
900   if(safe_timer.get_timeleft() > 0 || invincible_timer.get_timeleft() > 0)
901     return;                          
902   
903   SoundManager::get()->play_sound(IDToSound(SND_HURT));
904
905   physic.set_velocity_x(0);
906
907   if (mode == SHRINK && size == BIG)
908     {
909       if (got_power != NONE_POWER)
910         {
911           safe_timer.start(TUX_SAFE_TIME);
912           got_power = NONE_POWER;
913         }
914       else
915         {
916           growing_timer.start(GROWING_TIME);
917           safe_timer.start(TUX_SAFE_TIME + GROWING_TIME);
918           size = SMALL;
919           bbox.set_height(32);
920           duck = false;
921         }
922     }
923   else
924     {
925       physic.enable_gravity(true);
926       physic.set_acceleration(0, 0);
927       physic.set_velocity(0, 700);
928       --player_status.lives;
929       dying = DYING_SQUISHED;
930       dying_timer.start(3.0);
931       flags |= FLAG_NO_COLLDET;
932     }
933 }
934
935 /* Remove Tux's power ups */
936 void
937 Player::remove_powerups()
938 {
939   got_power = NONE_POWER;
940   size = SMALL;
941   bbox.set_height(32);
942 }
943
944 void
945 Player::move(const Vector& vector)
946 {
947   bbox.set_pos(vector);
948 }
949
950 void
951 Player::check_bounds(Camera* camera)
952 {
953   /* Keep tux in bounds: */
954   if (get_pos().x < 0)
955     { // Lock Tux to the size of the level, so that he doesn't fall of
956       // on the left side
957       bbox.set_pos(Vector(0, get_pos().y));
958     }
959
960   /* Keep in-bounds, vertically: */
961   if (get_pos().y > Sector::current()->solids->get_height() * 32)
962     {
963       kill(KILL);
964       return;
965     }
966
967   bool adjust = false;
968   // can happen if back scrolling is disabled
969   if(get_pos().x < camera->get_translation().x) {
970     bbox.set_pos(Vector(camera->get_translation().x, get_pos().y));
971     adjust = true;
972   }
973   if(get_pos().x >= camera->get_translation().x + screen->w - bbox.get_width())
974   {
975     bbox.set_pos(Vector(
976           camera->get_translation().x + screen->w - bbox.get_width(),
977           get_pos().y));
978     adjust = true;
979   }
980
981   if(adjust) {
982     // FIXME
983 #if 0
984     // squished now?
985     if(collision_object_map(bbox)) {
986       kill(KILL);
987       return;
988     }
989 #endif
990   }
991 }
992
993 void
994 Player::bounce(BadGuy& )
995 {
996   //Make sure we stopped flapping
997   flapping = false;
998   falling_from_flap = false;
999   
1000   if (input.jump)
1001     physic.set_velocity_y(520);
1002   else
1003     physic.set_velocity_y(200);
1004 }
1005