9200ae6ceefa1addabcfb947fc04c2753c32739f
[supertux.git] / src / player.cpp
1 //  $Id$
2 //
3 //  SuperTux -  A Jump'n Run
4 //  Copyright (C) 2003 Tobias Glaesser <tobi.web@gmx.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 "app/globals.h"
28 #include "app/gettext.h"
29 #include "player.h"
30 #include "defines.h"
31 #include "scene.h"
32 #include "tile.h"
33 #include "special/sprite.h"
34 #include "sector.h"
35 #include "tilemap.h"
36 #include "camera.h"
37 #include "gameobjs.h"
38 #include "resources.h"
39 #include "video/screen.h"
40 #include "statistics.h"
41 #include "gameloop.h"
42 #include "trigger/trigger_base.h"
43
44 // behavior definitions:
45 #define TILES_FOR_BUTTJUMP 3
46 // animation times (in ms):
47 #define SHOOTING_TIME .150
48
49 // time before idle animation starts
50 #define IDLE_TIME 2.500
51
52 // growing animation
53 Surface* growingtux_left[GROWING_FRAMES];
54 Surface* growingtux_right[GROWING_FRAMES];
55
56 Surface* tux_life = 0;
57
58 Sprite* smalltux_gameover = 0;
59 Sprite* smalltux_star = 0;
60 Sprite* bigtux_star = 0;
61
62 TuxBodyParts* small_tux = 0;
63 TuxBodyParts* big_tux = 0;
64 TuxBodyParts* fire_tux = 0;
65 TuxBodyParts* ice_tux = 0;
66
67 PlayerKeymap keymap;
68
69 PlayerKeymap::PlayerKeymap()
70 {
71   keymap.up    = SDLK_UP;
72   keymap.down  = SDLK_DOWN;
73   keymap.left  = SDLK_LEFT;
74   keymap.right = SDLK_RIGHT;
75
76   keymap.power = SDLK_LCTRL;
77   keymap.jump  = SDLK_LALT;
78 }
79
80 void player_input_init(player_input_type* pplayer_input)
81 {
82   pplayer_input->up = UP;
83   pplayer_input->old_up = UP;
84   pplayer_input->down = UP;
85   pplayer_input->fire = UP;
86   pplayer_input->left = UP;
87   pplayer_input->old_fire = UP;
88   pplayer_input->right = UP;
89   pplayer_input->jump = UP;
90   pplayer_input->old_jump = UP;
91   pplayer_input->activate = UP;
92 }
93
94 void
95 TuxBodyParts::set_action(std::string action)
96 {
97   if(head != NULL)
98     head->set_action(action);
99   if(body != NULL)
100     body->set_action(action);
101   if(arms != NULL)
102     arms->set_action(action);
103   if(feet != NULL)
104     feet->set_action(action);
105 }
106
107 void
108 TuxBodyParts::one_time_animation()
109 {
110   if(head != NULL)
111     head->start_animation(1);
112   if(body != NULL)
113     body->start_animation(1);
114   if(arms != NULL)
115     arms->start_animation(1);
116   if(feet != NULL)
117     feet->start_animation(1);
118 }
119
120 void
121 TuxBodyParts::draw(DrawingContext& context, const Vector& pos, int layer,
122                   Uint32 drawing_effect)
123 {
124   if(head != NULL)
125     head->draw(context, pos, layer-1, drawing_effect);
126   if(body != NULL)
127     body->draw(context, pos, layer-3, drawing_effect);
128   if(arms != NULL)
129     arms->draw(context, pos, layer,   drawing_effect);
130   if(feet != NULL)
131     feet->draw(context, pos, layer-2, drawing_effect);
132 }
133
134 Player::Player()
135 {
136   init();
137 }
138
139 Player::~Player()
140 {
141 }
142
143 void
144 Player::init()
145 {
146   holding_something = false;
147
148   bbox.set_size(32, 32);
149
150   size = SMALL;
151   got_power = NONE_POWER;
152
153   dir = RIGHT;
154   old_dir = dir;
155   duck = false;
156   dead = false;
157
158   dying   = DYING_NOT;
159   last_ground_y = 0;
160   fall_mode = ON_GROUND;
161   jumping = false;
162   flapping = false;
163   can_jump = true;
164   can_flap = false;
165   falling_from_flap = false;
166   enable_hover = false;
167   butt_jump = false;
168   
169   flapping_velocity = 0;
170
171   // temporary to help player's choosing a flapping
172   flapping_mode = MAREK_FLAP;
173
174   // Ricardo's flapping
175   flaps_nb = 0;
176
177   on_ground_flag = false;
178
179   frame_main = 0;
180   frame_ = 0;
181
182   player_input_init(&input);
183
184   frame_timer.start(.025, true);
185
186   physic.reset();
187 }
188
189 int
190 Player::key_event(SDLKey key, int state)
191 {
192   idle_timer.start(IDLE_TIME, true);
193
194   if(key == keymap.right)
195     {
196       input.right = state;
197       return true;
198     }
199   else if(key == keymap.left)
200     {
201       input.left = state;
202       return true;
203     }
204   else if(key == keymap.up)
205     {
206       if(state == UP)
207         input.old_up = UP;
208       input.up = state;
209       /* Up key also opens activates stuff */
210       input.activate = state;
211       return true;
212     }
213   else if(key == keymap.down)
214     {
215       input.down = state;
216       return true;
217     }
218   else if(key == keymap.power)
219     {
220       if (state == UP)
221         input.old_fire = UP;
222       input.fire = state;
223
224       return true;
225     }
226   else if(key == keymap.jump)
227     {
228       if (state == UP)
229         input.old_jump = UP;
230       input.jump = state;
231       return true;
232     }
233   else
234     return false;
235 }
236
237 void
238 Player::level_begin()
239 {
240   move(Vector(100, 170));
241   duck = false;
242
243   dying = DYING_NOT;
244
245   player_input_init(&input);
246
247   on_ground_flag = false;
248
249   physic.reset();
250 }
251
252 PlayerStatus&
253 Player::get_status()
254 {
255   return player_status;
256 }
257
258 void
259 Player::action(float elapsed_time)
260 {
261   if(dying && dying_timer.check()) {
262     dead = true;
263     return;
264   }
265
266   if (input.fire == UP)
267     holding_something = false;
268
269   /* --- HANDLE TUX! --- */
270   if(dying == DYING_NOT)
271     handle_input();
272
273   movement = physic.get_movement(elapsed_time);
274
275   if(dying == DYING_NOT) 
276     {
277 #if 0
278       collision_swept_object_map(&old_base, &base);
279
280       if ((!invincible_timer.started() && !safe_timer.started())
281           && (isspike(base.x, base.y) || isspike(base.x + base.width, base.y)
282           ||  isspike(base.x, base.y + base.height)
283           ||  isspike(base.x + base.width, base.y + base.height)))
284       {
285          kill(SHRINK);
286       }
287
288       // Don't accelerate Tux if he is running against a wall
289       if (target.x != base.x)
290       {
291         physic.set_velocity_x(0);
292       }
293
294       // special exception for cases where we're stuck under tiles after
295       // being ducked. In this case we drift out
296       if(!duck && on_ground() && old_base.x == base.x && old_base.y == base.y
297          && collision_object_map(base))
298         {
299           base.x += elapsed_time * WALK_SPEED * (dir ? 1: -1);
300           previous_base = old_base = base;
301         }
302
303       // Land:
304       if (!on_ground())
305         {
306           physic.enable_gravity(true);
307           if(under_solid())
308             {
309               // fall down
310               physic.set_velocity_y(0);
311               jumped_in_solid = true;
312               jumping = false;
313               flapping = false;
314             }
315         }
316       else
317         {
318           /* Land: */
319           if (physic.get_velocity_y() < 0)
320             {
321               base.y = (int)(((int)base.y / 32) * 32);
322               physic.set_velocity_y(0);
323             }
324
325           physic.enable_gravity(false);
326           /* Reset score multiplier (for multi-hits): */
327           if (!invincible_timer.started())
328             {
329             if(player_status.score_multiplier > player_status.max_score_multiplier)
330               {
331               player_status.max_score_multiplier = player_status.score_multiplier;
332
333               // show a message
334               char str[124];
335               sprintf(str, _("New max combo: %d"), player_status.max_score_multiplier-1);
336               Sector::current()->add_floating_text(base, str);
337               }
338             player_status.score_multiplier = 1;
339             }
340         }
341
342       if(jumped_in_solid)
343         {
344           if (isbrick(base.x, base.y) ||
345               isfullbox(base.x, base.y))
346             {
347               Sector::current()->trygrabdistro(
348                   Vector(base.x, base.y - 32), BOUNCE);
349               Sector::current()->trybumpbadguy(Vector(base.x, base.y - 64));
350
351               Sector::current()->trybreakbrick(
352                   Vector(base.x, base.y), size == SMALL);
353
354               bumpbrick(base.x, base.y);
355               Sector::current()->tryemptybox(Vector(base.x, base.y), RIGHT);
356             }
357
358           if (isbrick(base.x+ 31, base.y) ||
359               isfullbox(base.x+ 31, base.y))
360             {
361               Sector::current()->trygrabdistro(
362                   Vector(base.x+ 31, base.y - 32), BOUNCE);
363               Sector::current()->trybumpbadguy(Vector(base.x+ 31, base.y - 64));
364
365               if(size == BIG)
366                 Sector::current()->trybreakbrick(
367                     Vector(base.x+ 31, base.y), size == SMALL);
368
369               bumpbrick(base.x+ 31, base.y);
370               Sector::current()->tryemptybox(Vector(base.x+ 31, base.y), LEFT);
371             }
372         }
373 #endif
374
375       grabdistros();
376
377 #if 0
378       if (jumped_in_solid)
379         {
380           ++base.y;
381           ++old_base.y;
382           if(on_ground())
383             {
384               /* Make sure jumping is off. */
385               jumping = false;
386               flapping = false;
387             }
388         }
389 #endif
390     }
391
392   on_ground_flag = false;
393 }
394
395 bool
396 Player::on_ground()
397 {
398   return on_ground_flag;
399 }
400
401 void
402 Player::handle_horizontal_input()
403 {
404   float vx = physic.get_velocity_x();
405   float vy = physic.get_velocity_y();
406   float ax = physic.get_acceleration_x();
407   float ay = physic.get_acceleration_y();
408
409   float dirsign = 0;
410   if(input.left == DOWN && input.right == UP && (!duck || physic.get_velocity_y() != 0)) {
411       old_dir = dir;
412       dir = LEFT;
413       dirsign = -1;
414   } else if(input.left == UP && input.right == DOWN && (!duck || physic.get_velocity_y() != 0)) {
415       old_dir = dir;
416       dir = RIGHT;
417       dirsign = 1;
418   }
419
420   if (input.fire == UP) {
421       ax = dirsign * WALK_ACCELERATION_X;
422       // limit speed
423       if(vx >= MAX_WALK_XM && dirsign > 0) {
424         vx = MAX_WALK_XM;
425         ax = 0;
426       } else if(vx <= -MAX_WALK_XM && dirsign < 0) {
427         vx = -MAX_WALK_XM;
428         ax = 0;
429       }
430   } else {
431       ax = dirsign * RUN_ACCELERATION_X;
432       // limit speed
433       if(vx >= MAX_RUN_XM && dirsign > 0) {
434         vx = MAX_RUN_XM;
435         ax = 0;
436       } else if(vx <= -MAX_RUN_XM && dirsign < 0) {
437         vx = -MAX_RUN_XM;
438         ax = 0;
439       }
440   }
441
442   // we can reach WALK_SPEED without any acceleration
443   if(dirsign != 0 && fabs(vx) < WALK_SPEED) {
444     vx = dirsign * WALK_SPEED;
445   }
446
447   // changing directions?
448   if(on_ground() && ((vx < 0 && dirsign >0) || (vx>0 && dirsign<0)))
449     {
450       // let's skid!
451       if(fabs(vx)>SKID_XM && !skidding_timer.started())
452         {
453           skidding_timer.start(SKID_TIME);
454           SoundManager::get()->play_sound(IDToSound(SND_SKID));
455           // dust some partcles
456           Sector::current()->add_particles(
457               Vector(bbox.p1.x + (dir == RIGHT ? bbox.get_width() : 0),
458                 bbox.p2.y),
459               dir == RIGHT ? 270+20 : 90-40, dir == RIGHT ? 270+40 : 90-20,
460               Vector(280,-260), Vector(0,0.030), 3, Color(100,100,100), 3, 800,
461               LAYER_OBJECTS+1);
462
463           ax *= 2.5;
464         }
465       else
466         {
467           ax *= 2;
468         }
469     }
470
471   // we get slower when not pressing any keys
472   if(dirsign == 0) {
473       if(fabs(vx) < WALK_SPEED) {
474           vx = 0;
475           ax = 0;
476       } else if(vx < 0) {
477           ax = WALK_ACCELERATION_X * 1.5;
478       } else {
479           ax = WALK_ACCELERATION_X * -1.5;
480       }
481   }
482
483 #if 0
484   // if we're on ice slow down acceleration or deceleration
485   if (isice(base.x, base.y + base.height))
486   {
487     /* the acceleration/deceleration rate on ice is inversely proportional to
488      * the current velocity.
489      */
490
491     // increasing 1 will increase acceleration/deceleration rate
492     // decreasing 1 will decrease acceleration/deceleration rate
493     //  must stay above zero, though
494     if (ax != 0) ax *= 1 / fabs(vx);
495   }
496 #endif
497
498   physic.set_velocity(vx, vy);
499   physic.set_acceleration(ax, ay);
500 }
501
502 void
503 Player::handle_vertical_input()
504 {
505   // set fall mode...
506   if(on_ground()) {
507     fall_mode = ON_GROUND;
508     last_ground_y = get_pos().y;
509   } else {
510     if(get_pos().y > last_ground_y)
511       fall_mode = FALLING;
512     else if(fall_mode == ON_GROUND)
513       fall_mode = JUMPING;
514   }
515
516   // Press jump key
517   if(input.jump == DOWN && can_jump && on_ground())
518     {
519       if(duck) { // only jump a little bit when in duck mode {
520         physic.set_velocity_y(300);
521       } else {
522         // jump higher if we are running
523         if (fabs(physic.get_velocity_x()) > MAX_WALK_XM)
524           physic.set_velocity_y(580);
525         else
526           physic.set_velocity_y(520);
527       }
528
529       //bbox.move(Vector(0, -1));
530       jumping = true;
531       flapping = false;
532       can_jump = false;
533       can_flap = false;
534       flaps_nb = 0; // Ricardo's flapping
535       if (size == SMALL)
536         SoundManager::get()->play_sound(IDToSound(SND_JUMP));
537       else
538         SoundManager::get()->play_sound(IDToSound(SND_BIGJUMP));
539     }
540   // Let go of jump key
541   else if(input.jump == UP)
542     {
543       if (!flapping && !duck && !falling_from_flap && !on_ground())
544          {
545             can_flap = true;
546          }
547       if (jumping && physic.get_velocity_y() > 0)
548          {
549             jumping = false;
550             physic.set_velocity_y(0);
551          }
552     }
553
554  // temporary to help player's choosing a flapping
555  if(flapping_mode == RICARDO_FLAP)
556    {
557    // Flapping, Ricardo's version
558    // similar to SM3 Fox
559    if(input.jump == DOWN && input.old_jump == UP && can_flap &&
560      flaps_nb < 3)
561      {
562        physic.set_velocity_y(350);
563        physic.set_velocity_x(physic.get_velocity_x() * 35);
564        flaps_nb++;
565      }
566    }
567   else if(flapping_mode == MAREK_FLAP)
568    {
569    // Flapping, Marek's version
570    if (input.jump == DOWN && can_flap)
571      {
572          if (!flapping_timer.started())
573             {
574                flapping_timer.start(TUX_FLAPPING_TIME);
575                flapping_velocity = physic.get_velocity_x();
576             }
577          if (flapping_timer.check()) 
578             {
579                can_flap = false;
580                falling_from_flap = true;
581             }
582          jumping = true;
583          flapping = true;
584          if (!flapping_timer.check()) {
585            float cv = flapping_velocity * sqrt(
586                TUX_FLAPPING_TIME - flapping_timer.get_timegone() 
587                / TUX_FLAPPING_TIME);
588
589            //Handle change of direction while flapping
590            if (((dir == LEFT) && (cv > 0)) || (dir == RIGHT) && (cv < 0)) {
591              cv *= (-1);
592            }
593            physic.set_velocity_x(cv);
594            physic.set_velocity_y(
595                flapping_timer.get_timegone()/.850);
596          }
597      }
598    }
599   else if(flapping_mode == RYAN_FLAP)
600    {
601    // Flapping, Ryan's version
602    if (input.jump == DOWN && can_flap)
603      {
604          if (!flapping_timer.started())
605             {
606                flapping_timer.start(TUX_FLAPPING_TIME);
607             }
608          if (flapping_timer.check()) 
609             {
610                can_flap = false;
611                falling_from_flap = true;
612             }
613          jumping = true;
614          flapping = true;
615          if (flapping && flapping_timer.get_timegone() <= TUX_FLAPPING_TIME
616                 && physic.get_velocity_y() < 0)
617             {
618                float gravity = Sector::current()->gravity;
619                (void)gravity;
620                float xr = (fabsf(physic.get_velocity_x()) / MAX_RUN_XM);
621
622                // XXX: magic numbers. should be a percent of gravity
623                //      gravity is (by default) -0.1f
624                physic.set_acceleration_y(12 + 1*xr);
625
626 #if 0
627                // To slow down x-vel when flapping (not working)
628                if (fabsf(physic.get_velocity_x()) > MAX_WALK_XM)
629                {
630                    if (physic.get_velocity_x() < 0)
631                        physic.set_acceleration_x(1.0f);
632                    else if (physic.get_velocity_x() > 0)
633                        physic.set_acceleration_x(-1.0f);
634                }
635 #endif
636             }
637      }
638     else
639      {
640         physic.set_acceleration_y(0);
641      }
642    }
643
644    // Hover
645    //(disabled by default, use cheat code "hover" to toggle on/off)
646    //TODO: needs some tweaking, especially when used together with double jump and jumping off badguys
647    if (enable_hover && input.jump == DOWN && !jumping && !butt_jump && physic.get_velocity_y() <= 0)
648       {
649          physic.set_velocity_y(-100);
650       }
651
652 #if 0
653    /* In case the player has pressed Down while in a certain range of air,
654       enable butt jump action */
655   if (input.down == DOWN && !butt_jump && !duck)
656     if(tiles_on_air(TILES_FOR_BUTTJUMP) && jumping)
657       butt_jump = true;
658 #endif
659
660    /* When Down is not held anymore, disable butt jump */
661   if(butt_jump && input.down == UP)
662     butt_jump = false;
663
664   // Do butt jump
665   if (butt_jump && on_ground() && size == BIG)
666   {
667     // Add a smoke cloud
668     if (duck) 
669       Sector::current()->add_smoke_cloud(Vector(get_pos().x - 32, get_pos().y));
670     else 
671       Sector::current()->add_smoke_cloud(
672           Vector(get_pos().x - 32, get_pos().y + 32));
673     
674     butt_jump = false;
675
676 #if 0
677     // Break bricks beneath Tux
678     if(Sector::current()->trybreakbrick(
679           Vector(base.x + 1, base.y + base.height), false)
680         || Sector::current()->trybreakbrick(
681            Vector(base.x + base.width - 1, base.y + base.height), false))
682     {
683       physic.set_velocity_y(2);
684       butt_jump = true;
685     }
686 #endif
687
688 #if 0
689     // Kill nearby badguys
690     std::vector<GameObject*> gameobjects = Sector::current()->gameobjects;
691     for (std::vector<GameObject*>::iterator i = gameobjects.begin();
692          i != gameobjects.end();
693          i++)
694     {
695       BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
696       if(badguy)
697       {
698         // don't kill when badguys are already dying or in a certain mode
699         if(badguy->dying == DYING_NOT && badguy->mode != BadGuy::BOMB_TICKING &&
700            badguy->mode != BadGuy::BOMB_EXPLODE)
701           {
702             if (fabsf(base.x - badguy->base.x) < 96 &&
703                 fabsf(base.y - badguy->base.y) < 64)
704               badguy->kill_me(25);
705           }
706       }
707     }
708 #endif
709   }
710
711   /** jumping is only allowed if we're about to touch ground soon and if the
712    * button has been up in between the last jump
713    */
714   // FIXME
715 #if 0
716   if ( (issolid(get_pos().x + bbox.get_width() / 2,
717           get_pos().y + bbox.get_height() + 64) ||
718         issolid(get_pos().x + 1, get_pos().y + bbox.get_height() + 64) ||
719         issolid(get_pos().x + bbox.get_width() - 1,
720           get_pos().y + bbox.get_height() + 64))
721        && jumping  == false
722        && can_jump == false
723        && input.jump == DOWN
724        && input.old_jump == UP)
725     {
726       can_jump = true;
727     }
728 #endif
729
730   if(on_ground())   /* Make sure jumping is off. */
731     {
732       jumping = false;
733       flapping = false;
734       falling_from_flap = false;
735       if (flapping_timer.started()) {
736         flapping_timer.start(0);
737       }
738
739       physic.set_acceleration_y(0); //for flapping
740     }
741
742   input.old_jump = input.jump;
743 }
744
745 void
746 Player::handle_input()
747 {
748   /* Handle horizontal movement: */
749     handle_horizontal_input();
750
751   /* Jump/jumping? */
752
753   if (on_ground() && input.jump == UP)
754     can_jump = true;
755   handle_vertical_input();
756
757   /* Shoot! */
758   if (input.fire == DOWN && input.old_fire == UP && got_power != NONE_POWER)
759     {
760       if(Sector::current()->add_bullet(
761             get_pos() + Vector(0, bbox.get_height()/2),
762           physic.get_velocity_x(), dir))
763         shooting_timer.start(SHOOTING_TIME);
764       input.old_fire = DOWN;
765     }
766
767   /* tux animations: */
768   if(frame_timer.check()) {
769     if (input.right == UP && input.left == UP)
770     {
771       frame_main = 1;
772       frame_ = 1;
773     }
774     else
775     {
776       if ((input.fire == DOWN && (global_frame_counter % 2) == 0) ||
777           (global_frame_counter % 4) == 0)
778         frame_main = (frame_main + 1) % 4;
779
780       frame_ = frame_main;
781
782       if (frame_ == 3)
783         frame_ = 1;
784     }
785   }
786
787   /* Duck! */
788   if (input.down == DOWN && size == BIG && !duck 
789       && physic.get_velocity_y() == 0 && on_ground())
790     {
791       duck = true;
792       bbox.move(Vector(0, 32));
793       bbox.set_height(32);
794     }
795   else if(input.down == UP && size == BIG && duck)
796     {
797       // try if we can really unduck
798       bbox.move(Vector(0, -32));
799       bbox.set_height(64);
800       duck = false;
801       // FIXME
802 #if 0
803       // when unducking in air we need some space to do so
804       if(on_ground() || !collision_object_map(bbox)) {
805         duck = false;
806       } else {
807         // undo the ducking changes
808         bbox.move(Vector(0, 32));
809         bbox.set_height(32);
810       }
811 #endif
812     }
813 }
814
815 void
816 Player::grow(bool animate)
817 {
818   if(size == BIG)
819     return;
820   
821   size = BIG;
822   bbox.set_height(64);
823   bbox.move(Vector(0, -32));
824
825   if(animate)
826     growing_timer.start(GROWING_TIME);
827 }
828
829 void
830 Player::grabdistros()
831 {
832 }
833
834 void
835 Player::draw(DrawingContext& context)
836 {
837   TuxBodyParts* tux_body;
838           
839   if (size == SMALL)
840     tux_body = small_tux;
841   else if (got_power == FIRE_POWER)
842     tux_body = fire_tux;
843   else if (got_power == ICE_POWER)
844     tux_body = ice_tux;
845   else
846     tux_body = big_tux;
847
848   int layer = LAYER_OBJECTS - 1;
849
850   /* Set Tux sprite action */
851   if (duck && size == BIG)
852     {
853     if(dir == LEFT)
854       tux_body->set_action("duck-left");
855     else // dir == RIGHT
856       tux_body->set_action("duck-right");
857     }
858   else if (skidding_timer.started() && !skidding_timer.check())
859     {
860     if(dir == LEFT)
861       tux_body->set_action("skid-left");
862     else // dir == RIGHT
863       tux_body->set_action("skid-right");
864     }
865   else if (kick_timer.started() && !kick_timer.check())
866     {
867     if(dir == LEFT)
868       tux_body->set_action("kick-left");
869     else // dir == RIGHT
870       tux_body->set_action("kick-right");
871     }
872   else if (butt_jump && size == BIG)
873     {
874     if(dir == LEFT)
875       tux_body->set_action("buttjump-left");
876     else // dir == RIGHT
877       tux_body->set_action("buttjump-right");
878     }
879   else if (physic.get_velocity_y() != 0)
880     {
881     if(dir == LEFT)
882       tux_body->set_action("jump-left");
883     else // dir == RIGHT
884       tux_body->set_action("jump-right");
885     }
886   else
887     {
888     if (fabsf(physic.get_velocity_x()) < 1.0f) // standing
889       {
890       if(dir == LEFT)
891         tux_body->set_action("stand-left");
892       else // dir == RIGHT
893         tux_body->set_action("stand-right");
894       }
895     else // moving
896       {
897       if(dir == LEFT)
898         tux_body->set_action("walk-left");
899       else // dir == RIGHT
900         tux_body->set_action("walk-right");
901       }
902     }
903
904   if(idle_timer.check())
905     {
906     if(size == BIG)
907       {
908       if(dir == LEFT)
909         tux_body->head->set_action("idle-left");
910       else // dir == RIGHT
911         tux_body->head->set_action("idle-right");
912
913       tux_body->head->start_animation(1);
914       }
915
916     }
917
918   // Tux is holding something
919   if ((holding_something && physic.get_velocity_y() == 0) ||
920       (shooting_timer.get_timeleft() > 0 && !shooting_timer.check()))
921     {
922     if (duck)
923       {
924       if(dir == LEFT)
925         tux_body->arms->set_action("duck+grab-left");
926       else // dir == RIGHT
927         tux_body->arms->set_action("duck+grab-right");
928       }
929     else
930       {
931       if(dir == LEFT)
932         tux_body->arms->set_action("grab-left");
933       else // dir == RIGHT
934         tux_body->arms->set_action("grab-right");
935       }
936     }
937
938   /* Draw Tux */
939   if (dying == DYING_SQUISHED) {
940     smalltux_gameover->draw(context, get_pos(), LAYER_FOREGROUNDTILES+1);
941   } else if(growing_timer.get_timeleft() > 0) {
942     if(size == SMALL)
943       {
944       if (dir == RIGHT)
945         context.draw_surface(growingtux_right[GROWING_FRAMES-1 - 
946                  int((growing_timer.get_timegone() *
947                  GROWING_FRAMES) / GROWING_TIME)], get_pos(), layer);
948       else
949         context.draw_surface(growingtux_left[GROWING_FRAMES-1 - 
950                 int((growing_timer.get_timegone() *
951                 GROWING_FRAMES) / GROWING_TIME)], get_pos(), layer);
952       }
953     else
954       {
955       if (dir == RIGHT)
956         context.draw_surface(growingtux_right[
957             int((growing_timer.get_timegone() *
958                 GROWING_FRAMES) / GROWING_TIME)], get_pos(), layer);
959       else
960         context.draw_surface(growingtux_left[
961             int((growing_timer.get_timegone() *
962                              GROWING_FRAMES) / GROWING_TIME)],
963             get_pos(), layer);
964       }
965     }
966   else if (safe_timer.started() && global_frame_counter%2)
967     ;  // don't draw Tux
968   else
969     tux_body->draw(context, get_pos(), layer);
970
971   // Draw blinking star overlay
972   if (invincible_timer.started() &&
973      (invincible_timer.get_timeleft() > TUX_INVINCIBLE_TIME_WARNING
974       || global_frame_counter % 3)
975      && !dying)
976   {
977     if (size == SMALL || duck)
978       smalltux_star->draw(context, get_pos(), LAYER_OBJECTS + 2);
979     else
980       bigtux_star->draw(context, get_pos(), LAYER_OBJECTS + 2);
981   }
982  
983   if (debug_mode)
984     context.draw_filled_rect(get_pos(),
985         Vector(bbox.get_width(), bbox.get_height()),
986         Color(75,75,75, 150), LAYER_OBJECTS+1);
987 }
988
989 HitResponse
990 Player::collision(GameObject& other, const CollisionHit& hit)
991 {
992   if(dying) {
993     return FORCE_MOVE;
994   }
995
996   if(other.get_flags() & FLAG_SOLID) {
997     if(hit.normal.y < 0) { // landed on floor?
998       if (physic.get_velocity_y() < 0)
999         physic.set_velocity_y(0);
1000       on_ground_flag = true;
1001     } else if(hit.normal.y > 0) { // bumped against the roof
1002       physic.set_velocity_y(0);
1003     }
1004     
1005     if(hit.normal.x != 0) { // hit on the side?
1006       if(hit.normal.y > 0.6) // limits the slopes we can move up...
1007         physic.set_velocity_x(0);
1008     }
1009
1010     return CONTINUE;
1011   }
1012
1013   TriggerBase* trigger = dynamic_cast<TriggerBase*> (&other);
1014   if(trigger) {
1015     if(input.up == DOWN && input.old_up == UP)
1016       trigger->event(*this, TriggerBase::EVENT_ACTIVATE);
1017   }
1018
1019   return FORCE_MOVE;
1020 }
1021
1022 #if 0
1023 void
1024 Player::collision(void* p_c_object, int c_object)
1025 {
1026   //BadGuy* pbad_c = NULL;
1027   //Trampoline* ptramp_c = NULL;
1028   //FlyingPlatform* pplatform_c = NULL;
1029
1030   switch (c_object)
1031     {
1032     case CO_BADGUY:
1033       pbad_c = (BadGuy*) p_c_object;
1034
1035      /* Hurt player if he touches a badguy */
1036       if (!pbad_c->dying && !dying &&
1037           !safe_timer.started() &&
1038           pbad_c->mode != BadGuy::HELD)
1039         {
1040           if (pbad_c->mode == BadGuy::FLAT && input.fire == DOWN
1041                && !holding_something)
1042             {
1043               holding_something = true;
1044               pbad_c->mode = BadGuy::HELD;
1045               pbad_c->base.y-=8;
1046             }
1047           else if (pbad_c->mode == BadGuy::FLAT)
1048             {
1049               // Don't get hurt if we're kicking a flat badguy!
1050             }
1051           else if (pbad_c->mode == BadGuy::KICK)
1052             {
1053               /* Hurt if you get hit by kicked laptop: */
1054               if (!invincible_timer.started())
1055                 {
1056                   kill(SHRINK);
1057                 }
1058               else
1059                 pbad_c->kill_me(20);
1060             }
1061           else if (!pbad_c->frozen_timer.check() && (pbad_c->kind == BAD_MRBOMB
1062               || pbad_c->kind == BAD_JUMPY || pbad_c->kind == BAD_FISH
1063               || pbad_c->kind == BAD_SPIKY))
1064                 pbad_c->kill_me(20);
1065           else
1066             {
1067               if (!invincible_timer.started())
1068                 {
1069                   kill(SHRINK);
1070                 }
1071               else
1072                 {
1073                   pbad_c->kill_me(25);
1074                 }
1075             }
1076           player_status.score_multiplier++;
1077         }
1078       break;
1079
1080     case CO_TRAMPOLINE:
1081       ptramp_c = (Trampoline*) p_c_object;
1082       
1083       // Pick up trampoline
1084       if (ptramp_c->mode != Trampoline::M_HELD && input.fire == DOWN && !holding_something && on_ground())
1085       {
1086         holding_something = true;
1087         ptramp_c->mode = Trampoline::M_HELD;
1088         ptramp_c->base.y -= 8;
1089       }
1090       // Set down trampoline
1091       else if (ptramp_c->mode == Trampoline::M_HELD && input.fire != DOWN)
1092       {
1093         holding_something = false;
1094         ptramp_c->mode = Trampoline::M_NORMAL;
1095         ptramp_c->base.y += 8;
1096         ptramp_c->physic.set_velocity(physic.get_velocity_x(), physic.get_velocity_y());
1097
1098         //if (dir == RIGHT)
1099         //  ptramp_c->base.x = base.x + base.width+1;
1100         //else /* LEFT */
1101         //  ptramp_c->base.x = base.x - base.width-1;
1102       }
1103 /*
1104       // Don't let tux walk through trampoline
1105       else if (ptramp_c->mode != Trampoline::M_HELD && on_ground())
1106       {
1107         if (physic.get_velocity_x() > 0) // RIGHT
1108         {
1109           physic.set_velocity_x(0);
1110           base.x = ptramp_c->base.x - base.width;
1111         }
1112         else if (physic.get_velocity_x() < 0) // LEFT
1113         {
1114           physic.set_velocity_x(0);
1115           base.x = ptramp_c->base.x + ptramp_c->base.width;
1116         }
1117       }
1118 */
1119       break;
1120     case CO_FLYING_PLATFORM:
1121       pplatform_c = (FlyingPlatform*) p_c_object;
1122       
1123       base.y = pplatform_c->base.y - base.height;
1124       physic.set_velocity_x(pplatform_c->get_vel_x());
1125       
1126       physic.enable_gravity(false);
1127       can_jump = true;
1128       fall_mode = ON_GROUND;
1129       break;
1130
1131     default:
1132       break;
1133     }
1134
1135 }
1136 #endif
1137
1138 void
1139 Player::make_invincible()
1140 {
1141   SoundManager::get()->play_sound(IDToSound(SND_HERRING));
1142   invincible_timer.start(TUX_INVINCIBLE_TIME);
1143   Sector::current()->play_music(HERRING_MUSIC);               
1144 }
1145
1146 /* Kill Player! */
1147 void
1148 Player::kill(HurtMode mode)
1149 {
1150   if(dying)
1151     return;
1152
1153   if(safe_timer.get_timeleft() > 0 || invincible_timer.get_timeleft() > 0)
1154     return;                          
1155   
1156   SoundManager::get()->play_sound(IDToSound(SND_HURT));
1157
1158   physic.set_velocity_x(0);
1159
1160   if (mode == SHRINK && size == BIG)
1161     {
1162       if (got_power != NONE_POWER)
1163         {
1164           safe_timer.start(TUX_SAFE_TIME);
1165           got_power = NONE_POWER;
1166         }
1167       else
1168         {
1169           growing_timer.start(GROWING_TIME);
1170           safe_timer.start(TUX_SAFE_TIME + GROWING_TIME);
1171           size = SMALL;
1172           bbox.set_height(32);
1173           duck = false;
1174         }
1175     }
1176   else
1177     {
1178       physic.enable_gravity(true);
1179       physic.set_acceleration(0, 0);
1180       physic.set_velocity(0, 700);
1181       --player_status.lives;
1182       dying = DYING_SQUISHED;
1183       dying_timer.start(3.0);
1184     }
1185 }
1186
1187 /* Remove Tux's power ups */
1188 void
1189 Player::remove_powerups()
1190 {
1191   got_power = NONE_POWER;
1192   size = SMALL;
1193   bbox.set_height(32);
1194 }
1195
1196 void
1197 Player::move(const Vector& vector)
1198 {
1199   bbox.set_pos(vector);
1200 }
1201
1202 void
1203 Player::check_bounds(Camera* camera)
1204 {
1205   /* Keep tux in bounds: */
1206   if (get_pos().x < 0)
1207     { // Lock Tux to the size of the level, so that he doesn't fall of
1208       // on the left side
1209       bbox.set_pos(Vector(0, get_pos().y));
1210     }
1211
1212   /* Keep in-bounds, vertically: */
1213   if (get_pos().y > Sector::current()->solids->get_height() * 32)
1214     {
1215       kill(KILL);
1216       return;
1217     }
1218
1219   bool adjust = false;
1220   // can happen if back scrolling is disabled
1221   if(get_pos().x < camera->get_translation().x) {
1222     bbox.set_pos(Vector(camera->get_translation().x, get_pos().y));
1223     adjust = true;
1224   }
1225   if(get_pos().x >= camera->get_translation().x + screen->w - bbox.get_width())
1226   {
1227     bbox.set_pos(Vector(
1228           camera->get_translation().x + screen->w - bbox.get_width(),
1229           get_pos().y));
1230     adjust = true;
1231   }
1232
1233   if(adjust) {
1234     // FIXME
1235 #if 0
1236     // squished now?
1237     if(collision_object_map(bbox)) {
1238       kill(KILL);
1239       return;
1240     }
1241 #endif
1242   }
1243 }
1244
1245 void
1246 Player::bounce(BadGuy& badguy)
1247 {
1248   //Make sure we stopped flapping
1249   flapping = false;
1250   falling_from_flap = false;
1251   
1252   if (input.jump)
1253     physic.set_velocity_y(520);
1254   else
1255     physic.set_velocity_y(200);
1256 }
1257