first very unfinished and unpolished version of the intro
[supertux.git] / src / object / player.cpp
1 //  $Id$
2 //
3 //  SuperTux -  A Jump'n Run
4 //  Copyright (C) 2003 Tobias Glaesser <tobi.web@gmx.de>
5 //
6 //  This program is free software; you can redistribute it and/or
7 //  modify it under the terms of the GNU General Public License
8 //  as published by the Free Software Foundation; either version 2
9 //  of the License, or (at your option) any later version.
10 //
11 //  This program is distributed in the hope that it will be useful,
12 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //  GNU General Public License for more details.
15 //
16 //  You should have received a copy of the GNU General Public License
17 //  along with this program; if not, write to the Free Software
18 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19 #include <config.h>
20
21 #include <typeinfo>
22 #include <cmath>
23 #include <iostream>
24 #include <cassert>
25
26 #include "gettext.hpp"
27 #include "sprite/sprite_manager.hpp"
28 #include "audio/sound_manager.hpp"
29 #include "player.hpp"
30 #include "tile.hpp"
31 #include "sprite/sprite.hpp"
32 #include "sector.hpp"
33 #include "resources.hpp"
34 #include "video/screen.hpp"
35 #include "statistics.hpp"
36 #include "game_session.hpp"
37 #include "object/tilemap.hpp"
38 #include "object/camera.hpp"
39 #include "object/particles.hpp"
40 #include "object/portable.hpp"
41 #include "object/bullet.hpp"
42 #include "trigger/trigger_base.hpp"
43 #include "control/joystickkeyboardcontroller.hpp"
44 #include "main.hpp"
45 #include "badguy/badguy.hpp"
46 #include "player_status.hpp"
47
48 static const int TILES_FOR_BUTTJUMP = 3;
49 static const float SHOOTING_TIME = .150;
50 /// time before idle animation starts
51 static const float IDLE_TIME = 2.5;
52
53 static const float WALK_ACCELERATION_X = 300;
54 static const float RUN_ACCELERATION_X = 400;
55 static const float SKID_XM = 200;
56 static const float SKID_TIME = .3;
57 static const float MAX_WALK_XM = 230;
58 static const float MAX_RUN_XM = 320;
59 static const float WALK_SPEED = 100;
60
61 static const float KICK_TIME = .3;
62
63 // growing animation
64 Surface* growingtux_left[GROWING_FRAMES];
65 Surface* growingtux_right[GROWING_FRAMES];
66
67 Surface* tux_life = 0;
68
69 TuxBodyParts* small_tux = 0;
70 TuxBodyParts* big_tux = 0;
71 TuxBodyParts* fire_tux = 0;
72 TuxBodyParts* ice_tux = 0;
73
74 void
75 TuxBodyParts::set_action(std::string action, int loops)
76 {
77   if(head != NULL)
78     head->set_action(action, loops);
79   if(body != NULL)
80     body->set_action(action, loops);
81   if(arms != NULL)
82     arms->set_action(action, loops);
83   if(feet != NULL)
84     feet->set_action(action, loops);
85 }
86
87 void
88 TuxBodyParts::draw(DrawingContext& context, const Vector& pos, int layer)
89 {
90   if(head != NULL)
91     head->draw(context, pos, layer-1);
92   if(body != NULL)
93     body->draw(context, pos, layer-3);
94   if(arms != NULL)
95     arms->draw(context, pos, layer);
96   if(feet != NULL)
97     feet->draw(context, pos, layer-2);
98 }
99
100 Player::Player(PlayerStatus* _player_status)
101   : player_status(_player_status), grabbed_object(0)
102 {
103   controller = main_controller;
104   smalltux_gameover = sprite_manager->create("smalltux-gameover");
105   smalltux_star = sprite_manager->create("smalltux-star");
106   bigtux_star = sprite_manager->create("bigtux-star");
107   init();
108 }
109
110 Player::~Player()
111 {
112   delete smalltux_gameover;
113   delete smalltux_star;
114   delete bigtux_star;
115 }
116
117 void
118 Player::init()
119 {
120   if(is_big())
121     bbox.set_size(31.8, 63.8);
122   else
123     bbox.set_size(31.8, 31.8);
124
125   dir = RIGHT;
126   old_dir = dir;
127   duck = false;
128   dead = false;
129
130   dying = false;
131   last_ground_y = 0;
132   fall_mode = ON_GROUND;
133   jumping = false;
134   can_jump = true;
135   butt_jump = false;
136   deactivated = false;
137   backflipping = false;
138   backflip_direction = 0;
139   
140   on_ground_flag = false;
141   grabbed_object = 0;
142
143   physic.reset();
144 }
145
146 void
147 Player::set_controller(Controller* controller)
148 {
149   this->controller = controller;
150 }
151
152 void
153 Player::update(float elapsed_time)
154 {
155   if(dying && dying_timer.check()) {
156     dead = true;
157     return;
158   }
159
160   // fixes the "affected even while blinking" bug
161   if (safe_timer.started() && this->get_group() != COLGROUP_MOVING_ONLY_STATIC) {
162     this->set_group(COLGROUP_MOVING_ONLY_STATIC);
163   }
164   else if (!safe_timer.started() && this->get_group() == COLGROUP_MOVING_ONLY_STATIC) {
165     this->set_group(COLGROUP_MOVING);
166   }
167
168   if(!controller->hold(Controller::ACTION) && grabbed_object) {
169     // move the grabbed object a bit away from tux
170     Vector pos = get_pos() + 
171         Vector(dir == LEFT ? -bbox.get_width()-1 : bbox.get_width()+1,
172                 bbox.get_height()*0.66666 - 32);
173     Rect dest(pos, pos + Vector(32, 32));
174     if(Sector::current()->is_free_space(dest)) {
175       MovingObject* moving_object = dynamic_cast<MovingObject*> (grabbed_object);
176       if(moving_object) {
177         moving_object->set_pos(pos);
178       } else {
179 #ifdef DEBUG
180         std::cout << "Non MovingObjetc grabbed?!?\n";
181 #endif
182       }
183       grabbed_object->ungrab(*this, dir);
184       grabbed_object = 0;
185     }
186   }
187
188   if(!dying && !deactivated)
189     handle_input();
190
191   movement = physic.get_movement(elapsed_time);
192   on_ground_flag = false;
193
194 #if 0
195   // special exception for cases where we're stuck under tiles after
196   // being ducked. In this case we drift out
197   if(!duck && on_ground() && old_base.x == base.x && old_base.y == base.y
198      && collision_object_map(base)) {
199     base.x += elapsed_time * WALK_SPEED * (dir ? 1: -1);
200     previous_base = old_base = base;
201   }
202 #endif
203
204   if(grabbed_object != 0) {
205     Vector pos = get_pos() + 
206       Vector(dir == LEFT ? -16 : 16,
207              bbox.get_height()*0.66666 - 32);
208     grabbed_object->grab(*this, pos, dir);
209   }
210 }
211
212 bool
213 Player::on_ground()
214 {
215   return on_ground_flag;
216 }
217
218 bool
219 Player::is_big()
220 {
221   if(player_status->bonus == NO_BONUS)
222     return false;
223
224   return true;
225 }
226
227 void
228 Player::handle_horizontal_input()
229 {
230   float vx = physic.get_velocity_x();
231   float vy = physic.get_velocity_y();
232   float ax = physic.get_acceleration_x();
233   float ay = physic.get_acceleration_y();
234
235   float dirsign = 0;
236   if(!duck || physic.get_velocity_y() != 0) {
237     if(controller->hold(Controller::LEFT) && !controller->hold(Controller::RIGHT)) {
238       old_dir = dir;
239       dir = LEFT;
240       dirsign = -1;
241     } else if(!controller->hold(Controller::LEFT)
242               && controller->hold(Controller::RIGHT)) {
243       old_dir = dir;
244       dir = RIGHT;
245       dirsign = 1;
246     }
247   }
248
249   if (!controller->hold(Controller::ACTION)) {
250     ax = dirsign * WALK_ACCELERATION_X;
251     // limit speed
252     if(vx >= MAX_WALK_XM && dirsign > 0) {
253       vx = MAX_WALK_XM;
254       ax = 0;
255     } else if(vx <= -MAX_WALK_XM && dirsign < 0) {
256       vx = -MAX_WALK_XM;
257       ax = 0;
258     }
259   } else {
260     ax = dirsign * RUN_ACCELERATION_X;
261     // limit speed
262     if(vx >= MAX_RUN_XM && dirsign > 0) {
263       vx = MAX_RUN_XM;
264       ax = 0;
265     } else if(vx <= -MAX_RUN_XM && dirsign < 0) {
266       vx = -MAX_RUN_XM;
267       ax = 0;
268     }
269   }
270
271   // we can reach WALK_SPEED without any acceleration
272   if(dirsign != 0 && fabs(vx) < WALK_SPEED) {
273     vx = dirsign * WALK_SPEED;
274   }
275
276   // changing directions?
277   if(on_ground() && ((vx < 0 && dirsign >0) || (vx>0 && dirsign<0))) {
278     // let's skid!
279     if(fabs(vx)>SKID_XM && !skidding_timer.started()) {
280       skidding_timer.start(SKID_TIME);
281       sound_manager->play("sounds/skid.wav");
282       // dust some particles
283       Sector::current()->add_object(
284         new Particles(
285           Vector(dir == RIGHT ? bbox.p2.x : bbox.p1.x, bbox.p2.y),
286           dir == RIGHT ? 270+20 : 90-40, dir == RIGHT ? 270+40 : 90-20,
287           Vector(280, -260), Vector(0, 300), 3, Color(.4, .4, .4), 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     // if we have some velocity left then check if there is space for
492     // unducking
493     bbox.move(Vector(0, -32));
494     bbox.set_height(63.8);
495     if(Sector::current()->is_free_space(bbox) || (
496         physic.get_velocity_x() > -.01 && physic.get_velocity_x() < .01
497         && physic.get_velocity_y() > -.01 && physic.get_velocity_y() < .01))
498     {
499       duck = false;
500     } else {
501       // undo the ducking changes
502       bbox.move(Vector(0, 32));
503       bbox.set_height(31.8); 
504     }
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::set_visible(bool visible)
526 {
527   this->visible = visible;
528 }
529
530 bool
531 Player::get_visible()
532 {
533   return visible;
534 }
535
536 void
537 Player::kick()
538 {
539   kick_timer.start(KICK_TIME);
540 }
541
542 void
543 Player::draw(DrawingContext& context)
544 {
545   if(!visible)
546     return;
547   
548   TuxBodyParts* tux_body;
549           
550   if (player_status->bonus == GROWUP_BONUS)
551     tux_body = big_tux;
552   else if (player_status->bonus == FIRE_BONUS)
553     tux_body = fire_tux;
554   else if (player_status->bonus == ICE_BONUS)
555     tux_body = ice_tux;
556   else
557     tux_body = small_tux;
558
559   int layer = LAYER_OBJECTS + 10;
560
561   /* Set Tux sprite action */
562   if (duck && is_big())
563     {
564     if(dir == LEFT)
565       tux_body->set_action("duck-left");
566     else // dir == RIGHT
567       tux_body->set_action("duck-right");
568     }
569   else if (skidding_timer.started() && !skidding_timer.check())
570     {
571     if(dir == LEFT)
572       tux_body->set_action("skid-left");
573     else // dir == RIGHT
574       tux_body->set_action("skid-right");
575     }
576   else if (kick_timer.started() && !kick_timer.check())
577     {
578     if(dir == LEFT)
579       tux_body->set_action("kick-left");
580     else // dir == RIGHT
581       tux_body->set_action("kick-right");
582     }
583   else if (butt_jump && is_big())
584     {
585     if(dir == LEFT)
586       tux_body->set_action("buttjump-left");
587     else // dir == RIGHT
588       tux_body->set_action("buttjump-right");
589     }
590   else if (physic.get_velocity_y() != 0)
591     {
592     if(dir == LEFT)
593       tux_body->set_action("jump-left");
594     else // dir == RIGHT
595       tux_body->set_action("jump-right");
596     }
597   else
598     {
599     if (fabsf(physic.get_velocity_x()) < 1.0f) // standing
600       {
601       if(dir == LEFT)
602         tux_body->set_action("stand-left");
603       else // dir == RIGHT
604         tux_body->set_action("stand-right");
605       }
606     else // moving
607       {
608       if(dir == LEFT)
609         tux_body->set_action("walk-left");
610       else // dir == RIGHT
611         tux_body->set_action("walk-right");
612       }
613     }
614
615   if(idle_timer.check())
616     {
617     if(is_big())
618       {
619       if(dir == LEFT)
620         tux_body->head->set_action("idle-left", 1);
621       else // dir == RIGHT
622         tux_body->head->set_action("idle-right", 1);
623       }
624
625     }
626
627   // Tux is holding something
628   if ((grabbed_object != 0 && physic.get_velocity_y() == 0) ||
629       (shooting_timer.get_timeleft() > 0 && !shooting_timer.check()))
630     {
631     if (duck)
632       {
633       if(dir == LEFT)
634         tux_body->arms->set_action("duck+grab-left");
635       else // dir == RIGHT
636         tux_body->arms->set_action("duck+grab-right");
637       }
638     else
639       {
640       if(dir == LEFT)
641         tux_body->arms->set_action("grab-left");
642       else // dir == RIGHT
643         tux_body->arms->set_action("grab-right");
644       }
645     }
646
647   /* Draw Tux */
648   if(dying) {
649     smalltux_gameover->draw(context, get_pos(), layer);
650   } else if(growing_timer.get_timeleft() > 0) {
651     if(!is_big())
652       {
653       if (dir == RIGHT)
654         context.draw_surface(growingtux_right[GROWING_FRAMES-1 - 
655                  int((growing_timer.get_timegone() *
656                  GROWING_FRAMES) / GROWING_TIME)], get_pos(), layer);
657       else
658         context.draw_surface(growingtux_left[GROWING_FRAMES-1 - 
659                 int((growing_timer.get_timegone() *
660                 GROWING_FRAMES) / GROWING_TIME)], get_pos(), layer);
661       }
662     else
663       {
664       if (dir == RIGHT)
665         context.draw_surface(growingtux_right[
666             int((growing_timer.get_timegone() *
667                 GROWING_FRAMES) / GROWING_TIME)], get_pos(), layer);
668       else
669         context.draw_surface(growingtux_left[
670             int((growing_timer.get_timegone() *
671                              GROWING_FRAMES) / GROWING_TIME)],
672             get_pos(), layer);
673       }
674     }
675   else if (safe_timer.started() && size_t(game_time*40)%2)
676     ;  // don't draw Tux
677   else
678     tux_body->draw(context, get_pos(), layer);
679
680   // Draw blinking star overlay
681   if (invincible_timer.started() &&
682      (invincible_timer.get_timeleft() > TUX_INVINCIBLE_TIME_WARNING
683       || size_t(game_time*20)%2)
684      && !dying)
685   {
686     if (!is_big() || duck)
687       smalltux_star->draw(context, get_pos(), layer + 5);
688     else
689       bigtux_star->draw(context, get_pos(), layer + 5);
690   } 
691 }
692
693 void
694 Player::collision_tile(uint32_t tile_attributes)
695 {
696   if(tile_attributes & Tile::HURTS)
697     kill(SHRINK);
698 }
699
700 HitResponse
701 Player::collision(GameObject& other, const CollisionHit& hit)
702 {
703   Bullet* bullet = dynamic_cast<Bullet*> (&other);
704   if(bullet) {
705     return FORCE_MOVE;
706   }
707
708   if(other.get_flags() & FLAG_PORTABLE) {
709     Portable* portable = dynamic_cast<Portable*> (&other);
710     if(portable && grabbed_object == 0 && controller->hold(Controller::ACTION)
711         && fabsf(hit.normal.x) > .9) {
712       grabbed_object = portable;
713       return CONTINUE;
714     }
715   }
716  
717   if(other.get_flags() & FLAG_SOLID) {
718     if(hit.normal.y < 0) { // landed on floor?
719       if(physic.get_velocity_y() < 0)
720         physic.set_velocity_y(0);
721       on_ground_flag = true;
722     } else if(hit.normal.y > 0) { // bumped against the roof
723       physic.set_velocity_y(.1);
724     }
725     
726     if(fabsf(hit.normal.x) > .9) { // hit on the side?
727       physic.set_velocity_x(0);
728     }
729
730     return CONTINUE;
731   }
732
733 #ifdef DEBUG
734   assert(dynamic_cast<MovingObject*> (&other) != NULL);
735 #endif
736   MovingObject* moving_object = static_cast<MovingObject*> (&other); 
737   if(moving_object->get_group() == COLGROUP_TOUCHABLE) {
738     TriggerBase* trigger = dynamic_cast<TriggerBase*> (&other);
739     if(trigger) {
740       if(controller->pressed(Controller::UP))
741         trigger->event(*this, TriggerBase::EVENT_ACTIVATE);
742     }
743
744     return FORCE_MOVE;
745   }
746
747   BadGuy* badguy = dynamic_cast<BadGuy*> (&other);
748   if(badguy != NULL)
749     return CONTINUE;
750
751   return FORCE_MOVE;
752 }
753
754 void
755 Player::make_invincible()
756 {
757   sound_manager->play("sounds/invincible.wav");
758   invincible_timer.start(TUX_INVINCIBLE_TIME);
759   Sector::current()->play_music(HERRING_MUSIC);               
760 }
761
762 /* Kill Player! */
763 void
764 Player::kill(HurtMode mode)
765 {
766   if(dying || deactivated)
767     return;
768
769   if(mode != KILL && 
770           (safe_timer.get_timeleft() > 0 || invincible_timer.get_timeleft() > 0))
771     return;                          
772   
773   sound_manager->play("sounds/hurt.wav");
774
775   physic.set_velocity_x(0);
776
777   if (mode == SHRINK && is_big())
778     {
779       if (player_status->bonus == FIRE_BONUS
780           || player_status->bonus == ICE_BONUS)
781         {
782           safe_timer.start(TUX_SAFE_TIME);
783           player_status->bonus = GROWUP_BONUS;
784         }
785       else 
786         {
787           growing_timer.start(GROWING_TIME);
788           safe_timer.start(TUX_SAFE_TIME + GROWING_TIME);
789           bbox.set_height(31.8);
790           duck = false;
791           player_status->bonus = NO_BONUS;
792         }
793     }
794   else
795     {
796       physic.enable_gravity(true);
797       physic.set_acceleration(0, 0);
798       physic.set_velocity(0, 700);
799       player_status->lives -= 1;
800       player_status->bonus = NO_BONUS;
801       dying = true;
802       dying_timer.start(3.0);
803       set_group(COLGROUP_DISABLED);
804
805       DisplayEffect* effect = new DisplayEffect();
806       effect->fade_out(3.0);
807       Sector::current()->add_object(effect);
808       sound_manager->stop_music(3.0);
809     }
810 }
811
812 void
813 Player::move(const Vector& vector)
814 {
815   bbox.set_pos(vector);
816   if(is_big())
817     bbox.set_size(31.8, 63.8);
818   else
819     bbox.set_size(31.8, 31.8);
820   on_ground_flag = false;
821   duck = false;
822   last_ground_y = vector.y;
823
824   physic.reset();
825 }
826
827 void
828 Player::check_bounds(Camera* camera)
829 {
830   /* Keep tux in bounds: */
831   if (get_pos().x < 0)
832     { // Lock Tux to the size of the level, so that he doesn't fall of
833       // on the left side
834       bbox.set_pos(Vector(0, get_pos().y));
835     }
836
837   /* Keep in-bounds, vertically: */
838   if (get_pos().y > Sector::current()->solids->get_height() * 32)
839     {
840       kill(KILL);
841       return;
842     }
843
844   bool adjust = false;
845   // can happen if back scrolling is disabled
846   if(get_pos().x < camera->get_translation().x) {
847     bbox.set_pos(Vector(camera->get_translation().x, get_pos().y));
848     adjust = true;
849   }
850   if(get_pos().x >= camera->get_translation().x + SCREEN_WIDTH - bbox.get_width())
851   {
852     bbox.set_pos(Vector(
853           camera->get_translation().x + SCREEN_WIDTH - bbox.get_width(),
854           get_pos().y));
855     adjust = true;
856   }
857
858   if(adjust) {
859     // FIXME
860 #if 0
861     // squished now?
862     if(collision_object_map(bbox)) {
863       kill(KILL);
864       return;
865     }
866 #endif
867   }
868 }
869
870 void
871 Player::bounce(BadGuy& )
872 {
873   if(controller->hold(Controller::JUMP))
874     physic.set_velocity_y(520);
875   else
876     physic.set_velocity_y(300);
877 }
878
879 //Scripting Functions Below
880
881 void
882 Player::deactivate()
883 {
884   deactivated = true;
885   physic.set_velocity_x(0);
886   physic.set_velocity_y(0);
887 }
888
889 void
890 Player::activate()
891 {
892   deactivated = false;
893 }
894
895 void Player::walk(float speed)
896 {
897   physic.set_velocity_x(speed);
898 }