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