Stomp sprite stays in place now (i.e. doesn't follow Tux around anymore)
[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
20 #include <math.h>
21 #include <iostream>
22 #include <cassert>
23 #include "gameloop.h"
24 #include "globals.h"
25 #include "player.h"
26 #include "defines.h"
27 #include "scene.h"
28 #include "tile.h"
29 #include "sprite.h"
30 #include "sector.h"
31 #include "tilemap.h"
32 #include "camera.h"
33 #include "gameobjs.h"
34 #include "resources.h"
35 #include "interactive_object.h"
36 #include "screen/screen.h"
37
38 // behavior definitions:
39 #define TILES_FOR_BUTTJUMP 3
40 // animation times (in ms):
41 #define SHOOTING_TIME 320
42 #define STOMP_TIME 250
43 // others stuff:
44 #define AUTOSCROLL_DEAD_INTERVAL 300
45
46 // growing animation
47 Surface* growingtux_left[GROWING_FRAMES];
48 Surface* growingtux_right[GROWING_FRAMES];
49
50 Surface* tux_life;
51
52 Sprite* smalltux_gameover;
53 Sprite* smalltux_star;
54 Sprite* largetux_star;
55
56 PlayerSprite smalltux;
57 PlayerSprite largetux;
58 PlayerSprite icetux;
59 PlayerSprite firetux;
60
61 PlayerKeymap keymap;
62
63 PlayerKeymap::PlayerKeymap()
64 {
65   keymap.jump  = SDLK_SPACE;
66   keymap.activate = SDLK_UP;
67   keymap.duck  = SDLK_DOWN;
68   keymap.left  = SDLK_LEFT;
69   keymap.right = SDLK_RIGHT;
70   keymap.fire  = SDLK_LCTRL;
71 }
72
73 void player_input_init(player_input_type* pplayer_input)
74 {
75   pplayer_input->down = UP;
76   pplayer_input->fire = UP;
77   pplayer_input->left = UP;
78   pplayer_input->old_fire = UP;
79   pplayer_input->right = UP;
80   pplayer_input->up = UP;
81   pplayer_input->old_up = UP;
82   pplayer_input->activate = UP;
83 }
84
85 Player::Player()
86 {
87   init();
88 }
89
90 Player::~Player()
91 {
92 }
93
94 void
95 Player::init()
96 {
97   holding_something = false;
98
99   base.width = 32;
100   base.height = 32;
101
102   size = SMALL;
103   got_power = NONE_POWER;
104
105   base.x = 0;
106   base.y = 0;
107   previous_base = old_base = base;
108   dir = RIGHT;
109   old_dir = dir;
110   duck = false;
111   dead = false;
112
113   dying   = DYING_NOT;
114   last_ground_y = 0;
115   fall_mode = ON_GROUND;
116   jumping = false;
117   can_jump = true;
118   butt_jump = false;
119   
120   stomp_pos = Vector(0,0);
121   frame_main = 0;
122   frame_ = 0;
123
124   player_input_init(&input);
125
126   invincible_timer.init(true);
127   skidding_timer.init(true);
128   safe_timer.init(true);
129   frame_timer.init(true);
130   kick_timer.init(true);
131   shooting_timer.init(true);
132   growing_timer.init(true);
133
134   physic.reset();
135 }
136
137 int
138 Player::key_event(SDLKey key, int state)
139 {
140   if(key == keymap.right)
141     {
142       input.right = state;
143       return true;
144     }
145   else if(key == keymap.left)
146     {
147       input.left = state;
148       return true;
149     }
150   else if(key == keymap.jump)
151     {
152       input.up = state;
153       return true;
154     }
155   else if(key == keymap.duck)
156     {
157       input.down = state;
158       return true;
159     }
160   else if(key == keymap.fire)
161     {
162       if (state == UP)
163         input.old_fire = UP;
164       input.fire = state;
165       return true;
166     }
167   else if(key == keymap.activate)
168     {
169       input.activate = state;
170
171       if(state == DOWN) {
172         /** check for interactive objects */
173         for(Sector::InteractiveObjects::iterator i 
174             = Sector::current()->interactive_objects.begin();
175             i != Sector::current()->interactive_objects.end(); ++i) {
176           if(rectcollision(base, (*i)->get_area())) {
177             (*i)->interaction(INTERACTION_ACTIVATE);
178           }
179         }
180       }
181       
182       return true;
183     }
184   else
185     return false;
186 }
187
188 void
189 Player::level_begin()
190 {
191   base.x  = 100;
192   base.y  = 170;
193   previous_base = old_base = base;
194   duck = false;
195
196   dying = DYING_NOT;
197
198   player_input_init(&input);
199
200   invincible_timer.init(true);
201   skidding_timer.init(true);
202   safe_timer.init(true);
203   frame_timer.init(true);
204   growing_timer.init(true);
205
206   physic.reset();
207 }
208
209 void
210 Player::action(float elapsed_time)
211 {
212   bool jumped_in_solid = false;
213
214   if(dying && !dying_timer.check()) {
215     dead = true;
216     return;
217   }
218
219   if (input.fire == UP)
220     holding_something = false;
221
222   /* Move tux: */
223   previous_base = base;
224
225   /* --- HANDLE TUX! --- */
226   if(dying == DYING_NOT)
227     handle_input();
228
229   physic.apply(elapsed_time, base.x, base.y);
230
231   if(dying == DYING_NOT) 
232     {
233       base_type target = base;
234
235       collision_swept_object_map(&old_base, &base);
236
237       if ((!invincible_timer.started() && !safe_timer.started())
238           && (isspike(base.x, base.y) || isspike(base.x + base.width, base.y)
239           ||  isspike(base.x, base.y + base.height)
240           ||  isspike(base.x + base.width, base.y + base.height)))
241       {
242          kill(SHRINK);
243       }
244
245       // Don't accelerate Tux if he is running against a wall
246       if (target.x != base.x)
247         {
248           physic.set_velocity_x(0);
249         }
250
251       // special exception for cases where we're stuck under tiles after
252       // being ducked. In this case we drift out
253       if(!duck && on_ground() && old_base.x == base.x && old_base.y == base.y
254          && collision_object_map(base))
255         {
256           base.x += elapsed_time * WALK_SPEED * (dir ? 1: -1);
257           previous_base = old_base = base;
258         }
259
260       // Land:
261       if (!on_ground())
262         {
263           physic.enable_gravity(true);
264           if(under_solid())
265             {
266               // fall down
267               physic.set_velocity_y(0);
268               jumped_in_solid = true;
269               jumping = false;
270             }
271         }
272       else
273         {
274           /* Land: */
275           if (physic.get_velocity_y() < 0)
276             {
277               base.y = (int)(((int)base.y / 32) * 32);
278               physic.set_velocity_y(0);
279             }
280
281           physic.enable_gravity(false);
282           /* Reset score multiplier (for multi-hits): */
283           if (!invincible_timer.started())
284             player_status.score_multiplier = 1;
285         }
286
287       if(jumped_in_solid)
288         {
289           if (isbrick(base.x, base.y) ||
290               isfullbox(base.x, base.y))
291             {
292               Sector::current()->trygrabdistro(
293                   Vector(base.x, base.y - 32), BOUNCE);
294               Sector::current()->trybumpbadguy(Vector(base.x, base.y - 64));
295
296               Sector::current()->trybreakbrick(
297                   Vector(base.x, base.y), size == SMALL);
298
299               bumpbrick(base.x, base.y);
300               Sector::current()->tryemptybox(Vector(base.x, base.y), RIGHT);
301             }
302
303           if (isbrick(base.x+ 31, base.y) ||
304               isfullbox(base.x+ 31, base.y))
305             {
306               Sector::current()->trygrabdistro(
307                   Vector(base.x+ 31, base.y - 32), BOUNCE);
308               Sector::current()->trybumpbadguy(Vector(base.x+ 31, base.y - 64));
309
310               if(size == BIG)
311                 Sector::current()->trybreakbrick(
312                     Vector(base.x+ 31, base.y), size == SMALL);
313
314               bumpbrick(base.x+ 31, base.y);
315               Sector::current()->tryemptybox(Vector(base.x+ 31, base.y), LEFT);
316             }
317         }
318
319       grabdistros();
320
321       if (jumped_in_solid)
322         {
323           ++base.y;
324           ++old_base.y;
325           if(on_ground())
326             {
327               /* Make sure jumping is off. */
328               jumping = false;
329             }
330         }
331     }
332
333   /* ---- DONE HANDLING TUX! --- */
334
335   // check some timers
336   skidding_timer.check();
337   invincible_timer.check();
338   safe_timer.check();
339   kick_timer.check();
340 }
341
342 bool
343 Player::on_ground()
344 {
345   return ( issolid(base.x + base.width / 2, base.y + base.height) ||
346            issolid(base.x + 1, base.y + base.height) ||
347            issolid(base.x + base.width - 1, base.y + base.height));
348 }
349
350 bool
351 Player::under_solid()
352 {
353   return ( issolid(base.x + base.width / 2, base.y) ||
354            issolid(base.x + 1, base.y) ||
355            issolid(base.x + base.width - 1, base.y)  );
356 }
357
358 bool
359 Player::tiles_on_air(int tiles)
360 {
361   for(int t = 0; t != tiles; t++)
362      {
363      if(issolid(base.x + base.width / 2, base.y + base.height + (tiles*32)) ||
364          issolid(base.x + 1, base.y + base.height + (tiles*32)) ||
365          issolid(base.x + base.width - 1, base.y + base.height + (tiles*32)))
366        return false;
367      }
368   return true;
369 }
370
371 void
372 Player::handle_horizontal_input()
373 {
374   float vx = physic.get_velocity_x();
375   float vy = physic.get_velocity_y();
376   float ax = physic.get_acceleration_x();
377   float ay = physic.get_acceleration_y();
378
379   float dirsign = 0;
380   if(input.left == DOWN && input.right == UP && (!duck || physic.get_velocity_y() != 0)) {
381       old_dir = dir;
382       dir = LEFT;
383       dirsign = -1;
384   } else if(input.left == UP && input.right == DOWN && (!duck || physic.get_velocity_y() != 0)) {
385       old_dir = dir;
386       dir = RIGHT;
387       dirsign = 1;
388   }
389
390   if (input.fire == UP) {
391       ax = dirsign * WALK_ACCELERATION_X;
392       // limit speed
393       if(vx >= MAX_WALK_XM && dirsign > 0) {
394         vx = MAX_WALK_XM;
395         ax = 0;
396       } else if(vx <= -MAX_WALK_XM && dirsign < 0) {
397         vx = -MAX_WALK_XM;
398         ax = 0;
399       }
400   } else {
401       ax = dirsign * RUN_ACCELERATION_X;
402       // limit speed
403       if(vx >= MAX_RUN_XM && dirsign > 0) {
404         vx = MAX_RUN_XM;
405         ax = 0;
406       } else if(vx <= -MAX_RUN_XM && dirsign < 0) {
407         vx = -MAX_RUN_XM;
408         ax = 0;
409       }
410   }
411
412   // we can reach WALK_SPEED without any acceleration
413   if(dirsign != 0 && fabs(vx) < WALK_SPEED) {
414     vx = dirsign * WALK_SPEED;
415   }
416
417   // changing directions?
418   if(on_ground() && ((vx < 0 && dirsign >0) || (vx>0 && dirsign<0))) {
419       if(fabs(vx)>SKID_XM && !skidding_timer.check()) {
420           skidding_timer.start(SKID_TIME);
421           sound_manager->play_sound(sounds[SND_SKID]);
422           ax *= 2.5;
423       } else {
424           ax *= 2;
425       }
426   }
427
428   // we get slower when not pressing any keys
429   if(dirsign == 0) {
430       if(fabs(vx) < WALK_SPEED) {
431           vx = 0;
432           ax = 0;
433       } else if(vx < 0) {
434           ax = WALK_ACCELERATION_X * 1.5;
435       } else {
436           ax = WALK_ACCELERATION_X * -1.5;
437       }
438   }
439
440   // if we're on ice slow down acceleration or deceleration
441   if (isice(base.x, base.y + base.height))
442   {
443     /* the acceleration/deceleration rate on ice is inversely proportional to
444      * the current velocity.
445      */
446
447     // increasing 1 will increase acceleration/deceleration rate
448     // decreasing 1 will decrease acceleration/deceleration rate
449     //  must stay above zero, though
450     if (ax != 0) ax *= 1 / fabs(vx);
451   }
452
453   physic.set_velocity(vx, vy);
454   physic.set_acceleration(ax, ay);
455 }
456
457 void
458 Player::handle_vertical_input()
459 {
460   // set fall mode...
461   if(on_ground()) {
462     fall_mode = ON_GROUND;
463     last_ground_y = base.y;
464   } else {
465     if(base.y > last_ground_y)
466       fall_mode = FALLING;
467     else if(fall_mode == ON_GROUND)
468       fall_mode = JUMPING;
469   }
470
471   // Press jump key
472   if(input.up == DOWN && can_jump && on_ground())
473     {
474       if(duck) { // only jump a little bit when in duck mode {
475         physic.set_velocity_y(3);
476       } else {
477         // jump higher if we are running
478         if (fabs(physic.get_velocity_x()) > MAX_WALK_XM)
479           physic.set_velocity_y(5.8);
480         else
481           physic.set_velocity_y(5.2);
482       }
483
484       --base.y;
485       jumping = true;
486       can_jump = false;
487       if (size == SMALL)
488         sound_manager->play_sound(sounds[SND_JUMP]);
489       else
490         sound_manager->play_sound(sounds[SND_BIGJUMP]);
491     }
492   // Let go of jump key
493   else if(input.up == UP && jumping && physic.get_velocity_y() > 0)
494     {
495       jumping = false;
496       physic.set_velocity_y(0);
497     }
498
499    /* In case the player has pressed Down while in a certain range of air,
500       enable butt jump action */
501   if (input.down == DOWN && !butt_jump && !duck)
502     if(tiles_on_air(TILES_FOR_BUTTJUMP) && jumping)
503       butt_jump = true;
504
505    /* When Down is not held anymore, disable butt jump */
506   if(butt_jump && input.down == UP)
507     butt_jump = false;
508
509   // Do butt jump
510   if (butt_jump && on_ground() && size == BIG)
511   {
512     
513     if (duck) 
514       stomp_pos = Vector(base.x - 32, base.y);
515     else 
516       stomp_pos = Vector(base.x - 32, base.y + 32);    
517     stomp_timer.start(STOMP_TIME);
518     
519     butt_jump = false;
520
521     // Break bricks beneath Tux
522     if(Sector::current()->trybreakbrick(
523           Vector(base.x + 1, base.y + base.height), false)
524         || Sector::current()->trybreakbrick(
525            Vector(base.x + base.width - 1, base.y + base.height), false))
526     {
527       physic.set_velocity_y(2);
528       butt_jump = true;
529     }
530
531     // Kill nearby badguys
532     std::vector<GameObject*> gameobjects = Sector::current()->gameobjects;
533     for (std::vector<GameObject*>::iterator i = gameobjects.begin();
534          i != gameobjects.end();
535          i++)
536     {
537       BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
538       if(badguy)
539       {
540         
541         if (fabsf(base.x - badguy->base.x) < 150 &&
542             fabsf(base.y - badguy->base.y) < 60 &&
543             (issolid(badguy->base.x + 1, badguy->base.y + badguy->base.height) ||
544               issolid(badguy->base.x + badguy->base.width - 1, badguy->base.y + badguy->base.height)))
545           badguy->kill_me(25);
546       }
547     }
548   }
549
550   if ( (issolid(base.x + base.width / 2, base.y + base.height + 64) ||
551         issolid(base.x + 1, base.y + base.height + 64) ||
552         issolid(base.x + base.width - 1, base.y + base.height + 64))
553        && jumping  == false
554        && can_jump == false
555        && input.up == DOWN
556        && input.old_up == UP)
557     {
558       can_jump = true;
559     }
560
561   if(on_ground())   /* Make sure jumping is off. */
562     jumping = false;
563
564   input.old_up = input.up;
565 }
566
567 void
568 Player::handle_input()
569 {
570   /* Handle horizontal movement: */
571     handle_horizontal_input();
572
573   /* Jump/jumping? */
574
575   if (on_ground() && input.up == UP)
576     can_jump = true;
577   handle_vertical_input();
578
579   /* Shoot! */
580   if (input.fire == DOWN && input.old_fire == UP && got_power != NONE_POWER)
581     {
582       if(Sector::current()->add_bullet(Vector(base.x, base.y + (base.height/2)),
583           physic.get_velocity_x(), dir))
584         shooting_timer.start(SHOOTING_TIME);
585       input.old_fire = DOWN;
586     }
587
588   /* tux animations: */
589   if(!frame_timer.check())
590     {
591       frame_timer.start(25);
592       if (input.right == UP && input.left == UP)
593         {
594           frame_main = 1;
595           frame_ = 1;
596         }
597       else
598         {
599           if ((input.fire == DOWN && (global_frame_counter % 2) == 0) ||
600               (global_frame_counter % 4) == 0)
601             frame_main = (frame_main + 1) % 4;
602
603           frame_ = frame_main;
604
605           if (frame_ == 3)
606             frame_ = 1;
607         }
608     }
609
610   /* Duck! */
611   if (input.down == DOWN && size == BIG && !duck && physic.get_velocity_y() == 0 && on_ground())
612     {
613       duck = true;
614       base.height = 32;                             
615       base.y += 32;
616       // changing base size confuses collision otherwise
617       old_base = previous_base = base;
618     }
619   else if(input.down == UP && size == BIG && duck)
620     {
621       // try if we can really unduck
622       base.y -= 32;
623       base.height = 64;
624       // when unducking in air we need some space to do so
625       if(on_ground() || !collision_object_map(base)) {
626         duck = false;
627         // changing base size confuses collision otherwise
628         old_base = previous_base = base;                                
629       } else {
630         // undo the ducking changes
631         base.y += 32;
632         base.height = 32;
633       }   
634     }
635 }
636
637 void
638 Player::grow(bool animate)
639 {
640   if(size == BIG)
641     return;
642   
643   size = BIG;
644   base.height = 64;
645   base.y -= 32;
646
647   if(animate)
648     growing_timer.start(GROWING_TIME);
649
650   old_base = previous_base = base;
651 }
652
653 void
654 Player::grabdistros()
655 {
656   /* Grab distros: */
657   if (!dying)
658     {
659       Sector::current()->trygrabdistro(Vector(base.x, base.y), NO_BOUNCE);
660       Sector::current()->trygrabdistro(Vector(base.x+ 31, base.y), NO_BOUNCE);
661
662       Sector::current()->trygrabdistro(
663           Vector(base.x, base.y + base.height), NO_BOUNCE);
664       Sector::current()->trygrabdistro(
665           Vector(base.x+ 31, base.y + base.height), NO_BOUNCE);
666
667       if(size == BIG)
668         {
669           Sector::current()->trygrabdistro(
670               Vector(base.x, base.y + base.height / 2), NO_BOUNCE);
671           Sector::current()->trygrabdistro(
672               Vector(base.x+ 31, base.y + base.height / 2), NO_BOUNCE);
673         }
674
675     }
676
677   /* Enough distros for a One-up? */
678   if (player_status.distros >= DISTROS_LIFEUP)
679     {
680       player_status.distros = player_status.distros - DISTROS_LIFEUP;
681       if(player_status.lives < MAX_LIVES)
682         ++player_status.lives;
683       /*We want to hear the sound even, if MAX_LIVES is reached*/
684       sound_manager->play_sound(sounds[SND_LIFEUP]);
685     }
686 }
687
688 void
689 Player::draw(DrawingContext& context)
690 {
691   PlayerSprite* sprite;
692           
693   if (size == SMALL)
694     sprite = &smalltux;
695   else if (got_power == FIRE_POWER)
696     sprite = &firetux;
697   else if (got_power == ICE_POWER)
698     sprite = &icetux;
699   else
700     sprite = &largetux;
701
702   int layer = LAYER_OBJECTS - 1;
703   Vector pos = Vector(base.x, base.y);
704
705   if (!safe_timer.started() || (global_frame_counter % 2) == 0)
706     {
707       if (dying == DYING_SQUISHED)
708         {
709           smalltux_gameover->draw(context, pos, LAYER_OBJECTS+1);
710         }
711       else
712         {
713           if(growing_timer.check())
714             {
715               if (dir == RIGHT)
716                 context.draw_surface(growingtux_right[(growing_timer.get_gone() * GROWING_FRAMES) / GROWING_TIME], pos, layer);
717               else 
718                 context.draw_surface(growingtux_left[(growing_timer.get_gone() * GROWING_FRAMES) / GROWING_TIME], pos, layer);
719             }
720           else if (duck && size != SMALL)
721             {
722               if (dir == RIGHT)
723                 sprite->duck_right->draw(context, pos, layer);
724               else 
725                 sprite->duck_left->draw(context, pos, layer);
726             }
727           else if (skidding_timer.started())
728             {
729               if (dir == RIGHT)
730                 sprite->skid_right->draw(context, pos, layer);
731               else
732                 sprite->skid_left->draw(context, pos, layer);
733             }
734           else if (kick_timer.started())
735             {
736               if (dir == RIGHT)
737                 sprite->kick_right->draw(context, pos, layer);
738               else
739                 sprite->kick_left->draw(context, pos, layer);
740             }
741           else if (physic.get_velocity_y() != 0)
742             {
743               if (dir == RIGHT)
744                 sprite->jump_right->draw(context, pos, layer);
745               else
746                 sprite->jump_left->draw(context, pos, layer);
747             }
748           else
749             {
750               if (fabsf(physic.get_velocity_x()) < 1.0f) // standing
751                 {
752                   if (dir == RIGHT)
753                     sprite->stand_right->draw(context, pos, layer);
754                   else
755                     sprite->stand_left->draw(context, pos, layer);
756                 }
757               else // moving
758                 {
759                   if (dir == RIGHT)
760                     sprite->walk_right->draw(context, pos, layer);
761                   else
762                     sprite->walk_left->draw(context, pos, layer);
763                 }
764             }
765         }
766     }     
767
768   // Draw arm overlay graphics when Tux is holding something
769   if ((holding_something && physic.get_velocity_y() == 0) || shooting_timer.check() && !duck)
770   {
771     if (dir == RIGHT)
772       sprite->grab_right->draw(context, pos, LAYER_OBJECTS + 1);
773     else
774       sprite->grab_left->draw(context, pos, LAYER_OBJECTS + 1);
775   }
776   
777   // Draw stomp clouds when doing a butt jump
778   if (stomp_timer.check())
779       sprite->stomp->draw(context, stomp_pos, LAYER_OBJECTS + 1);
780
781   // Draw blinking star overlay
782   if (invincible_timer.started() &&
783       (invincible_timer.get_left() > TUX_INVINCIBLE_TIME_WARNING || global_frame_counter % 3))
784   {
785     if (size == SMALL || duck)
786       smalltux_star->draw(context, pos, LAYER_OBJECTS + 2);
787     else
788       largetux_star->draw(context, pos, LAYER_OBJECTS + 2);
789   }
790  
791   if (debug_mode)
792     context.draw_filled_rect(Vector(base.x, base.y),
793         Vector(base.width, base.height), Color(75,75,75, 150), LAYER_OBJECTS+1);
794 }
795
796 void
797 Player::collision(const MovingObject& other, int collision_type)
798 {
799   (void) other;
800   (void) collision_type;
801   // will be implemented later
802 }
803
804 void
805 Player::collision(void* p_c_object, int c_object)
806 {
807   BadGuy* pbad_c = NULL;
808   Trampoline* ptramp_c = NULL;
809   FlyingPlatform* pplatform_c = NULL;
810
811   switch (c_object)
812     {
813     case CO_BADGUY:
814       pbad_c = (BadGuy*) p_c_object;
815
816      /* Hurt player if he touches a badguy */
817       if (!pbad_c->dying && !dying &&
818           !safe_timer.started() &&
819           pbad_c->mode != BadGuy::HELD)
820         {
821           if (pbad_c->mode == BadGuy::FLAT && input.fire == DOWN
822                && !holding_something)
823             {
824               holding_something = true;
825               pbad_c->mode = BadGuy::HELD;
826               pbad_c->base.y-=8;
827             }
828           else if (pbad_c->mode == BadGuy::FLAT)
829             {
830               // Don't get hurt if we're kicking a flat badguy!
831             }
832           else if (pbad_c->mode == BadGuy::KICK)
833             {
834               /* Hurt if you get hit by kicked laptop: */
835               if (!invincible_timer.started())
836                 {
837                   kill(SHRINK);
838                 }
839               else
840                 pbad_c->kill_me(20);
841             }
842           else if (pbad_c->frozen_timer.check() && (pbad_c->kind == BAD_MRBOMB
843               || pbad_c->kind == BAD_JUMPY || pbad_c->kind == BAD_FISH
844               || pbad_c->kind == BAD_SPIKY))
845                 pbad_c->kill_me(20);
846           else
847             {
848               if (!invincible_timer.started())
849                 {
850                   kill(SHRINK);
851                 }
852               else
853                 {
854                   pbad_c->kill_me(25);
855                 }
856             }
857           player_status.score_multiplier++;
858         }
859       break;
860
861     case CO_TRAMPOLINE:
862       ptramp_c = (Trampoline*) p_c_object;
863       
864       // Pick up trampoline
865       if (ptramp_c->mode != Trampoline::M_HELD && input.fire == DOWN && !holding_something && on_ground())
866       {
867         holding_something = true;
868         ptramp_c->mode = Trampoline::M_HELD;
869         ptramp_c->base.y -= 8;
870       }
871       // Set down trampoline
872       else if (ptramp_c->mode == Trampoline::M_HELD && input.fire != DOWN)
873       {
874         holding_something = false;
875         ptramp_c->mode = Trampoline::M_NORMAL;
876         ptramp_c->base.y += 8;
877         ptramp_c->physic.set_velocity(physic.get_velocity_x(), physic.get_velocity_y());
878
879         //if (dir == RIGHT)
880         //  ptramp_c->base.x = base.x + base.width+1;
881         //else /* LEFT */
882         //  ptramp_c->base.x = base.x - base.width-1;
883       }
884 /*
885       // Don't let tux walk through trampoline
886       else if (ptramp_c->mode != Trampoline::M_HELD && on_ground())
887       {
888         if (physic.get_velocity_x() > 0) // RIGHT
889         {
890           physic.set_velocity_x(0);
891           base.x = ptramp_c->base.x - base.width;
892         }
893         else if (physic.get_velocity_x() < 0) // LEFT
894         {
895           physic.set_velocity_x(0);
896           base.x = ptramp_c->base.x + ptramp_c->base.width;
897         }
898       }
899 */
900       break;
901     case CO_FLYING_PLATFORM:
902       pplatform_c = (FlyingPlatform*) p_c_object;
903       
904       base.y = pplatform_c->base.y - base.height;
905       physic.set_velocity_x(pplatform_c->get_vel_x());
906       
907       physic.enable_gravity(false);
908       can_jump = true;
909       fall_mode = ON_GROUND;
910       break;
911
912     default:
913       break;
914     }
915
916 }
917
918 /* Kill Player! */
919
920 void
921 Player::kill(HurtMode mode)
922 {
923   if(dying)
924     return;
925   
926   sound_manager->play_sound(sounds[SND_HURT]);
927
928   physic.set_velocity_x(0);
929
930   if (mode == SHRINK && size == BIG)
931     {
932       if (got_power != NONE_POWER)
933         {
934           got_power = NONE_POWER;
935         }
936       else
937         {
938           size = SMALL;
939           base.height = 32;
940           duck = false;
941         }
942       safe_timer.start(TUX_SAFE_TIME);
943     }
944   else
945     {
946       physic.enable_gravity(true);
947       physic.set_acceleration(0, 0);
948       physic.set_velocity(0, 7);
949       --player_status.lives;
950       dying = DYING_SQUISHED;
951       dying_timer.start(3000);
952     }
953 }
954
955 /* Remove Tux's power ups */
956 void
957 Player::remove_powerups()
958 {
959   got_power = NONE_POWER;
960   size = SMALL;
961   base.height = 32;
962 }
963
964 void
965 Player::move(const Vector& vector)
966 {
967   base.x = vector.x;
968   base.y = vector.y;
969   old_base = previous_base = base;
970 }
971
972 void
973 Player::check_bounds(Camera* camera)
974 {
975   /* Keep tux in bounds: */
976   if (base.x < 0)
977     { // Lock Tux to the size of the level, so that he doesn't fall of
978       // on the left side
979       base.x = 0;
980     }
981
982   /* Keep in-bounds, vertically: */
983   if (base.y > Sector::current()->solids->get_height() * 32)
984     {
985       kill(KILL);
986       return;
987     }
988
989   bool adjust = false;
990   // can happen if back scrolling is disabled
991   if(base.x < camera->get_translation().x) {
992     base.x = camera->get_translation().x;
993     adjust = true;
994   }
995   if(base.x >= camera->get_translation().x + screen->w - base.width) {
996     base.x = camera->get_translation().x + screen->w - base.width;
997     adjust = true;
998   }
999
1000   if(adjust) {
1001     // squished now?
1002     if(collision_object_map(base)) {
1003       kill(KILL);
1004       return;
1005     }
1006   }
1007 }
1008