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