somre sound preloading
[supertux.git] / src / object / player.cpp
1 //  $Id$
2 //
3 //  SuperTux
4 //  Copyright (C) 2006 Matthias Braun <matze@braunis.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 "statistics.hpp"
35 #include "game_session.hpp"
36 #include "object/tilemap.hpp"
37 #include "object/camera.hpp"
38 #include "object/particles.hpp"
39 #include "object/portable.hpp"
40 #include "object/bullet.hpp"
41 #include "trigger/trigger_base.hpp"
42 #include "control/joystickkeyboardcontroller.hpp"
43 #include "scripting/squirrel_util.hpp"
44 #include "main.hpp"
45 #include "platform.hpp"
46 #include "badguy/badguy.hpp"
47 #include "player_status.hpp"
48 #include "log.hpp"
49 #include "falling_coin.hpp"
50 #include "random_generator.hpp"
51
52 static const int TILES_FOR_BUTTJUMP = 3;
53 static const float SHOOTING_TIME = .150;
54 /// time before idle animation starts
55 static const float IDLE_TIME = 2.5;
56
57 static const float WALK_ACCELERATION_X = 300;
58 static const float RUN_ACCELERATION_X = 400;
59 static const float SKID_XM = 200;
60 static const float SKID_TIME = .3;
61 static const float MAX_WALK_XM = 230;
62 static const float MAX_RUN_XM = 320;
63 static const float WALK_SPEED = 100;
64
65 static const float KICK_TIME = .3;
66
67 // growing animation
68 Surface* growingtux_left[GROWING_FRAMES];
69 Surface* growingtux_right[GROWING_FRAMES];
70
71 Surface* tux_life = 0;
72
73 TuxBodyParts* small_tux = 0;
74 TuxBodyParts* big_tux = 0;
75 TuxBodyParts* fire_tux = 0;
76 TuxBodyParts* ice_tux = 0;
77
78 void
79 TuxBodyParts::set_action(std::string action, int loops)
80 {
81   if(head != NULL)
82     head->set_action(action, loops);
83   if(body != NULL)
84     body->set_action(action, loops);
85   if(arms != NULL)
86     arms->set_action(action, loops);
87   if(feet != NULL)
88     feet->set_action(action, loops);
89 }
90
91 void
92 TuxBodyParts::draw(DrawingContext& context, const Vector& pos, int layer)
93 {
94   if(head != NULL)
95     head->draw(context, pos, layer-1);
96   if(body != NULL)
97     body->draw(context, pos, layer-3);
98   if(arms != NULL)
99     arms->draw(context, pos, layer+10);
100   if(feet != NULL)
101     feet->draw(context, pos, layer-2);
102 }
103
104 Player::Player(PlayerStatus* _player_status)
105   : player_status(_player_status), grabbed_object(0)
106 {
107   controller = main_controller;
108   smalltux_gameover = sprite_manager->create("images/creatures/tux_small/smalltux-gameover.sprite");
109   smalltux_star = sprite_manager->create("images/creatures/tux_small/smalltux-star.sprite");
110   bigtux_star = sprite_manager->create("images/creatures/tux_big/bigtux-star.sprite");
111
112   sound_manager->preload("sounds/bigjump.wav");
113   sound_manager->preload("sounds/jump.wav");
114   sound_manager->preload("sounds/hurt.wav");
115   sound_manager->preload("sounds/skid.wav");
116
117   init();
118 }
119
120 Player::~Player()
121 {
122   delete smalltux_gameover;
123   delete smalltux_star;
124   delete bigtux_star;
125 }
126
127 void
128 Player::init()
129 {
130   if(is_big())
131     bbox.set_size(31.8, 62.8);
132   else
133     bbox.set_size(31.8, 30.8);
134   adjust_height = 0;
135
136   dir = RIGHT;
137   old_dir = dir;
138   duck = false;
139   dead = false;
140
141   dying = false;
142   last_ground_y = 0;
143   fall_mode = ON_GROUND;
144   jumping = false;
145   can_jump = true;
146   butt_jump = false;
147   deactivated = false;
148   backflipping = false;
149   backflip_direction = 0;
150   visible = true;
151   
152   on_ground_flag = false;
153   grabbed_object = 0;
154
155   floor_normal = Vector(0,-1);
156
157   physic.reset();
158 }
159
160 void
161 Player::expose(HSQUIRRELVM vm, SQInteger table_idx)
162 {
163   Scripting::Player* interface = static_cast<Scripting::Player*> (this);
164   Scripting::expose_object(vm, table_idx, interface, "Tux", false);
165 }
166
167 void
168 Player::unexpose(HSQUIRRELVM vm, SQInteger table_idx)
169 {
170   Scripting::unexpose_object(vm, table_idx, "Tux");
171 }
172
173 void
174 Player::set_controller(Controller* controller)
175 {
176   this->controller = controller;
177 }
178
179 void
180 Player::update(float elapsed_time)
181 {
182   if(dying && dying_timer.check()) {
183     dead = true;
184     return;
185   }
186
187   if(adjust_height != 0) {
188     bbox.move(Vector(0, bbox.get_height() - adjust_height));
189     bbox.set_height(adjust_height);
190     adjust_height = 0;
191   }
192
193   if(!dying && !deactivated)
194     handle_input();
195
196   movement = physic.get_movement(elapsed_time);
197
198   if(grabbed_object != 0 && !dying ) {
199     Vector pos = get_pos() + 
200       Vector(dir == LEFT ? -16 : 16,
201              bbox.get_height()*0.66666 - 32);
202     grabbed_object->grab(*this, pos, dir);
203   }
204
205   on_ground_flag = false;
206 }
207
208 bool
209 Player::on_ground()
210 {
211   return on_ground_flag;
212 }
213
214 bool
215 Player::is_big()
216 {
217   if(player_status->bonus == NO_BONUS)
218     return false;
219
220   return true;
221 }
222
223 void
224 Player::handle_horizontal_input()
225 {
226   float vx = physic.get_velocity_x();
227   float vy = physic.get_velocity_y();
228   float ax = physic.get_acceleration_x();
229   float ay = physic.get_acceleration_y();
230
231   float dirsign = 0;
232   if(!duck || physic.get_velocity_y() != 0) {
233     if(controller->hold(Controller::LEFT) && !controller->hold(Controller::RIGHT)) {
234       old_dir = dir;
235       dir = LEFT;
236       dirsign = -1;
237     } else if(!controller->hold(Controller::LEFT)
238               && controller->hold(Controller::RIGHT)) {
239       old_dir = dir;
240       dir = RIGHT;
241       dirsign = 1;
242     }
243   }
244
245   if (!controller->hold(Controller::ACTION)) {
246     ax = dirsign * WALK_ACCELERATION_X;
247     // limit speed
248     if(vx >= MAX_WALK_XM && dirsign > 0) {
249       vx = MAX_WALK_XM;
250       ax = 0;
251     } else if(vx <= -MAX_WALK_XM && dirsign < 0) {
252       vx = -MAX_WALK_XM;
253       ax = 0;
254     }
255   } else {
256     ax = dirsign * RUN_ACCELERATION_X;
257     // limit speed
258     if(vx >= MAX_RUN_XM && dirsign > 0) {
259       vx = MAX_RUN_XM;
260       ax = 0;
261     } else if(vx <= -MAX_RUN_XM && dirsign < 0) {
262       vx = -MAX_RUN_XM;
263       ax = 0;
264     }
265   }
266
267   // we can reach WALK_SPEED without any acceleration
268   if(dirsign != 0 && fabs(vx) < WALK_SPEED) {
269     vx = dirsign * WALK_SPEED;
270   }
271
272   // changing directions?
273   if(on_ground() && ((vx < 0 && dirsign >0) || (vx>0 && dirsign<0))) {
274     // let's skid!
275     if(fabs(vx)>SKID_XM && !skidding_timer.started()) {
276       skidding_timer.start(SKID_TIME);
277       sound_manager->play("sounds/skid.wav");
278       // dust some particles
279       Sector::current()->add_object(
280         new Particles(
281           Vector(dir == RIGHT ? bbox.p2.x : bbox.p1.x, bbox.p2.y),
282           dir == RIGHT ? 270+20 : 90-40, dir == RIGHT ? 270+40 : 90-20,
283           Vector(280, -260), Vector(0, 300), 3, Color(.4, .4, .4), 3, .8,
284           LAYER_OBJECTS+1));
285       
286       ax *= 2.5;
287     } else {
288       ax *= 2;
289     }
290   }
291
292   // we get slower when not pressing any keys
293   if(dirsign == 0) {
294     if(fabs(vx) < WALK_SPEED) {
295       vx = 0;
296       ax = 0;
297     } else if(vx < 0) {
298       ax = WALK_ACCELERATION_X * 1.5;
299     } else {
300       ax = WALK_ACCELERATION_X * -1.5;
301     }
302   }
303
304 #if 0
305   // if we're on ice slow down acceleration or deceleration
306   if (isice(base.x, base.y + base.height))
307   {
308     /* the acceleration/deceleration rate on ice is inversely proportional to
309      * the current velocity.
310      */
311
312     // increasing 1 will increase acceleration/deceleration rate
313     // decreasing 1 will decrease acceleration/deceleration rate
314     //  must stay above zero, though
315     if (ax != 0) ax *= 1 / fabs(vx);
316   }
317 #endif
318
319   // extend/shrink tux collision rectangle so that we fall through/walk over 1
320   // tile holes
321   if(fabsf(vx) > MAX_WALK_XM) {
322     bbox.set_width(34);
323   } else {
324     bbox.set_width(31.8);
325   }
326
327   // on downward slopes, adjust vertical velocity to match slope angle
328   if (on_ground()) {
329     if (floor_normal.y != 0) {
330       if ((floor_normal.x * vx) > 0) {
331         // we overdo it a little, just to be on the safe side
332         vy = vx * (floor_normal.x / floor_normal.y) * 2;
333       }
334     }
335   }
336
337   physic.set_velocity(vx, vy);
338   physic.set_acceleration(ax, ay);
339 }
340
341 void
342 Player::handle_vertical_input()
343 {
344   // set fall mode...
345   if(on_ground()) {
346     fall_mode = ON_GROUND;
347     last_ground_y = get_pos().y;
348   } else {
349     if(get_pos().y > last_ground_y)
350       fall_mode = FALLING;
351     else if(fall_mode == ON_GROUND)
352       fall_mode = JUMPING;
353   }
354
355   if(on_ground()) { /* Make sure jumping is off. */
356     jumping = false;
357     if (backflipping) {
358       backflipping = false;
359       backflip_direction = 0;
360     }
361   }
362
363   // Press jump key
364   if(controller->pressed(Controller::JUMP) && can_jump && on_ground()) {
365     if (duck) { 
366       if (physic.get_velocity_x() != 0) // only jump a little bit when running ducked
367         physic.set_velocity_y(300);
368       else { //do a backflip
369         backflipping = true;
370         physic.set_velocity_y(580);
371         backflip_timer.start(0.15);
372       }
373     }
374     else if (fabs(physic.get_velocity_x()) > MAX_WALK_XM) // jump higher if we are running
375       physic.set_velocity_y(580);
376     else
377       physic.set_velocity_y(520);
378     
379     //bbox.move(Vector(0, -1));
380     jumping = true;
381     can_jump = false;
382     if (is_big())
383       sound_manager->play("sounds/bigjump.wav");
384     else
385       sound_manager->play("sounds/jump.wav");
386   } else if(!controller->hold(Controller::JUMP)) { // Let go of jump key
387     if (!backflipping && jumping && physic.get_velocity_y() > 0) {
388       jumping = false;
389       physic.set_velocity_y(0);
390     }
391   }
392
393   /* In case the player has pressed Down while in a certain range of air,
394      enable butt jump action */
395   if (controller->hold(Controller::DOWN) && !butt_jump && !duck)
396     //if(tiles_on_air(TILES_FOR_BUTTJUMP) && jumping)
397     butt_jump = true;
398   
399   /* When Down is not held anymore, disable butt jump */
400   if(butt_jump && !controller->hold(Controller::DOWN))
401     butt_jump = false;
402   
403 #if 0
404   // Do butt jump
405   if (butt_jump && on_ground() && is_big()) {
406     // Add a smoke cloud
407     if (duck) 
408       Sector::current()->add_smoke_cloud(Vector(get_pos().x - 32, get_pos().y));
409     else 
410       Sector::current()->add_smoke_cloud(
411         Vector(get_pos().x - 32, get_pos().y + 32));
412     
413     butt_jump = false;
414     
415     // Break bricks beneath Tux
416     if(Sector::current()->trybreakbrick(
417          Vector(base.x + 1, base.y + base.height), false)
418        || Sector::current()->trybreakbrick(
419          Vector(base.x + base.width - 1, base.y + base.height), false)) {
420       physic.set_velocity_y(2);
421       butt_jump = true;
422     }
423     
424     // Kill nearby badguys
425     std::vector<GameObject*> gameobjects = Sector::current()->gameobjects;
426     for (std::vector<GameObject*>::iterator i = gameobjects.begin();
427          i != gameobjects.end();
428          i++) {
429       BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
430       if(badguy) {
431         // don't kill when badguys are already dying or in a certain mode
432         if(badguy->dying == DYING_NOT && badguy->mode != BadGuy::BOMB_TICKING &&
433            badguy->mode != BadGuy::BOMB_EXPLODE) {
434           if (fabsf(base.x - badguy->base.x) < 96 &&
435               fabsf(base.y - badguy->base.y) < 64)
436             badguy->kill_me(25);
437         }
438       }
439     }
440   }
441 #endif
442
443   /** jumping is only allowed if we're about to touch ground soon and if the
444    * button has been up in between the last jump
445    */
446   // FIXME
447 #if 0
448   if ( (issolid(get_pos().x + bbox.get_width() / 2,
449           get_pos().y + bbox.get_height() + 64) ||
450         issolid(get_pos().x + 1, get_pos().y + bbox.get_height() + 64) ||
451         issolid(get_pos().x + bbox.get_width() - 1,
452           get_pos().y + bbox.get_height() + 64))
453        && jumping  == false
454        && can_jump == false
455        && input.jump && !input.old_jump)
456     {
457       can_jump = true;
458     }
459 #endif
460 }
461
462 void
463 Player::handle_input()
464 {
465   if(!controller->hold(Controller::ACTION) && grabbed_object) {
466     // move the grabbed object a bit away from tux
467     Vector pos = get_pos() + 
468         Vector(dir == LEFT ? -bbox.get_width()-1 : bbox.get_width()+1,
469                 bbox.get_height()*0.66666 - 32);
470     Rect dest(pos, pos + Vector(32, 32));
471     if(Sector::current()->is_free_space(dest)) {
472       MovingObject* moving_object = dynamic_cast<MovingObject*> (grabbed_object);
473       if(moving_object) {
474         moving_object->set_pos(pos);
475       } else {
476         log_debug << "Non MovingObjetc grabbed?!?" << std::endl;
477       }
478       grabbed_object->ungrab(*this, dir);
479       grabbed_object = 0;
480     }
481   }
482  
483   /* Handle horizontal movement: */
484   if (!backflipping) handle_horizontal_input();
485   else {
486     if (backflip_direction == 0) {
487       dir == LEFT ? backflip_direction = 1 : backflip_direction = -1;
488     }
489     else backflip_direction == 1 ? dir = LEFT : dir = RIGHT; //prevent player from changing direction when backflipping 
490     if (backflip_timer.check()) physic.set_velocity_x(100 * backflip_direction);
491   }
492
493
494   /* Jump/jumping? */
495   if (on_ground() && !controller->hold(Controller::JUMP))
496     can_jump = true;
497   handle_vertical_input();
498
499   /* Shoot! */
500   if (controller->pressed(Controller::ACTION) && player_status->bonus == FIRE_BONUS) {
501     if(Sector::current()->add_bullet(
502          get_pos() + ((dir == LEFT)? Vector(0, bbox.get_height()/2) 
503                       : Vector(32, bbox.get_height()/2)),
504          physic.get_velocity_x(), dir))
505       shooting_timer.start(SHOOTING_TIME);
506   }
507   
508   /* Duck! */
509   if (controller->hold(Controller::DOWN) && is_big() && !duck 
510       && physic.get_velocity_y() == 0 && on_ground()) {
511     duck = true;
512     bbox.move(Vector(0, 32));
513     bbox.set_height(31.8);
514   } else if(!controller->hold(Controller::DOWN) && is_big() && duck) {
515     // if we have some velocity left then check if there is space for
516     // unducking
517     bbox.move(Vector(0, -32));
518     bbox.set_height(63.8);
519     if(Sector::current()->is_free_space(bbox) || (
520         physic.get_velocity_x() > -.01 && physic.get_velocity_x() < .01
521         && physic.get_velocity_y() > -.01 && physic.get_velocity_y() < .01))
522     {
523       duck = false;
524     } else {
525       // undo the ducking changes
526       bbox.move(Vector(0, 32));
527       bbox.set_height(31.8); 
528     }
529   }
530 }
531
532 void
533 Player::add_coins(int count)
534 {
535   player_status->add_coins(count);
536 }
537
538 void
539 Player::add_bonus(const std::string& bonustype)
540 {
541   if(bonustype == "grow") {
542     add_bonus(GROWUP_BONUS);
543   } else if(bonustype == "fireflower") {
544     add_bonus(FIRE_BONUS);
545   } else if(bonustype == "iceflower") {
546     add_bonus(ICE_BONUS);
547   } else if(bonustype == "none") {
548     add_bonus(NO_BONUS);
549   } else {
550     std::ostringstream msg;
551     msg << "Unknown bonus type "  << bonustype;
552     throw std::runtime_error(msg.str());
553   }
554 }
555
556 void
557 Player::add_bonus(BonusType type, bool animate)
558 {
559   // always ignore NO_BONUS
560   if (type == NO_BONUS) {
561     return;
562   }
563
564   // ignore GROWUP_BONUS if we're already big
565   if (type == GROWUP_BONUS) {
566     if (player_status->bonus == GROWUP_BONUS) return; 
567     if (player_status->bonus == FIRE_BONUS) return;
568     if (player_status->bonus == ICE_BONUS) return;
569   }
570
571   set_bonus(type, animate);
572 }
573
574 void
575 Player::set_bonus(BonusType type, bool animate)
576 {
577   if(player_status->bonus == NO_BONUS) {
578     adjust_height = 62.8;
579     if(animate)
580       growing_timer.start(GROWING_TIME);
581   }
582
583   if ((type == NO_BONUS) || (type == GROWUP_BONUS)) {
584     player_status->max_fire_bullets = 0;
585     player_status->max_ice_bullets = 0;
586   }
587   if (type == FIRE_BONUS) player_status->max_fire_bullets++;
588   if (type == ICE_BONUS) player_status->max_ice_bullets++;
589
590   player_status->bonus = type;
591 }
592
593 void
594 Player::set_visible(bool visible)
595 {
596   this->visible = visible;
597 }
598
599 bool
600 Player::get_visible()
601 {
602   return visible;
603 }
604
605 void
606 Player::kick()
607 {
608   kick_timer.start(KICK_TIME);
609 }
610
611 void
612 Player::draw(DrawingContext& context)
613 {
614   if(!visible)
615     return;
616
617   TuxBodyParts* tux_body;
618           
619   if (player_status->bonus == GROWUP_BONUS)
620     tux_body = big_tux;
621   else if (player_status->bonus == FIRE_BONUS)
622     tux_body = fire_tux;
623   else if (player_status->bonus == ICE_BONUS)
624     tux_body = ice_tux;
625   else
626     tux_body = small_tux;
627
628   int layer = LAYER_OBJECTS + 1;
629
630   /* Set Tux sprite action */
631   if (duck && is_big())
632     {
633     if(dir == LEFT)
634       tux_body->set_action("duck-left");
635     else // dir == RIGHT
636       tux_body->set_action("duck-right");
637     }
638   else if (skidding_timer.started() && !skidding_timer.check())
639     {
640     if(dir == LEFT)
641       tux_body->set_action("skid-left");
642     else // dir == RIGHT
643       tux_body->set_action("skid-right");
644     }
645   else if (kick_timer.started() && !kick_timer.check())
646     {
647     if(dir == LEFT)
648       tux_body->set_action("kick-left");
649     else // dir == RIGHT
650       tux_body->set_action("kick-right");
651     }
652   else if (butt_jump && is_big())
653     {
654     if(dir == LEFT)
655       tux_body->set_action("buttjump-left");
656     else // dir == RIGHT
657       tux_body->set_action("buttjump-right");
658     }
659   else if (!on_ground())
660     {
661     if(dir == LEFT)
662       tux_body->set_action("jump-left");
663     else // dir == RIGHT
664       tux_body->set_action("jump-right");
665     }
666   else
667     {
668     if (fabsf(physic.get_velocity_x()) < 1.0f) // standing
669       {
670       if(dir == LEFT)
671         tux_body->set_action("stand-left");
672       else // dir == RIGHT
673         tux_body->set_action("stand-right");
674       }
675     else // moving
676       {
677       if(dir == LEFT)
678         tux_body->set_action("walk-left");
679       else // dir == RIGHT
680         tux_body->set_action("walk-right");
681       }
682     }
683
684   if(idle_timer.check())
685     {
686     if(is_big())
687       {
688       if(dir == LEFT)
689         tux_body->head->set_action("idle-left", 1);
690       else // dir == RIGHT
691         tux_body->head->set_action("idle-right", 1);
692       }
693
694     }
695
696   // Tux is holding something
697   if ((grabbed_object != 0 && physic.get_velocity_y() == 0) ||
698       (shooting_timer.get_timeleft() > 0 && !shooting_timer.check()))
699     {
700     if (duck)
701       {
702       if(dir == LEFT)
703         tux_body->arms->set_action("duck+grab-left");
704       else // dir == RIGHT
705         tux_body->arms->set_action("duck+grab-right");
706       }
707     else
708       {
709       if(dir == LEFT)
710         tux_body->arms->set_action("grab-left");
711       else // dir == RIGHT
712         tux_body->arms->set_action("grab-right");
713       }
714     }
715
716   /* Draw Tux */
717   if(dying) {
718     smalltux_gameover->draw(context, get_pos(), LAYER_FLOATINGOBJECTS + 1);
719   } else if(growing_timer.get_timeleft() > 0) {
720       if (dir == RIGHT) {
721         context.draw_surface(growingtux_right[int((growing_timer.get_timegone() *
722                  GROWING_FRAMES) / GROWING_TIME)], get_pos() - Vector(0, 32), layer);
723       } else {
724         context.draw_surface(growingtux_left[int((growing_timer.get_timegone() *
725                 GROWING_FRAMES) / GROWING_TIME)], get_pos() - Vector(0, 32), layer);
726       }
727     }
728   else if (safe_timer.started() && size_t(game_time*40)%2)
729     ;  // don't draw Tux
730   else
731     tux_body->draw(context, get_pos(), layer);
732
733   // Draw blinking star overlay
734   if (invincible_timer.started() &&
735      (invincible_timer.get_timeleft() > TUX_INVINCIBLE_TIME_WARNING
736       || size_t(game_time*20)%2)
737      && !dying)
738   {
739     if (!is_big() || duck)
740       smalltux_star->draw(context, get_pos(), layer + 5);
741     else
742       bigtux_star->draw(context, get_pos(), layer + 5);
743   } 
744 }
745
746 void
747 Player::collision_tile(uint32_t tile_attributes)
748 {
749   if(tile_attributes & Tile::HURTS)
750     kill(false);
751 }
752
753 HitResponse
754 Player::collision(GameObject& other, const CollisionHit& hit)
755 {
756   Bullet* bullet = dynamic_cast<Bullet*> (&other);
757   if(bullet) {
758     return FORCE_MOVE;
759   }
760
761   if(other.get_flags() & FLAG_PORTABLE) {
762     Portable* portable = dynamic_cast<Portable*> (&other);
763     if(portable && grabbed_object == 0 && controller->hold(Controller::ACTION)
764         && fabsf(hit.normal.x) > .9) {
765       grabbed_object = portable;
766       return CONTINUE;
767     }
768   }
769  
770   if(other.get_flags() & FLAG_SOLID) {
771     /*
772     printf("Col %p: HN: %3.1f %3.1f D %.1f P: %3.1f %3.1f M: %3.1f %3.1f\n",
773         &other,
774         hit.normal.x, hit.normal.y, hit.depth,
775         get_pos().x, get_pos().y,
776         movement.x, movement.y);
777     */
778     
779     if(hit.normal.y < 0) { // landed on floor?
780       if(physic.get_velocity_y() < 0)
781         physic.set_velocity_y(0);
782
783       on_ground_flag = true;
784
785       // remember normal of this tile
786       if (hit.normal.y > -0.9) {
787         floor_normal.x = hit.normal.x;
788         floor_normal.y = hit.normal.y;
789       } else {
790         // slowly adjust to unisolid tiles. 
791         // Necessary because our bounding box sometimes reaches through slopes and thus hits unisolid tiles
792         floor_normal.x = (floor_normal.x * 0.9) + (hit.normal.x * 0.1);
793         floor_normal.y = (floor_normal.y * 0.9) + (hit.normal.y * 0.1);
794       }
795
796       // hack platforms so that we stand normally on them when going down...
797       Platform* platform = dynamic_cast<Platform*> (&other);
798       if(platform != NULL) {
799         if(platform->get_speed().y > 0)
800           physic.set_velocity_y(-platform->get_speed().y);
801         //physic.set_velocity_x(platform->get_speed().x);
802       }
803     } else if(hit.normal.y > 0) { // bumped against the roof
804       physic.set_velocity_y(.1);
805
806       // hack platform so that we are not glued to it from below
807       Platform* platform = dynamic_cast<Platform*> (&other);
808       if(platform != NULL) {
809         physic.set_velocity_y(-platform->get_speed().y);
810       }      
811     }
812     
813     if(fabsf(hit.normal.x) > .9) { // hit on the side?
814       physic.set_velocity_x(0);
815     }
816
817     MovingObject* omov = dynamic_cast<MovingObject*> (&other);
818     if(omov != NULL) {
819       Vector mov = movement - omov->get_movement();
820       /*
821       printf("W %p - HITN: %3.1f %3.1f D:%3.1f TM: %3.1f %3.1f TD: %3.1f %3.1f PM: %3.2f %3.1f\n",
822           omov,
823           hit.normal.x, hit.normal.y,
824           hit.depth,
825           movement.x, movement.y,
826           dest.p1.x, dest.p1.y,
827           omov->get_movement().x, omov->get_movement().y);
828       */
829     }
830     
831     return CONTINUE;
832   }
833
834 #ifdef DEBUG
835   assert(dynamic_cast<MovingObject*> (&other) != NULL);
836 #endif
837   MovingObject* moving_object = static_cast<MovingObject*> (&other); 
838   if(moving_object->get_group() == COLGROUP_TOUCHABLE) {
839     TriggerBase* trigger = dynamic_cast<TriggerBase*> (&other);
840     if(trigger) {
841       if(controller->pressed(Controller::UP))
842         trigger->event(*this, TriggerBase::EVENT_ACTIVATE);
843     }
844
845     return FORCE_MOVE;
846   }
847
848   BadGuy* badguy = dynamic_cast<BadGuy*> (&other);
849   if(badguy != NULL) {
850     if(safe_timer.started())
851       return FORCE_MOVE;
852
853     return CONTINUE;
854   }
855
856   return FORCE_MOVE;
857 }
858
859 void
860 Player::make_invincible()
861 {
862   sound_manager->play("sounds/invincible.wav");
863   invincible_timer.start(TUX_INVINCIBLE_TIME);
864   Sector::current()->play_music(HERRING_MUSIC);               
865 }
866
867 /* Kill Player! */
868 void
869 Player::kill(bool completely)
870 {
871   if(dying || deactivated)
872     return;
873
874   if(!completely && 
875       (safe_timer.get_timeleft() > 0 || invincible_timer.get_timeleft() > 0))
876     return;                          
877   
878   sound_manager->play("sounds/hurt.wav");
879
880   physic.set_velocity_x(0);
881
882   if(!completely && is_big()) {
883     if(player_status->bonus == FIRE_BONUS
884         || player_status->bonus == ICE_BONUS) {
885       safe_timer.start(TUX_SAFE_TIME);
886       set_bonus(GROWUP_BONUS);
887     } else {
888       //growing_timer.start(GROWING_TIME);
889       safe_timer.start(TUX_SAFE_TIME /* + GROWING_TIME */);
890       adjust_height = 30.8;
891       duck = false;
892       set_bonus(NO_BONUS);
893     }
894   } else {
895     for (int i = 0; (i < 5) && (i < player_status->coins); i++)
896     {
897       // the numbers: starting x, starting y, velocity y
898       Sector::current()->add_object(new FallingCoin(get_pos() + 
899             Vector(systemRandom.rand(5), systemRandom.rand(-32,18)), 
900             systemRandom.rand(-100,100)));
901     }
902     physic.enable_gravity(true);
903     physic.set_acceleration(0, 0);
904     physic.set_velocity(0, 700);
905     player_status->coins -= 25;
906     set_bonus(NO_BONUS);
907     dying = true;
908     dying_timer.start(3.0);
909     set_group(COLGROUP_DISABLED);
910
911     DisplayEffect* effect = new DisplayEffect();
912     effect->fade_out(3.0);
913     Sector::current()->add_object(effect);
914     sound_manager->stop_music(3.0);
915   }
916 }
917
918 void
919 Player::move(const Vector& vector)
920 {
921   bbox.set_pos(vector);
922   if(is_big())
923     bbox.set_size(31.8, 63.8);
924   else
925     bbox.set_size(31.8, 31.8);
926   duck = false;
927   last_ground_y = vector.y;
928
929   physic.reset();
930 }
931
932 void
933 Player::check_bounds(Camera* camera)
934 {
935   /* Keep tux in bounds: */
936   if (get_pos().x < 0) {
937     // Lock Tux to the size of the level, so that he doesn't fall of
938     // on the left side
939     bbox.set_pos(Vector(0, get_pos().y));
940   }
941
942   /* Keep in-bounds, vertically: */
943   if (get_pos().y > Sector::current()->solids->get_height() * 32) {
944     kill(true);
945     return;
946   }
947
948   bool adjust = false;
949   // can happen if back scrolling is disabled
950   if(get_pos().x < camera->get_translation().x) {
951     bbox.set_pos(Vector(camera->get_translation().x, get_pos().y));
952     adjust = true;
953   }
954   if(get_pos().x >= camera->get_translation().x + SCREEN_WIDTH - bbox.get_width())
955   {
956     bbox.set_pos(Vector(
957           camera->get_translation().x + SCREEN_WIDTH - bbox.get_width(),
958           get_pos().y));
959     adjust = true;
960   }
961
962   if(adjust) {
963     // FIXME
964 #if 0
965     // squished now?
966     if(collision_object_map(bbox)) {
967       kill(KILL);
968       return;
969     }
970 #endif
971   }
972 }
973
974 void
975 Player::add_velocity(const Vector& velocity)
976 {
977   physic.set_velocity(physic.get_velocity() + velocity);
978 }
979
980 void
981 Player::bounce(BadGuy& )
982 {
983   if(controller->hold(Controller::JUMP))
984     physic.set_velocity_y(520);
985   else
986     physic.set_velocity_y(300);
987 }
988
989 //Scripting Functions Below
990
991 void
992 Player::deactivate()
993 {
994   deactivated = true;
995   physic.set_velocity_x(0);
996   physic.set_velocity_y(0);
997   physic.set_acceleration_x(0);
998   physic.set_acceleration_y(0);
999 }
1000
1001 void
1002 Player::activate()
1003 {
1004   deactivated = false;
1005 }
1006
1007 void Player::walk(float speed)
1008 {
1009   physic.set_velocity_x(speed);
1010 }
1011