some startup time debugging, changed skid dust particles back to old behaviour
[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 "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(dir == RIGHT ? bbox.p2.x : bbox.p1.x, bbox.p2.y),
285           dir == RIGHT ? 270+20 : 90-40, dir == RIGHT ? 270+40 : 90-20,
286           Vector(280, -260), Vector(0, 300), 3, Color(.4, .4, .4), 3, .8,
287           LAYER_OBJECTS+1));
288       
289       ax *= 2.5;
290     } else {
291       ax *= 2;
292     }
293   }
294
295   // we get slower when not pressing any keys
296   if(dirsign == 0) {
297     if(fabs(vx) < WALK_SPEED) {
298       vx = 0;
299       ax = 0;
300     } else if(vx < 0) {
301       ax = WALK_ACCELERATION_X * 1.5;
302     } else {
303       ax = WALK_ACCELERATION_X * -1.5;
304     }
305   }
306
307 #if 0
308   // if we're on ice slow down acceleration or deceleration
309   if (isice(base.x, base.y + base.height))
310   {
311     /* the acceleration/deceleration rate on ice is inversely proportional to
312      * the current velocity.
313      */
314
315     // increasing 1 will increase acceleration/deceleration rate
316     // decreasing 1 will decrease acceleration/deceleration rate
317     //  must stay above zero, though
318     if (ax != 0) ax *= 1 / fabs(vx);
319   }
320 #endif
321
322   // extend/shrink tux collision rectangle so that we fall through/walk over 1
323   // tile holes
324   if(fabsf(vx) > MAX_WALK_XM) {
325     bbox.set_width(33);
326   } else {
327     bbox.set_width(31.8);
328   }
329
330   physic.set_velocity(vx, vy);
331   physic.set_acceleration(ax, ay);
332 }
333
334 void
335 Player::handle_vertical_input()
336 {
337   // set fall mode...
338   if(on_ground()) {
339     fall_mode = ON_GROUND;
340     last_ground_y = get_pos().y;
341   } else {
342     if(get_pos().y > last_ground_y)
343       fall_mode = FALLING;
344     else if(fall_mode == ON_GROUND)
345       fall_mode = JUMPING;
346   }
347
348   if(on_ground()) { /* Make sure jumping is off. */
349     jumping = false;
350     if (backflipping) {
351       backflipping = false;
352       backflip_direction = 0;
353     }
354   }
355
356   // Press jump key
357   if(controller->pressed(Controller::JUMP) && can_jump && on_ground()) {
358     if (duck) { 
359       if (physic.get_velocity_x() != 0) // only jump a little bit when running ducked
360         physic.set_velocity_y(300);
361       else { //do a backflip
362         backflipping = true;
363         physic.set_velocity_y(580);
364         backflip_timer.start(0.15);
365       }
366     }
367     else if (fabs(physic.get_velocity_x()) > MAX_WALK_XM) // jump higher if we are running
368       physic.set_velocity_y(580);
369     else
370       physic.set_velocity_y(520);
371     
372     //bbox.move(Vector(0, -1));
373     jumping = true;
374     can_jump = false;
375     if (is_big())
376       sound_manager->play("sounds/bigjump.wav");
377     else
378       sound_manager->play("sounds/jump.wav");
379   } else if(!controller->hold(Controller::JUMP)) { // Let go of jump key
380     if (!backflipping && jumping && physic.get_velocity_y() > 0) {
381       jumping = false;
382       physic.set_velocity_y(0);
383     }
384   }
385
386   /* In case the player has pressed Down while in a certain range of air,
387      enable butt jump action */
388   if (controller->hold(Controller::DOWN) && !butt_jump && !duck)
389     //if(tiles_on_air(TILES_FOR_BUTTJUMP) && jumping)
390     butt_jump = true;
391   
392   /* When Down is not held anymore, disable butt jump */
393   if(butt_jump && !controller->hold(Controller::DOWN))
394     butt_jump = false;
395   
396 #if 0
397   // Do butt jump
398   if (butt_jump && on_ground() && is_big()) {
399     // Add a smoke cloud
400     if (duck) 
401       Sector::current()->add_smoke_cloud(Vector(get_pos().x - 32, get_pos().y));
402     else 
403       Sector::current()->add_smoke_cloud(
404         Vector(get_pos().x - 32, get_pos().y + 32));
405     
406     butt_jump = false;
407     
408     // Break bricks beneath Tux
409     if(Sector::current()->trybreakbrick(
410          Vector(base.x + 1, base.y + base.height), false)
411        || Sector::current()->trybreakbrick(
412          Vector(base.x + base.width - 1, base.y + base.height), false)) {
413       physic.set_velocity_y(2);
414       butt_jump = true;
415     }
416     
417     // Kill nearby badguys
418     std::vector<GameObject*> gameobjects = Sector::current()->gameobjects;
419     for (std::vector<GameObject*>::iterator i = gameobjects.begin();
420          i != gameobjects.end();
421          i++) {
422       BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
423       if(badguy) {
424         // don't kill when badguys are already dying or in a certain mode
425         if(badguy->dying == DYING_NOT && badguy->mode != BadGuy::BOMB_TICKING &&
426            badguy->mode != BadGuy::BOMB_EXPLODE) {
427           if (fabsf(base.x - badguy->base.x) < 96 &&
428               fabsf(base.y - badguy->base.y) < 64)
429             badguy->kill_me(25);
430         }
431       }
432     }
433   }
434 #endif
435
436   /** jumping is only allowed if we're about to touch ground soon and if the
437    * button has been up in between the last jump
438    */
439   // FIXME
440 #if 0
441   if ( (issolid(get_pos().x + bbox.get_width() / 2,
442           get_pos().y + bbox.get_height() + 64) ||
443         issolid(get_pos().x + 1, get_pos().y + bbox.get_height() + 64) ||
444         issolid(get_pos().x + bbox.get_width() - 1,
445           get_pos().y + bbox.get_height() + 64))
446        && jumping  == false
447        && can_jump == false
448        && input.jump && !input.old_jump)
449     {
450       can_jump = true;
451     }
452 #endif
453 }
454
455 void
456 Player::handle_input()
457 {
458   /* Handle horizontal movement: */
459   if (!backflipping) handle_horizontal_input();
460   else {
461     if (backflip_direction == 0) {
462       dir == LEFT ? backflip_direction = 1 : backflip_direction = -1;
463     }
464     else backflip_direction == 1 ? dir = LEFT : dir = RIGHT; //prevent player from changing direction when backflipping 
465     if (backflip_timer.check()) physic.set_velocity_x(100 * backflip_direction);
466   }
467
468
469   /* Jump/jumping? */
470   if (on_ground() && !controller->hold(Controller::JUMP))
471     can_jump = true;
472   handle_vertical_input();
473
474   /* Shoot! */
475   if (controller->pressed(Controller::ACTION) && player_status->bonus == FIRE_BONUS) {
476     if(Sector::current()->add_bullet(
477          get_pos() + ((dir == LEFT)? Vector(0, bbox.get_height()/2) 
478                       : Vector(32, bbox.get_height()/2)),
479          physic.get_velocity_x(), dir))
480       shooting_timer.start(SHOOTING_TIME);
481   }
482   
483   /* Duck! */
484   if (controller->hold(Controller::DOWN) && is_big() && !duck 
485       && physic.get_velocity_y() == 0 && on_ground()) {
486     duck = true;
487     bbox.move(Vector(0, 32));
488     bbox.set_height(31.8);
489   } else if(!controller->hold(Controller::DOWN) && is_big() && duck) {
490     // try if we can really unduck
491     bbox.move(Vector(0, -32));
492     bbox.set_height(63.8);
493     duck = false;
494     // FIXME
495 #if 0
496     // when unducking in air we need some space to do so
497     if(on_ground() || !collision_object_map(bbox)) {
498       duck = false;
499     } else {
500       // undo the ducking changes
501       bbox.move(Vector(0, 32));
502       bbox.set_height(31.8);
503     }
504 #endif
505   }
506 }
507
508 void
509 Player::set_bonus(BonusType type, bool animate)
510 {
511   if(player_status->bonus >= type)
512     return;
513   
514   if(player_status->bonus == NO_BONUS) {
515     bbox.set_height(63.8);
516     bbox.move(Vector(0, -32));
517     if(animate)
518       growing_timer.start(GROWING_TIME);
519   }
520   
521   player_status->bonus = type;
522 }
523
524 void
525 Player::kick()
526 {
527   kick_timer.start(KICK_TIME);
528 }
529
530 void
531 Player::draw(DrawingContext& context)
532 {
533   TuxBodyParts* tux_body;
534           
535   if (player_status->bonus == GROWUP_BONUS)
536     tux_body = big_tux;
537   else if (player_status->bonus == FIRE_BONUS)
538     tux_body = fire_tux;
539   else if (player_status->bonus == ICE_BONUS)
540     tux_body = ice_tux;
541   else
542     tux_body = small_tux;
543
544   int layer = LAYER_OBJECTS + 10;
545
546   /* Set Tux sprite action */
547   if (duck && is_big())
548     {
549     if(dir == LEFT)
550       tux_body->set_action("duck-left");
551     else // dir == RIGHT
552       tux_body->set_action("duck-right");
553     }
554   else if (skidding_timer.started() && !skidding_timer.check())
555     {
556     if(dir == LEFT)
557       tux_body->set_action("skid-left");
558     else // dir == RIGHT
559       tux_body->set_action("skid-right");
560     }
561   else if (kick_timer.started() && !kick_timer.check())
562     {
563     if(dir == LEFT)
564       tux_body->set_action("kick-left");
565     else // dir == RIGHT
566       tux_body->set_action("kick-right");
567     }
568   else if (butt_jump && is_big())
569     {
570     if(dir == LEFT)
571       tux_body->set_action("buttjump-left");
572     else // dir == RIGHT
573       tux_body->set_action("buttjump-right");
574     }
575   else if (physic.get_velocity_y() != 0)
576     {
577     if(dir == LEFT)
578       tux_body->set_action("jump-left");
579     else // dir == RIGHT
580       tux_body->set_action("jump-right");
581     }
582   else
583     {
584     if (fabsf(physic.get_velocity_x()) < 1.0f) // standing
585       {
586       if(dir == LEFT)
587         tux_body->set_action("stand-left");
588       else // dir == RIGHT
589         tux_body->set_action("stand-right");
590       }
591     else // moving
592       {
593       if(dir == LEFT)
594         tux_body->set_action("walk-left");
595       else // dir == RIGHT
596         tux_body->set_action("walk-right");
597       }
598     }
599
600   if(idle_timer.check())
601     {
602     if(is_big())
603       {
604       if(dir == LEFT)
605         tux_body->head->set_action("idle-left", 1);
606       else // dir == RIGHT
607         tux_body->head->set_action("idle-right", 1);
608       }
609
610     }
611
612   // Tux is holding something
613   if ((grabbed_object != 0 && physic.get_velocity_y() == 0) ||
614       (shooting_timer.get_timeleft() > 0 && !shooting_timer.check()))
615     {
616     if (duck)
617       {
618       if(dir == LEFT)
619         tux_body->arms->set_action("duck+grab-left");
620       else // dir == RIGHT
621         tux_body->arms->set_action("duck+grab-right");
622       }
623     else
624       {
625       if(dir == LEFT)
626         tux_body->arms->set_action("grab-left");
627       else // dir == RIGHT
628         tux_body->arms->set_action("grab-right");
629       }
630     }
631
632   /* Draw Tux */
633   if(dying) {
634     smalltux_gameover->draw(context, get_pos(), layer);
635   } else if(growing_timer.get_timeleft() > 0) {
636     if(!is_big())
637       {
638       if (dir == RIGHT)
639         context.draw_surface(growingtux_right[GROWING_FRAMES-1 - 
640                  int((growing_timer.get_timegone() *
641                  GROWING_FRAMES) / GROWING_TIME)], get_pos(), layer);
642       else
643         context.draw_surface(growingtux_left[GROWING_FRAMES-1 - 
644                 int((growing_timer.get_timegone() *
645                 GROWING_FRAMES) / GROWING_TIME)], get_pos(), layer);
646       }
647     else
648       {
649       if (dir == RIGHT)
650         context.draw_surface(growingtux_right[
651             int((growing_timer.get_timegone() *
652                 GROWING_FRAMES) / GROWING_TIME)], get_pos(), layer);
653       else
654         context.draw_surface(growingtux_left[
655             int((growing_timer.get_timegone() *
656                              GROWING_FRAMES) / GROWING_TIME)],
657             get_pos(), layer);
658       }
659     }
660   else if (safe_timer.started() && size_t(game_time*40)%2)
661     ;  // don't draw Tux
662   else
663     tux_body->draw(context, get_pos(), layer);
664
665   // Draw blinking star overlay
666   if (invincible_timer.started() &&
667      (invincible_timer.get_timeleft() > TUX_INVINCIBLE_TIME_WARNING
668       || size_t(game_time*20)%2)
669      && !dying)
670   {
671     if (!is_big() || duck)
672       smalltux_star->draw(context, get_pos(), layer + 5);
673     else
674       bigtux_star->draw(context, get_pos(), layer + 5);
675   } 
676 }
677
678 void
679 Player::collision_tile(uint32_t tile_attributes)
680 {
681   if(tile_attributes & Tile::HURTS)
682     kill(SHRINK);
683 }
684
685 HitResponse
686 Player::collision(GameObject& other, const CollisionHit& hit)
687 {
688   Bullet* bullet = dynamic_cast<Bullet*> (&other);
689   if(bullet) {
690     return FORCE_MOVE;
691   }
692
693   if(other.get_flags() & FLAG_PORTABLE) {
694     Portable* portable = dynamic_cast<Portable*> (&other);
695     if(portable && grabbed_object == 0 && controller->hold(Controller::ACTION)
696         && fabsf(hit.normal.x) > .9) {
697       grabbed_object = portable;
698       return CONTINUE;
699     }
700   }
701  
702   if(other.get_flags() & FLAG_SOLID) {
703     if(hit.normal.y < 0) { // landed on floor?
704       if(physic.get_velocity_y() < 0)
705         physic.set_velocity_y(0);
706       on_ground_flag = true;
707     } else if(hit.normal.y > 0) { // bumped against the roof
708       physic.set_velocity_y(.1);
709     }
710     
711     if(fabsf(hit.normal.x) > .9) { // hit on the side?
712       physic.set_velocity_x(0);
713     }
714
715     return CONTINUE;
716   }
717
718   TriggerBase* trigger = dynamic_cast<TriggerBase*> (&other);
719   if(trigger) {
720     if(controller->pressed(Controller::UP))
721       trigger->event(*this, TriggerBase::EVENT_ACTIVATE);
722
723     return FORCE_MOVE;
724   }
725
726   MovingObject* moving_object = static_cast<MovingObject*> (&other);
727   if(moving_object->get_group() == COLGROUP_TOUCHABLE)
728     return FORCE_MOVE;
729
730   if(is_invincible())
731     return FORCE_MOVE;
732
733   return CONTINUE;
734 }
735
736 void
737 Player::make_invincible()
738 {
739   sound_manager->play("sounds/invincible.wav");
740   invincible_timer.start(TUX_INVINCIBLE_TIME);
741   Sector::current()->play_music(HERRING_MUSIC);               
742 }
743
744 /* Kill Player! */
745 void
746 Player::kill(HurtMode mode)
747 {
748   if(dying || deactivated)
749     return;
750
751   if(mode != KILL && 
752           safe_timer.get_timeleft() > 0 || invincible_timer.get_timeleft() > 0)
753     return;                          
754   
755   sound_manager->play("sounds/hurt.wav");
756
757   physic.set_velocity_x(0);
758
759   if (mode == SHRINK && is_big())
760     {
761       if (player_status->bonus == FIRE_BONUS
762           || player_status->bonus == ICE_BONUS)
763         {
764           safe_timer.start(TUX_SAFE_TIME);
765           player_status->bonus = GROWUP_BONUS;
766         }
767       else 
768         {
769           growing_timer.start(GROWING_TIME);
770           safe_timer.start(TUX_SAFE_TIME + GROWING_TIME);
771           bbox.set_height(31.8);
772           duck = false;
773           player_status->bonus = NO_BONUS;
774         }
775     }
776   else
777     {
778       physic.enable_gravity(true);
779       physic.set_acceleration(0, 0);
780       physic.set_velocity(0, 700);
781       player_status->lives -= 1;
782       player_status->bonus = NO_BONUS;
783       dying = true;
784       dying_timer.start(3.0);
785       set_group(COLGROUP_DISABLED);
786     }
787 }
788
789 void
790 Player::move(const Vector& vector)
791 {
792   bbox.set_pos(vector);
793   if(is_big())
794     bbox.set_size(31.8, 63.8);
795   else
796     bbox.set_size(31.8, 31.8);
797   on_ground_flag = false;
798   duck = false;
799   last_ground_y = vector.y;
800
801   physic.reset();
802 }
803
804 void
805 Player::check_bounds(Camera* camera)
806 {
807   /* Keep tux in bounds: */
808   if (get_pos().x < 0)
809     { // Lock Tux to the size of the level, so that he doesn't fall of
810       // on the left side
811       bbox.set_pos(Vector(0, get_pos().y));
812     }
813
814   /* Keep in-bounds, vertically: */
815   if (get_pos().y > Sector::current()->solids->get_height() * 32)
816     {
817       kill(KILL);
818       return;
819     }
820
821   bool adjust = false;
822   // can happen if back scrolling is disabled
823   if(get_pos().x < camera->get_translation().x) {
824     bbox.set_pos(Vector(camera->get_translation().x, get_pos().y));
825     adjust = true;
826   }
827   if(get_pos().x >= camera->get_translation().x + SCREEN_WIDTH - bbox.get_width())
828   {
829     bbox.set_pos(Vector(
830           camera->get_translation().x + SCREEN_WIDTH - bbox.get_width(),
831           get_pos().y));
832     adjust = true;
833   }
834
835   if(adjust) {
836     // FIXME
837 #if 0
838     // squished now?
839     if(collision_object_map(bbox)) {
840       kill(KILL);
841       return;
842     }
843 #endif
844   }
845 }
846
847 void
848 Player::bounce(BadGuy& )
849 {
850   if(controller->hold(Controller::JUMP))
851     physic.set_velocity_y(520);
852   else
853     physic.set_velocity_y(300);
854 }
855
856 //Scripting Functions Below
857
858 void
859 Player::deactivate()
860 {
861   deactivated = true;
862   physic.set_velocity_x(0);
863   physic.set_velocity_y(0);
864 }
865
866 void
867 Player::activate()
868 {
869   deactivated = false;
870 }
871
872 void Player::walk(float speed)
873 {
874   physic.set_velocity_x(speed);
875 }