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