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