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