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