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