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