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