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