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