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