fa9d84a6619c06ce1c4c4a196c537667fae80eb3
[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_LALT;
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)
95 {
96   if(head != NULL)
97     head->set_action(action);
98   if(body != NULL)
99     body->set_action(action);
100   if(arms != NULL)
101     arms->set_action(action);
102   if(feet != NULL)
103     feet->set_action(action);
104 }
105
106 void
107 TuxBodyParts::one_time_animation()
108 {
109   if(head != NULL)
110     head->start_animation(1);
111   if(body != NULL)
112     body->start_animation(1);
113   if(arms != NULL)
114     arms->start_animation(1);
115   if(feet != NULL)
116     feet->start_animation(1);
117 }
118
119 void
120 TuxBodyParts::draw(DrawingContext& context, const Vector& pos, int layer,
121                   Uint32 drawing_effect)
122 {
123   if(head != NULL)
124     head->draw(context, pos, layer-1, drawing_effect);
125   if(body != NULL)
126     body->draw(context, pos, layer-3, drawing_effect);
127   if(arms != NULL)
128     arms->draw(context, pos, layer,   drawing_effect);
129   if(feet != NULL)
130     feet->draw(context, pos, layer-2, drawing_effect);
131 }
132
133 Player::Player()
134 {
135   init();
136 }
137
138 Player::~Player()
139 {
140 }
141
142 void
143 Player::init()
144 {
145   holding_something = false;
146
147   bbox.set_size(32, 32);
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   = DYING_NOT;
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
178   frame_main = 0;
179   frame_ = 0;
180
181   player_input_init(&input);
182
183   frame_timer.start(.025, true);
184
185   physic.reset();
186 }
187
188 int
189 Player::key_event(SDLKey key, int state)
190 {
191   idle_timer.start(IDLE_TIME, true);
192
193   if(key == keymap.right)
194     {
195       input.right = state;
196       return true;
197     }
198   else if(key == keymap.left)
199     {
200       input.left = state;
201       return true;
202     }
203   else if(key == keymap.up)
204     {
205       if(state == UP)
206         input.old_up = UP;
207       input.up = state;
208       /* Up key also opens activates stuff */
209       input.activate = state;
210       return true;
211     }
212   else if(key == keymap.down)
213     {
214       input.down = state;
215       return true;
216     }
217   else if(key == keymap.power)
218     {
219       if (state == UP)
220         input.old_fire = UP;
221       input.fire = state;
222
223       return true;
224     }
225   else if(key == keymap.jump)
226     {
227       if (state == UP)
228         input.old_jump = UP;
229       input.jump = state;
230       return true;
231     }
232   else
233     return false;
234 }
235
236 void
237 Player::level_begin()
238 {
239   move(Vector(100, 170));
240   duck = false;
241
242   dying = DYING_NOT;
243
244   player_input_init(&input);
245
246   on_ground_flag = false;
247
248   physic.reset();
249 }
250
251 PlayerStatus&
252 Player::get_status()
253 {
254   return player_status;
255 }
256
257 void
258 Player::action(float elapsed_time)
259 {
260   if(dying && dying_timer.check()) {
261     dead = true;
262     return;
263   }
264
265   if (input.fire == UP)
266     holding_something = false;
267
268   if(dying == DYING_NOT)
269     handle_input();
270
271   movement = physic.get_movement(elapsed_time);
272
273 #if 0
274       // special exception for cases where we're stuck under tiles after
275       // being ducked. In this case we drift out
276       if(!duck && on_ground() && old_base.x == base.x && old_base.y == base.y
277          && collision_object_map(base))
278         {
279           base.x += elapsed_time * WALK_SPEED * (dir ? 1: -1);
280           previous_base = old_base = base;
281         }
282
283       /* Reset score multiplier (for multi-hits): */
284       if (!invincible_timer.started())
285             {
286             if(player_status.score_multiplier > player_status.max_score_multiplier)
287               {
288               player_status.max_score_multiplier = player_status.score_multiplier;
289
290               // show a message
291               char str[124];
292               sprintf(str, _("New max combo: %d"), player_status.max_score_multiplier-1);
293               Sector::current()->add_floating_text(base, str);
294               }
295             player_status.score_multiplier = 1;
296             }
297         }
298
299     }
300 #endif
301
302   on_ground_flag = false;
303 }
304
305 bool
306 Player::on_ground()
307 {
308   return on_ground_flag;
309 }
310
311 void
312 Player::handle_horizontal_input()
313 {
314   float vx = physic.get_velocity_x();
315   float vy = physic.get_velocity_y();
316   float ax = physic.get_acceleration_x();
317   float ay = physic.get_acceleration_y();
318
319   float dirsign = 0;
320   if(input.left == DOWN && input.right == UP && (!duck || physic.get_velocity_y() != 0)) {
321       old_dir = dir;
322       dir = LEFT;
323       dirsign = -1;
324   } else if(input.left == UP && input.right == DOWN && (!duck || physic.get_velocity_y() != 0)) {
325       old_dir = dir;
326       dir = RIGHT;
327       dirsign = 1;
328   }
329
330   if (input.fire == UP) {
331       ax = dirsign * WALK_ACCELERATION_X;
332       // limit speed
333       if(vx >= MAX_WALK_XM && dirsign > 0) {
334         vx = MAX_WALK_XM;
335         ax = 0;
336       } else if(vx <= -MAX_WALK_XM && dirsign < 0) {
337         vx = -MAX_WALK_XM;
338         ax = 0;
339       }
340   } else {
341       ax = dirsign * RUN_ACCELERATION_X;
342       // limit speed
343       if(vx >= MAX_RUN_XM && dirsign > 0) {
344         vx = MAX_RUN_XM;
345         ax = 0;
346       } else if(vx <= -MAX_RUN_XM && dirsign < 0) {
347         vx = -MAX_RUN_XM;
348         ax = 0;
349       }
350   }
351
352   // we can reach WALK_SPEED without any acceleration
353   if(dirsign != 0 && fabs(vx) < WALK_SPEED) {
354     vx = dirsign * WALK_SPEED;
355   }
356
357   // changing directions?
358   if(on_ground() && ((vx < 0 && dirsign >0) || (vx>0 && dirsign<0)))
359     {
360       // let's skid!
361       if(fabs(vx)>SKID_XM && !skidding_timer.started())
362         {
363           skidding_timer.start(SKID_TIME);
364           SoundManager::get()->play_sound(IDToSound(SND_SKID));
365           // dust some partcles
366           Sector::current()->add_particles(
367               Vector(bbox.p1.x + (dir == RIGHT ? bbox.get_width() : 0),
368                 bbox.p2.y),
369               dir == RIGHT ? 270+20 : 90-40, dir == RIGHT ? 270+40 : 90-20,
370               Vector(280,-260), Vector(0,0.030), 3, Color(100,100,100), 3, 800,
371               LAYER_OBJECTS+1);
372
373           ax *= 2.5;
374         }
375       else
376         {
377           ax *= 2;
378         }
379     }
380
381   // we get slower when not pressing any keys
382   if(dirsign == 0) {
383       if(fabs(vx) < WALK_SPEED) {
384           vx = 0;
385           ax = 0;
386       } else if(vx < 0) {
387           ax = WALK_ACCELERATION_X * 1.5;
388       } else {
389           ax = WALK_ACCELERATION_X * -1.5;
390       }
391   }
392
393 #if 0
394   // if we're on ice slow down acceleration or deceleration
395   if (isice(base.x, base.y + base.height))
396   {
397     /* the acceleration/deceleration rate on ice is inversely proportional to
398      * the current velocity.
399      */
400
401     // increasing 1 will increase acceleration/deceleration rate
402     // decreasing 1 will decrease acceleration/deceleration rate
403     //  must stay above zero, though
404     if (ax != 0) ax *= 1 / fabs(vx);
405   }
406 #endif
407
408   physic.set_velocity(vx, vy);
409   physic.set_acceleration(ax, ay);
410 }
411
412 void
413 Player::handle_vertical_input()
414 {
415   // set fall mode...
416   if(on_ground()) {
417     fall_mode = ON_GROUND;
418     last_ground_y = get_pos().y;
419   } else {
420     if(get_pos().y > last_ground_y)
421       fall_mode = FALLING;
422     else if(fall_mode == ON_GROUND)
423       fall_mode = JUMPING;
424   }
425
426   // Press jump key
427   if(input.jump == DOWN && can_jump && on_ground())
428     {
429       if(duck) { // only jump a little bit when in duck mode {
430         physic.set_velocity_y(300);
431       } else {
432         // jump higher if we are running
433         if (fabs(physic.get_velocity_x()) > MAX_WALK_XM)
434           physic.set_velocity_y(580);
435         else
436           physic.set_velocity_y(520);
437       }
438
439       //bbox.move(Vector(0, -1));
440       jumping = true;
441       flapping = false;
442       can_jump = false;
443       can_flap = false;
444       flaps_nb = 0; // Ricardo's flapping
445       if (size == SMALL)
446         SoundManager::get()->play_sound(IDToSound(SND_JUMP));
447       else
448         SoundManager::get()->play_sound(IDToSound(SND_BIGJUMP));
449     }
450   // Let go of jump key
451   else if(input.jump == UP)
452     {
453       if (!flapping && !duck && !falling_from_flap && !on_ground())
454          {
455             can_flap = true;
456          }
457       if (jumping && physic.get_velocity_y() > 0)
458          {
459             printf("jumpend.\n");
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 == DOWN && input.old_jump == UP && can_flap &&
471      flaps_nb < 3)
472      {
473        physic.set_velocity_y(350);
474        physic.set_velocity_x(physic.get_velocity_x() * 35);
475        flaps_nb++;
476      }
477    }
478   else if(flapping_mode == MAREK_FLAP)
479    {
480    // Flapping, Marek's version
481    if (input.jump == DOWN && can_flap)
482      {
483          if (!flapping_timer.started())
484             {
485                flapping_timer.start(TUX_FLAPPING_TIME);
486                flapping_velocity = physic.get_velocity_x();
487             }
488          if (flapping_timer.check()) 
489             {
490                can_flap = false;
491                falling_from_flap = true;
492             }
493          jumping = true;
494          flapping = true;
495          if (!flapping_timer.check()) {
496            float cv = flapping_velocity * sqrt(
497                TUX_FLAPPING_TIME - flapping_timer.get_timegone() 
498                / TUX_FLAPPING_TIME);
499
500            //Handle change of direction while flapping
501            if (((dir == LEFT) && (cv > 0)) || (dir == RIGHT) && (cv < 0)) {
502              cv *= (-1);
503            }
504            physic.set_velocity_x(cv);
505            physic.set_velocity_y(
506                flapping_timer.get_timegone()/.850);
507          }
508      }
509    }
510   else if(flapping_mode == RYAN_FLAP)
511    {
512    // Flapping, Ryan's version
513    if (input.jump == DOWN && can_flap)
514      {
515          if (!flapping_timer.started())
516             {
517                flapping_timer.start(TUX_FLAPPING_TIME);
518             }
519          if (flapping_timer.check()) 
520             {
521                can_flap = false;
522                falling_from_flap = true;
523             }
524          jumping = true;
525          flapping = true;
526          if (flapping && flapping_timer.get_timegone() <= TUX_FLAPPING_TIME
527                 && physic.get_velocity_y() < 0)
528             {
529                float gravity = Sector::current()->gravity;
530                (void)gravity;
531                float xr = (fabsf(physic.get_velocity_x()) / MAX_RUN_XM);
532
533                // XXX: magic numbers. should be a percent of gravity
534                //      gravity is (by default) -0.1f
535                physic.set_acceleration_y(12 + 1*xr);
536
537 #if 0
538                // To slow down x-vel when flapping (not working)
539                if (fabsf(physic.get_velocity_x()) > MAX_WALK_XM)
540                {
541                    if (physic.get_velocity_x() < 0)
542                        physic.set_acceleration_x(1.0f);
543                    else if (physic.get_velocity_x() > 0)
544                        physic.set_acceleration_x(-1.0f);
545                }
546 #endif
547             }
548      }
549     else
550      {
551         physic.set_acceleration_y(0);
552      }
553    }
554
555    // Hover
556    //(disabled by default, use cheat code "hover" to toggle on/off)
557    //TODO: needs some tweaking, especially when used together with double jump and jumping off badguys
558    if (enable_hover && input.jump == DOWN && !jumping && !butt_jump && physic.get_velocity_y() <= 0)
559       {
560          physic.set_velocity_y(-100);
561       }
562
563 #if 0
564    /* In case the player has pressed Down while in a certain range of air,
565       enable butt jump action */
566   if (input.down == DOWN && !butt_jump && !duck)
567     if(tiles_on_air(TILES_FOR_BUTTJUMP) && jumping)
568       butt_jump = true;
569 #endif
570
571    /* When Down is not held anymore, disable butt jump */
572   if(butt_jump && input.down == UP)
573     butt_jump = false;
574
575   // Do butt jump
576   if (butt_jump && on_ground() && size == BIG)
577   {
578     // Add a smoke cloud
579     if (duck) 
580       Sector::current()->add_smoke_cloud(Vector(get_pos().x - 32, get_pos().y));
581     else 
582       Sector::current()->add_smoke_cloud(
583           Vector(get_pos().x - 32, get_pos().y + 32));
584     
585     butt_jump = false;
586
587 #if 0
588     // Break bricks beneath Tux
589     if(Sector::current()->trybreakbrick(
590           Vector(base.x + 1, base.y + base.height), false)
591         || Sector::current()->trybreakbrick(
592            Vector(base.x + base.width - 1, base.y + base.height), false))
593     {
594       physic.set_velocity_y(2);
595       butt_jump = true;
596     }
597 #endif
598
599 #if 0
600     // Kill nearby badguys
601     std::vector<GameObject*> gameobjects = Sector::current()->gameobjects;
602     for (std::vector<GameObject*>::iterator i = gameobjects.begin();
603          i != gameobjects.end();
604          i++)
605     {
606       BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
607       if(badguy)
608       {
609         // don't kill when badguys are already dying or in a certain mode
610         if(badguy->dying == DYING_NOT && badguy->mode != BadGuy::BOMB_TICKING &&
611            badguy->mode != BadGuy::BOMB_EXPLODE)
612           {
613             if (fabsf(base.x - badguy->base.x) < 96 &&
614                 fabsf(base.y - badguy->base.y) < 64)
615               badguy->kill_me(25);
616           }
617       }
618     }
619 #endif
620   }
621
622   /** jumping is only allowed if we're about to touch ground soon and if the
623    * button has been up in between the last jump
624    */
625   // FIXME
626 #if 0
627   if ( (issolid(get_pos().x + bbox.get_width() / 2,
628           get_pos().y + bbox.get_height() + 64) ||
629         issolid(get_pos().x + 1, get_pos().y + bbox.get_height() + 64) ||
630         issolid(get_pos().x + bbox.get_width() - 1,
631           get_pos().y + bbox.get_height() + 64))
632        && jumping  == false
633        && can_jump == false
634        && input.jump == DOWN
635        && input.old_jump == UP)
636     {
637       can_jump = true;
638     }
639 #endif
640
641   if(on_ground())   /* Make sure jumping is off. */
642     {
643       jumping = false;
644       flapping = false;
645       falling_from_flap = false;
646       if (flapping_timer.started()) {
647         flapping_timer.start(0);
648       }
649
650       physic.set_acceleration_y(0); //for flapping
651     }
652
653   input.old_jump = input.jump;
654 }
655
656 void
657 Player::handle_input()
658 {
659   /* Handle horizontal movement: */
660   handle_horizontal_input();
661
662   /* Jump/jumping? */
663   if (on_ground() && input.jump == UP)
664     can_jump = true;
665   handle_vertical_input();
666
667   /* Shoot! */
668   if (input.fire == DOWN && input.old_fire == UP && got_power != NONE_POWER) {
669     if(Sector::current()->add_bullet(
670           get_pos() + Vector(0, bbox.get_height()/2),
671           physic.get_velocity_x(), dir))
672       shooting_timer.start(SHOOTING_TIME);
673     input.old_fire = DOWN;
674   }
675
676 #if 0
677   /* tux animations: */
678   if(frame_timer.check()) {
679     if (input.right == UP && input.left == UP)
680     {
681       frame_main = 1;
682       frame_ = 1;
683     }
684     else
685     {
686       if ((input.fire == DOWN && (global_frame_counter % 2) == 0) ||
687           (global_frame_counter % 4) == 0)
688         frame_main = (frame_main + 1) % 4;
689
690       frame_ = frame_main;
691
692       if (frame_ == 3)
693         frame_ = 1;
694     }
695   }
696 #endif
697
698   /* Duck! */
699   if (input.down == DOWN && size == BIG && !duck 
700       && physic.get_velocity_y() == 0 && on_ground())
701     {
702       duck = true;
703       bbox.move(Vector(0, 32));
704       bbox.set_height(32);
705     }
706   else if(input.down == UP && size == BIG && duck)
707     {
708       // try if we can really unduck
709       bbox.move(Vector(0, -32));
710       bbox.set_height(64);
711       duck = false;
712       // FIXME
713 #if 0
714       // when unducking in air we need some space to do so
715       if(on_ground() || !collision_object_map(bbox)) {
716         duck = false;
717       } else {
718         // undo the ducking changes
719         bbox.move(Vector(0, 32));
720         bbox.set_height(32);
721       }
722 #endif
723     }
724 }
725
726 void
727 Player::grow(bool animate)
728 {
729   if(size == BIG)
730     return;
731   
732   size = BIG;
733   bbox.set_height(64);
734   bbox.move(Vector(0, -32));
735
736   if(animate)
737     growing_timer.start(GROWING_TIME);
738 }
739
740 void
741 Player::grabdistros()
742 {
743 }
744
745 void
746 Player::draw(DrawingContext& context)
747 {
748   TuxBodyParts* tux_body;
749           
750   if (size == SMALL)
751     tux_body = small_tux;
752   else if (got_power == FIRE_POWER)
753     tux_body = fire_tux;
754   else if (got_power == ICE_POWER)
755     tux_body = ice_tux;
756   else
757     tux_body = big_tux;
758
759   int layer = LAYER_OBJECTS - 1;
760
761   /* Set Tux sprite action */
762   if (duck && size == BIG)
763     {
764     if(dir == LEFT)
765       tux_body->set_action("duck-left");
766     else // dir == RIGHT
767       tux_body->set_action("duck-right");
768     }
769   else if (skidding_timer.started() && !skidding_timer.check())
770     {
771     if(dir == LEFT)
772       tux_body->set_action("skid-left");
773     else // dir == RIGHT
774       tux_body->set_action("skid-right");
775     }
776   else if (kick_timer.started() && !kick_timer.check())
777     {
778     if(dir == LEFT)
779       tux_body->set_action("kick-left");
780     else // dir == RIGHT
781       tux_body->set_action("kick-right");
782     }
783   else if (butt_jump && size == BIG)
784     {
785     if(dir == LEFT)
786       tux_body->set_action("buttjump-left");
787     else // dir == RIGHT
788       tux_body->set_action("buttjump-right");
789     }
790   else if (physic.get_velocity_y() != 0)
791     {
792     if(dir == LEFT)
793       tux_body->set_action("jump-left");
794     else // dir == RIGHT
795       tux_body->set_action("jump-right");
796     }
797   else
798     {
799     if (fabsf(physic.get_velocity_x()) < 1.0f) // standing
800       {
801       if(dir == LEFT)
802         tux_body->set_action("stand-left");
803       else // dir == RIGHT
804         tux_body->set_action("stand-right");
805       }
806     else // moving
807       {
808       if(dir == LEFT)
809         tux_body->set_action("walk-left");
810       else // dir == RIGHT
811         tux_body->set_action("walk-right");
812       }
813     }
814
815   if(idle_timer.check())
816     {
817     if(size == BIG)
818       {
819       if(dir == LEFT)
820         tux_body->head->set_action("idle-left");
821       else // dir == RIGHT
822         tux_body->head->set_action("idle-right");
823
824       tux_body->head->start_animation(1);
825       }
826
827     }
828
829   // Tux is holding something
830   if ((holding_something && physic.get_velocity_y() == 0) ||
831       (shooting_timer.get_timeleft() > 0 && !shooting_timer.check()))
832     {
833     if (duck)
834       {
835       if(dir == LEFT)
836         tux_body->arms->set_action("duck+grab-left");
837       else // dir == RIGHT
838         tux_body->arms->set_action("duck+grab-right");
839       }
840     else
841       {
842       if(dir == LEFT)
843         tux_body->arms->set_action("grab-left");
844       else // dir == RIGHT
845         tux_body->arms->set_action("grab-right");
846       }
847     }
848
849   /* Draw Tux */
850   if (dying == DYING_SQUISHED) {
851     smalltux_gameover->draw(context, get_pos(), LAYER_FOREGROUNDTILES+1);
852   } else if(growing_timer.get_timeleft() > 0) {
853     if(size == SMALL)
854       {
855       if (dir == RIGHT)
856         context.draw_surface(growingtux_right[GROWING_FRAMES-1 - 
857                  int((growing_timer.get_timegone() *
858                  GROWING_FRAMES) / GROWING_TIME)], get_pos(), layer);
859       else
860         context.draw_surface(growingtux_left[GROWING_FRAMES-1 - 
861                 int((growing_timer.get_timegone() *
862                 GROWING_FRAMES) / GROWING_TIME)], get_pos(), layer);
863       }
864     else
865       {
866       if (dir == RIGHT)
867         context.draw_surface(growingtux_right[
868             int((growing_timer.get_timegone() *
869                 GROWING_FRAMES) / GROWING_TIME)], get_pos(), layer);
870       else
871         context.draw_surface(growingtux_left[
872             int((growing_timer.get_timegone() *
873                              GROWING_FRAMES) / GROWING_TIME)],
874             get_pos(), layer);
875       }
876     }
877   else if (safe_timer.started() && global_frame_counter%2)
878     ;  // don't draw Tux
879   else
880     tux_body->draw(context, get_pos(), layer);
881
882   // Draw blinking star overlay
883   if (invincible_timer.started() &&
884      (invincible_timer.get_timeleft() > TUX_INVINCIBLE_TIME_WARNING
885       || global_frame_counter % 3)
886      && !dying)
887   {
888     if (size == SMALL || duck)
889       smalltux_star->draw(context, get_pos(), LAYER_OBJECTS + 2);
890     else
891       bigtux_star->draw(context, get_pos(), LAYER_OBJECTS + 2);
892   }
893  
894   if (debug_mode)
895     context.draw_filled_rect(get_pos(),
896         Vector(bbox.get_width(), bbox.get_height()),
897         Color(75,75,75, 150), LAYER_OBJECTS+1);
898 }
899
900 HitResponse
901 Player::collision(GameObject& other, const CollisionHit& hit)
902 {
903   if(dying) {
904     return FORCE_MOVE;
905   }
906
907   if(other.get_flags() & FLAG_SOLID) {
908     if(hit.normal.y < 0) { // landed on floor?
909       if (physic.get_velocity_y() < 0)
910         physic.set_velocity_y(0);
911       on_ground_flag = true;
912     } else if(hit.normal.y > 0) { // bumped against the roof
913       physic.set_velocity_y(0);
914     }
915     
916     if(hit.normal.x != 0) { // hit on the side?
917       if(hit.normal.y > 0.6) // limits the slopes we can move up...
918         physic.set_velocity_x(0);
919     }
920
921     return CONTINUE;
922   }
923
924   TriggerBase* trigger = dynamic_cast<TriggerBase*> (&other);
925   if(trigger) {
926     if(input.up == DOWN && input.old_up == UP)
927       trigger->event(*this, TriggerBase::EVENT_ACTIVATE);
928   }
929
930   return FORCE_MOVE;
931 }
932
933 #if 0
934 void
935 Player::collision(void* p_c_object, int c_object)
936 {
937   //BadGuy* pbad_c = NULL;
938   //Trampoline* ptramp_c = NULL;
939   //FlyingPlatform* pplatform_c = NULL;
940
941   switch (c_object)
942     {
943     case CO_BADGUY:
944       pbad_c = (BadGuy*) p_c_object;
945
946      /* Hurt player if he touches a badguy */
947       if (!pbad_c->dying && !dying &&
948           !safe_timer.started() &&
949           pbad_c->mode != BadGuy::HELD)
950         {
951           if (pbad_c->mode == BadGuy::FLAT && input.fire == DOWN
952                && !holding_something)
953             {
954               holding_something = true;
955               pbad_c->mode = BadGuy::HELD;
956               pbad_c->base.y-=8;
957             }
958           else if (pbad_c->mode == BadGuy::FLAT)
959             {
960               // Don't get hurt if we're kicking a flat badguy!
961             }
962           else if (pbad_c->mode == BadGuy::KICK)
963             {
964               /* Hurt if you get hit by kicked laptop: */
965               if (!invincible_timer.started())
966                 {
967                   kill(SHRINK);
968                 }
969               else
970                 pbad_c->kill_me(20);
971             }
972           else if (!pbad_c->frozen_timer.check() && (pbad_c->kind == BAD_MRBOMB
973               || pbad_c->kind == BAD_JUMPY || pbad_c->kind == BAD_FISH
974               || pbad_c->kind == BAD_SPIKY))
975                 pbad_c->kill_me(20);
976           else
977             {
978               if (!invincible_timer.started())
979                 {
980                   kill(SHRINK);
981                 }
982               else
983                 {
984                   pbad_c->kill_me(25);
985                 }
986             }
987           player_status.score_multiplier++;
988         }
989       break;
990
991     case CO_TRAMPOLINE:
992       ptramp_c = (Trampoline*) p_c_object;
993       
994       // Pick up trampoline
995       if (ptramp_c->mode != Trampoline::M_HELD && input.fire == DOWN && !holding_something && on_ground())
996       {
997         holding_something = true;
998         ptramp_c->mode = Trampoline::M_HELD;
999         ptramp_c->base.y -= 8;
1000       }
1001       // Set down trampoline
1002       else if (ptramp_c->mode == Trampoline::M_HELD && input.fire != DOWN)
1003       {
1004         holding_something = false;
1005         ptramp_c->mode = Trampoline::M_NORMAL;
1006         ptramp_c->base.y += 8;
1007         ptramp_c->physic.set_velocity(physic.get_velocity_x(), physic.get_velocity_y());
1008
1009         //if (dir == RIGHT)
1010         //  ptramp_c->base.x = base.x + base.width+1;
1011         //else /* LEFT */
1012         //  ptramp_c->base.x = base.x - base.width-1;
1013       }
1014 /*
1015       // Don't let tux walk through trampoline
1016       else if (ptramp_c->mode != Trampoline::M_HELD && on_ground())
1017       {
1018         if (physic.get_velocity_x() > 0) // RIGHT
1019         {
1020           physic.set_velocity_x(0);
1021           base.x = ptramp_c->base.x - base.width;
1022         }
1023         else if (physic.get_velocity_x() < 0) // LEFT
1024         {
1025           physic.set_velocity_x(0);
1026           base.x = ptramp_c->base.x + ptramp_c->base.width;
1027         }
1028       }
1029 */
1030       break;
1031     case CO_FLYING_PLATFORM:
1032       pplatform_c = (FlyingPlatform*) p_c_object;
1033       
1034       base.y = pplatform_c->base.y - base.height;
1035       physic.set_velocity_x(pplatform_c->get_vel_x());
1036       
1037       physic.enable_gravity(false);
1038       can_jump = true;
1039       fall_mode = ON_GROUND;
1040       break;
1041
1042     default:
1043       break;
1044     }
1045
1046 }
1047 #endif
1048
1049 void
1050 Player::make_invincible()
1051 {
1052   SoundManager::get()->play_sound(IDToSound(SND_HERRING));
1053   invincible_timer.start(TUX_INVINCIBLE_TIME);
1054   Sector::current()->play_music(HERRING_MUSIC);               
1055 }
1056
1057 /* Kill Player! */
1058 void
1059 Player::kill(HurtMode mode)
1060 {
1061   if(dying)
1062     return;
1063
1064   if(safe_timer.get_timeleft() > 0 || invincible_timer.get_timeleft() > 0)
1065     return;                          
1066   
1067   SoundManager::get()->play_sound(IDToSound(SND_HURT));
1068
1069   physic.set_velocity_x(0);
1070
1071   if (mode == SHRINK && size == BIG)
1072     {
1073       if (got_power != NONE_POWER)
1074         {
1075           safe_timer.start(TUX_SAFE_TIME);
1076           got_power = NONE_POWER;
1077         }
1078       else
1079         {
1080           growing_timer.start(GROWING_TIME);
1081           safe_timer.start(TUX_SAFE_TIME + GROWING_TIME);
1082           size = SMALL;
1083           bbox.set_height(32);
1084           duck = false;
1085         }
1086     }
1087   else
1088     {
1089       physic.enable_gravity(true);
1090       physic.set_acceleration(0, 0);
1091       physic.set_velocity(0, 700);
1092       --player_status.lives;
1093       dying = DYING_SQUISHED;
1094       dying_timer.start(3.0);
1095     }
1096 }
1097
1098 /* Remove Tux's power ups */
1099 void
1100 Player::remove_powerups()
1101 {
1102   got_power = NONE_POWER;
1103   size = SMALL;
1104   bbox.set_height(32);
1105 }
1106
1107 void
1108 Player::move(const Vector& vector)
1109 {
1110   bbox.set_pos(vector);
1111 }
1112
1113 void
1114 Player::check_bounds(Camera* camera)
1115 {
1116   /* Keep tux in bounds: */
1117   if (get_pos().x < 0)
1118     { // Lock Tux to the size of the level, so that he doesn't fall of
1119       // on the left side
1120       bbox.set_pos(Vector(0, get_pos().y));
1121     }
1122
1123   /* Keep in-bounds, vertically: */
1124   if (get_pos().y > Sector::current()->solids->get_height() * 32)
1125     {
1126       kill(KILL);
1127       return;
1128     }
1129
1130   bool adjust = false;
1131   // can happen if back scrolling is disabled
1132   if(get_pos().x < camera->get_translation().x) {
1133     bbox.set_pos(Vector(camera->get_translation().x, get_pos().y));
1134     adjust = true;
1135   }
1136   if(get_pos().x >= camera->get_translation().x + screen->w - bbox.get_width())
1137   {
1138     bbox.set_pos(Vector(
1139           camera->get_translation().x + screen->w - bbox.get_width(),
1140           get_pos().y));
1141     adjust = true;
1142   }
1143
1144   if(adjust) {
1145     // FIXME
1146 #if 0
1147     // squished now?
1148     if(collision_object_map(bbox)) {
1149       kill(KILL);
1150       return;
1151     }
1152 #endif
1153   }
1154 }
1155
1156 void
1157 Player::bounce(BadGuy& badguy)
1158 {
1159   //Make sure we stopped flapping
1160   flapping = false;
1161   falling_from_flap = false;
1162   
1163   if (input.jump)
1164     physic.set_velocity_y(520);
1165   else
1166     physic.set_velocity_y(200);
1167 }
1168