Changed collision code, we now have several collision groups:
[supertux.git] / src / object / 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 #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 "video/screen.hpp"
35 #include "statistics.hpp"
36 #include "game_session.hpp"
37 #include "object/tilemap.hpp"
38 #include "object/camera.hpp"
39 #include "object/gameobjs.hpp"
40 #include "object/portable.hpp"
41 #include "trigger/trigger_base.hpp"
42 #include "control/joystickkeyboardcontroller.hpp"
43 #include "main.hpp"
44 #include "player_status.hpp"
45
46 static const int TILES_FOR_BUTTJUMP = 3;
47 static const float SHOOTING_TIME = .150;
48 /// time before idle animation starts
49 static const float IDLE_TIME = 2.5;
50
51 static const float WALK_ACCELERATION_X = 300;
52 static const float RUN_ACCELERATION_X = 400;
53 static const float SKID_XM = 200;
54 static const float SKID_TIME = .3;
55 static const float MAX_WALK_XM = 230;
56 static const float MAX_RUN_XM = 320;
57 static const float WALK_SPEED = 100;
58
59 // growing animation
60 Surface* growingtux_left[GROWING_FRAMES];
61 Surface* growingtux_right[GROWING_FRAMES];
62
63 Surface* tux_life = 0;
64
65 TuxBodyParts* small_tux = 0;
66 TuxBodyParts* big_tux = 0;
67 TuxBodyParts* fire_tux = 0;
68 TuxBodyParts* ice_tux = 0;
69
70 void
71 TuxBodyParts::set_action(std::string action, int loops)
72 {
73   if(head != NULL)
74     head->set_action(action, loops);
75   if(body != NULL)
76     body->set_action(action, loops);
77   if(arms != NULL)
78     arms->set_action(action, loops);
79   if(feet != NULL)
80     feet->set_action(action, loops);
81 }
82
83 void
84 TuxBodyParts::draw(DrawingContext& context, const Vector& pos, int layer)
85 {
86   if(head != NULL)
87     head->draw(context, pos, layer-1);
88   if(body != NULL)
89     body->draw(context, pos, layer-3);
90   if(arms != NULL)
91     arms->draw(context, pos, layer);
92   if(feet != NULL)
93     feet->draw(context, pos, layer-2);
94 }
95
96 Player::Player(PlayerStatus* _player_status)
97   : player_status(_player_status), grabbed_object(0)
98 {
99   controller = main_controller;
100   smalltux_gameover = sprite_manager->create("smalltux-gameover");
101   smalltux_star = sprite_manager->create("smalltux-star");
102   bigtux_star = sprite_manager->create("bigtux-star");
103   init();
104 }
105
106 Player::~Player()
107 {
108   delete smalltux_gameover;
109   delete smalltux_star;
110   delete bigtux_star;
111 }
112
113 void
114 Player::init()
115 {
116   if(is_big())
117     bbox.set_size(31.8, 63.8);
118   else
119     bbox.set_size(31.8, 31.8);
120
121   dir = RIGHT;
122   old_dir = dir;
123   duck = false;
124   dead = false;
125
126   dying = false;
127   last_ground_y = 0;
128   fall_mode = ON_GROUND;
129   jumping = false;
130   can_jump = true;
131   butt_jump = false;
132   deactivated = false;
133   backflipping = false;
134   backflip_direction = 0;
135   
136   on_ground_flag = false;
137   grabbed_object = 0;
138
139   physic.reset();
140 }
141
142 void
143 Player::set_controller(Controller* controller)
144 {
145   this->controller = controller;
146 }
147
148 void
149 Player::update(float elapsed_time)
150 {
151   if(dying && dying_timer.check()) {
152     dead = true;
153     return;
154   }
155
156   if(!controller->hold(Controller::ACTION) && grabbed_object) {
157     grabbed_object = 0;
158     // move the grabbed object a bit away from tux
159     Vector pos = get_pos() + 
160         Vector(dir == LEFT ? -bbox.get_width() : bbox.get_width(),
161                 bbox.get_height()*0.66666 - 32);
162     MovingObject* object = dynamic_cast<MovingObject*> (grabbed_object);
163     if(object) {
164       object->set_pos(pos);
165     } else {
166 #ifdef DEBUG
167       std::cout << "Non MovingObjetc grabbed?!?\n";
168 #endif
169     }
170   }
171
172   if(!dying && !deactivated)
173     handle_input();
174
175   movement = physic.get_movement(elapsed_time);
176   on_ground_flag = false;
177
178 #if 0
179   // special exception for cases where we're stuck under tiles after
180   // being ducked. In this case we drift out
181   if(!duck && on_ground() && old_base.x == base.x && old_base.y == base.y
182      && collision_object_map(base)) {
183     base.x += elapsed_time * WALK_SPEED * (dir ? 1: -1);
184     previous_base = old_base = base;
185   }
186 #endif
187
188   if(grabbed_object != 0) {
189     Vector pos = get_pos() + 
190       Vector(dir == LEFT ? -16 : 16,
191              bbox.get_height()*0.66666 - 32);
192     grabbed_object->grab(*this, pos);
193   }
194 }
195
196 bool
197 Player::on_ground()
198 {
199   return on_ground_flag;
200 }
201
202 bool
203 Player::is_big()
204 {
205   if(player_status->bonus == NO_BONUS)
206     return false;
207
208   return true;
209 }
210
211 void
212 Player::handle_horizontal_input()
213 {
214   float vx = physic.get_velocity_x();
215   float vy = physic.get_velocity_y();
216   float ax = physic.get_acceleration_x();
217   float ay = physic.get_acceleration_y();
218
219   float dirsign = 0;
220   if(!duck || physic.get_velocity_y() != 0) {
221     if(controller->hold(Controller::LEFT) && !controller->hold(Controller::RIGHT)) {
222       old_dir = dir;
223       dir = LEFT;
224       dirsign = -1;
225     } else if(!controller->hold(Controller::LEFT)
226               && controller->hold(Controller::RIGHT)) {
227       old_dir = dir;
228       dir = RIGHT;
229       dirsign = 1;
230     }
231   }
232
233   if (!controller->hold(Controller::ACTION)) {
234     ax = dirsign * WALK_ACCELERATION_X;
235     // limit speed
236     if(vx >= MAX_WALK_XM && dirsign > 0) {
237       vx = MAX_WALK_XM;
238       ax = 0;
239     } else if(vx <= -MAX_WALK_XM && dirsign < 0) {
240       vx = -MAX_WALK_XM;
241       ax = 0;
242     }
243   } else {
244     ax = dirsign * RUN_ACCELERATION_X;
245     // limit speed
246     if(vx >= MAX_RUN_XM && dirsign > 0) {
247       vx = MAX_RUN_XM;
248       ax = 0;
249     } else if(vx <= -MAX_RUN_XM && dirsign < 0) {
250       vx = -MAX_RUN_XM;
251       ax = 0;
252     }
253   }
254
255   // we can reach WALK_SPEED without any acceleration
256   if(dirsign != 0 && fabs(vx) < WALK_SPEED) {
257     vx = dirsign * WALK_SPEED;
258   }
259
260   // changing directions?
261   if(on_ground() && ((vx < 0 && dirsign >0) || (vx>0 && dirsign<0))) {
262     // let's skid!
263     if(fabs(vx)>SKID_XM && !skidding_timer.started()) {
264       skidding_timer.start(SKID_TIME);
265       sound_manager->play("sounds/skid.wav");
266       // dust some particles
267       Sector::current()->add_object(
268         new Particles(
269           Vector(bbox.p1.x + (dir == RIGHT ? bbox.get_width() : 0),
270                  bbox.p2.y),
271           dir == RIGHT ? 270+20 : 90-40, dir == RIGHT ? 270+40 : 90-20,
272           Vector(280,-260), Vector(0,0.030), 3, Color(100,100,100), 3, .8,
273           LAYER_OBJECTS+1));
274       
275       ax *= 2.5;
276     } else {
277       ax *= 2;
278     }
279   }
280
281   // we get slower when not pressing any keys
282   if(dirsign == 0) {
283     if(fabs(vx) < WALK_SPEED) {
284       vx = 0;
285       ax = 0;
286     } else if(vx < 0) {
287       ax = WALK_ACCELERATION_X * 1.5;
288     } else {
289       ax = WALK_ACCELERATION_X * -1.5;
290     }
291   }
292
293 #if 0
294   // if we're on ice slow down acceleration or deceleration
295   if (isice(base.x, base.y + base.height))
296   {
297     /* the acceleration/deceleration rate on ice is inversely proportional to
298      * the current velocity.
299      */
300
301     // increasing 1 will increase acceleration/deceleration rate
302     // decreasing 1 will decrease acceleration/deceleration rate
303     //  must stay above zero, though
304     if (ax != 0) ax *= 1 / fabs(vx);
305   }
306 #endif
307
308   // extend/shrink tux collision rectangle so that we fall through/walk over 1
309   // tile holes
310   if(fabsf(vx) > MAX_WALK_XM) {
311     bbox.set_width(33);
312   } else {
313     bbox.set_width(31.8);
314   }
315
316   physic.set_velocity(vx, vy);
317   physic.set_acceleration(ax, ay);
318 }
319
320 void
321 Player::handle_vertical_input()
322 {
323   // set fall mode...
324   if(on_ground()) {
325     fall_mode = ON_GROUND;
326     last_ground_y = get_pos().y;
327   } else {
328     if(get_pos().y > last_ground_y)
329       fall_mode = FALLING;
330     else if(fall_mode == ON_GROUND)
331       fall_mode = JUMPING;
332   }
333
334   if(on_ground()) { /* Make sure jumping is off. */
335     jumping = false;
336     if (backflipping) {
337       backflipping = false;
338       backflip_direction = 0;
339     }
340   }
341
342   // Press jump key
343   if(controller->pressed(Controller::JUMP) && can_jump && on_ground()) {
344     if (duck) { 
345       if (physic.get_velocity_x() != 0) // only jump a little bit when running ducked
346         physic.set_velocity_y(300);
347       else { //do a backflip
348         backflipping = true;
349         physic.set_velocity_y(580);
350         backflip_timer.start(0.15);
351       }
352     }
353     else if (fabs(physic.get_velocity_x()) > MAX_WALK_XM) // jump higher if we are running
354       physic.set_velocity_y(580);
355     else
356       physic.set_velocity_y(520);
357     
358     //bbox.move(Vector(0, -1));
359     jumping = true;
360     can_jump = false;
361     if (is_big())
362       sound_manager->play("sounds/bigjump.wav");
363     else
364       sound_manager->play("sounds/jump.wav");
365   } else if(!controller->hold(Controller::JUMP)) { // Let go of jump key
366     if (!backflipping && jumping && physic.get_velocity_y() > 0) {
367       jumping = false;
368       physic.set_velocity_y(0);
369     }
370   }
371
372   /* In case the player has pressed Down while in a certain range of air,
373      enable butt jump action */
374   if (controller->hold(Controller::DOWN) && !butt_jump && !duck)
375     //if(tiles_on_air(TILES_FOR_BUTTJUMP) && jumping)
376     butt_jump = true;
377   
378   /* When Down is not held anymore, disable butt jump */
379   if(butt_jump && !controller->hold(Controller::DOWN))
380     butt_jump = false;
381   
382 #if 0
383   // Do butt jump
384   if (butt_jump && on_ground() && is_big()) {
385     // Add a smoke cloud
386     if (duck) 
387       Sector::current()->add_smoke_cloud(Vector(get_pos().x - 32, get_pos().y));
388     else 
389       Sector::current()->add_smoke_cloud(
390         Vector(get_pos().x - 32, get_pos().y + 32));
391     
392     butt_jump = false;
393     
394     // Break bricks beneath Tux
395     if(Sector::current()->trybreakbrick(
396          Vector(base.x + 1, base.y + base.height), false)
397        || Sector::current()->trybreakbrick(
398          Vector(base.x + base.width - 1, base.y + base.height), false)) {
399       physic.set_velocity_y(2);
400       butt_jump = true;
401     }
402     
403     // Kill nearby badguys
404     std::vector<GameObject*> gameobjects = Sector::current()->gameobjects;
405     for (std::vector<GameObject*>::iterator i = gameobjects.begin();
406          i != gameobjects.end();
407          i++) {
408       BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
409       if(badguy) {
410         // don't kill when badguys are already dying or in a certain mode
411         if(badguy->dying == DYING_NOT && badguy->mode != BadGuy::BOMB_TICKING &&
412            badguy->mode != BadGuy::BOMB_EXPLODE) {
413           if (fabsf(base.x - badguy->base.x) < 96 &&
414               fabsf(base.y - badguy->base.y) < 64)
415             badguy->kill_me(25);
416         }
417       }
418     }
419   }
420 #endif
421
422   /** jumping is only allowed if we're about to touch ground soon and if the
423    * button has been up in between the last jump
424    */
425   // FIXME
426 #if 0
427   if ( (issolid(get_pos().x + bbox.get_width() / 2,
428           get_pos().y + bbox.get_height() + 64) ||
429         issolid(get_pos().x + 1, get_pos().y + bbox.get_height() + 64) ||
430         issolid(get_pos().x + bbox.get_width() - 1,
431           get_pos().y + bbox.get_height() + 64))
432        && jumping  == false
433        && can_jump == false
434        && input.jump && !input.old_jump)
435     {
436       can_jump = true;
437     }
438 #endif
439 }
440
441 void
442 Player::handle_input()
443 {
444   /* Handle horizontal movement: */
445   if (!backflipping) handle_horizontal_input();
446   else {
447     if (backflip_direction == 0) {
448       dir == LEFT ? backflip_direction = 1 : backflip_direction = -1;
449     }
450     else backflip_direction == 1 ? dir = LEFT : dir = RIGHT; //prevent player from changing direction when backflipping 
451     if (backflip_timer.check()) physic.set_velocity_x(100 * backflip_direction);
452   }
453
454
455   /* Jump/jumping? */
456   if (on_ground() && !controller->hold(Controller::JUMP))
457     can_jump = true;
458   handle_vertical_input();
459
460   /* Shoot! */
461   if (controller->pressed(Controller::ACTION) && player_status->bonus == FIRE_BONUS) {
462     if(Sector::current()->add_bullet(
463          get_pos() + ((dir == LEFT)? Vector(0, bbox.get_height()/2) 
464                       : Vector(32, bbox.get_height()/2)),
465          physic.get_velocity_x(), dir))
466       shooting_timer.start(SHOOTING_TIME);
467   }
468   
469   /* Duck! */
470   if (controller->hold(Controller::DOWN) && is_big() && !duck 
471       && physic.get_velocity_y() == 0 && on_ground()) {
472     duck = true;
473     bbox.move(Vector(0, 32));
474     bbox.set_height(31.8);
475   } else if(!controller->hold(Controller::DOWN) && is_big() && duck) {
476     // try if we can really unduck
477     bbox.move(Vector(0, -32));
478     bbox.set_height(63.8);
479     duck = false;
480     // FIXME
481 #if 0
482     // when unducking in air we need some space to do so
483     if(on_ground() || !collision_object_map(bbox)) {
484       duck = false;
485     } else {
486       // undo the ducking changes
487       bbox.move(Vector(0, 32));
488       bbox.set_height(31.8);
489     }
490 #endif
491   }
492 }
493
494 void
495 Player::set_bonus(BonusType type, bool animate)
496 {
497   if(player_status->bonus >= type)
498     return;
499   
500   if(player_status->bonus == NO_BONUS) {
501     bbox.set_height(63.8);
502     bbox.move(Vector(0, -32));
503     if(animate)
504       growing_timer.start(GROWING_TIME);
505   }
506   
507   player_status->bonus = type;
508 }
509
510 void
511 Player::draw(DrawingContext& context)
512 {
513   TuxBodyParts* tux_body;
514           
515   if (player_status->bonus == GROWUP_BONUS)
516     tux_body = big_tux;
517   else if (player_status->bonus == FIRE_BONUS)
518     tux_body = fire_tux;
519   else if (player_status->bonus == ICE_BONUS)
520     tux_body = ice_tux;
521   else
522     tux_body = small_tux;
523
524   int layer = LAYER_OBJECTS + 10;
525
526   /* Set Tux sprite action */
527   if (duck && is_big())
528     {
529     if(dir == LEFT)
530       tux_body->set_action("duck-left");
531     else // dir == RIGHT
532       tux_body->set_action("duck-right");
533     }
534   else if (skidding_timer.started() && !skidding_timer.check())
535     {
536     if(dir == LEFT)
537       tux_body->set_action("skid-left");
538     else // dir == RIGHT
539       tux_body->set_action("skid-right");
540     }
541   else if (kick_timer.started() && !kick_timer.check())
542     {
543     if(dir == LEFT)
544       tux_body->set_action("kick-left");
545     else // dir == RIGHT
546       tux_body->set_action("kick-right");
547     }
548   else if (butt_jump && is_big())
549     {
550     if(dir == LEFT)
551       tux_body->set_action("buttjump-left");
552     else // dir == RIGHT
553       tux_body->set_action("buttjump-right");
554     }
555   else if (physic.get_velocity_y() != 0)
556     {
557     if(dir == LEFT)
558       tux_body->set_action("jump-left");
559     else // dir == RIGHT
560       tux_body->set_action("jump-right");
561     }
562   else
563     {
564     if (fabsf(physic.get_velocity_x()) < 1.0f) // standing
565       {
566       if(dir == LEFT)
567         tux_body->set_action("stand-left");
568       else // dir == RIGHT
569         tux_body->set_action("stand-right");
570       }
571     else // moving
572       {
573       if(dir == LEFT)
574         tux_body->set_action("walk-left");
575       else // dir == RIGHT
576         tux_body->set_action("walk-right");
577       }
578     }
579
580   if(idle_timer.check())
581     {
582     if(is_big())
583       {
584       if(dir == LEFT)
585         tux_body->head->set_action("idle-left", 1);
586       else // dir == RIGHT
587         tux_body->head->set_action("idle-right", 1);
588       }
589
590     }
591
592   // Tux is holding something
593   if ((grabbed_object != 0 && physic.get_velocity_y() == 0) ||
594       (shooting_timer.get_timeleft() > 0 && !shooting_timer.check()))
595     {
596     if (duck)
597       {
598       if(dir == LEFT)
599         tux_body->arms->set_action("duck+grab-left");
600       else // dir == RIGHT
601         tux_body->arms->set_action("duck+grab-right");
602       }
603     else
604       {
605       if(dir == LEFT)
606         tux_body->arms->set_action("grab-left");
607       else // dir == RIGHT
608         tux_body->arms->set_action("grab-right");
609       }
610     }
611
612   /* Draw Tux */
613   if(dying) {
614     smalltux_gameover->draw(context, get_pos(), layer);
615   } else if(growing_timer.get_timeleft() > 0) {
616     if(!is_big())
617       {
618       if (dir == RIGHT)
619         context.draw_surface(growingtux_right[GROWING_FRAMES-1 - 
620                  int((growing_timer.get_timegone() *
621                  GROWING_FRAMES) / GROWING_TIME)], get_pos(), layer);
622       else
623         context.draw_surface(growingtux_left[GROWING_FRAMES-1 - 
624                 int((growing_timer.get_timegone() *
625                 GROWING_FRAMES) / GROWING_TIME)], get_pos(), layer);
626       }
627     else
628       {
629       if (dir == RIGHT)
630         context.draw_surface(growingtux_right[
631             int((growing_timer.get_timegone() *
632                 GROWING_FRAMES) / GROWING_TIME)], get_pos(), layer);
633       else
634         context.draw_surface(growingtux_left[
635             int((growing_timer.get_timegone() *
636                              GROWING_FRAMES) / GROWING_TIME)],
637             get_pos(), layer);
638       }
639     }
640   else if (safe_timer.started() && size_t(game_time*40)%2)
641     ;  // don't draw Tux
642   else
643     tux_body->draw(context, get_pos(), layer);
644
645   // Draw blinking star overlay
646   if (invincible_timer.started() &&
647      (invincible_timer.get_timeleft() > TUX_INVINCIBLE_TIME_WARNING
648       || size_t(game_time*20)%2)
649      && !dying)
650   {
651     if (!is_big() || duck)
652       smalltux_star->draw(context, get_pos(), layer + 5);
653     else
654       bigtux_star->draw(context, get_pos(), layer + 5);
655   } 
656 }
657
658 HitResponse
659 Player::collision(GameObject& other, const CollisionHit& hit)
660 {
661   Portable* portable = dynamic_cast<Portable*> (&other);
662   if(portable && grabbed_object == 0 && controller->hold(Controller::ACTION)
663      && fabsf(hit.normal.x) > .9) {
664     grabbed_object = portable;
665     return CONTINUE;
666   }
667  
668   if(other.get_flags() & FLAG_SOLID) {
669     TileMap* tilemap = dynamic_cast<TileMap*> (&other);
670     if(tilemap) {
671       const TilemapCollisionHit* thit = 
672         static_cast<const TilemapCollisionHit*> (&hit);
673       if(thit->tileflags & Tile::SPIKE)
674         kill(SHRINK);
675
676       if(! (thit->tileflags & Tile::SOLID))
677         return FORCE_MOVE;
678     }
679     
680     if(hit.normal.y < 0) { // landed on floor?
681       if (physic.get_velocity_y() < 0)
682         physic.set_velocity_y(0);
683       on_ground_flag = true;
684     } else if(hit.normal.y > 0) { // bumped against the roof
685       physic.set_velocity_y(.1);
686     }
687     
688     if(fabsf(hit.normal.x) > .9) { // hit on the side?
689       physic.set_velocity_x(0);
690     }
691
692     return CONTINUE;
693   }
694
695   TriggerBase* trigger = dynamic_cast<TriggerBase*> (&other);
696   if(trigger) {
697     if(controller->pressed(Controller::UP))
698       trigger->event(*this, TriggerBase::EVENT_ACTIVATE);
699   }
700
701   return FORCE_MOVE;
702 }
703
704 void
705 Player::make_invincible()
706 {
707   sound_manager->play("sounds/invincible.wav");
708   invincible_timer.start(TUX_INVINCIBLE_TIME);
709   Sector::current()->play_music(HERRING_MUSIC);               
710 }
711
712 /* Kill Player! */
713 void
714 Player::kill(HurtMode mode)
715 {
716   if(dying || deactivated)
717     return;
718
719   if(mode != KILL && 
720           safe_timer.get_timeleft() > 0 || invincible_timer.get_timeleft() > 0)
721     return;                          
722   
723   sound_manager->play("sounds/hurt.wav");
724
725   physic.set_velocity_x(0);
726
727   if (mode == SHRINK && is_big())
728     {
729       if (player_status->bonus == FIRE_BONUS
730           || player_status->bonus == ICE_BONUS)
731         {
732           safe_timer.start(TUX_SAFE_TIME);
733           player_status->bonus = GROWUP_BONUS;
734         }
735       else 
736         {
737           growing_timer.start(GROWING_TIME);
738           safe_timer.start(TUX_SAFE_TIME + GROWING_TIME);
739           bbox.set_height(31.8);
740           duck = false;
741           player_status->bonus = NO_BONUS;
742         }
743     }
744   else
745     {
746       physic.enable_gravity(true);
747       physic.set_acceleration(0, 0);
748       physic.set_velocity(0, 700);
749       player_status->lives -= 1;
750       player_status->bonus = NO_BONUS;
751       dying = true;
752       dying_timer.start(3.0);
753       set_group(COLGROUP_DISABLED);
754     }
755 }
756
757 void
758 Player::move(const Vector& vector)
759 {
760   bbox.set_pos(vector);
761   if(is_big())
762     bbox.set_size(31.8, 63.8);
763   else
764     bbox.set_size(31.8, 31.8);
765   on_ground_flag = false;
766   duck = false;
767   last_ground_y = vector.y;
768
769   physic.reset();
770 }
771
772 void
773 Player::check_bounds(Camera* camera)
774 {
775   /* Keep tux in bounds: */
776   if (get_pos().x < 0)
777     { // Lock Tux to the size of the level, so that he doesn't fall of
778       // on the left side
779       bbox.set_pos(Vector(0, get_pos().y));
780     }
781
782   /* Keep in-bounds, vertically: */
783   if (get_pos().y > Sector::current()->solids->get_height() * 32)
784     {
785       kill(KILL);
786       return;
787     }
788
789   bool adjust = false;
790   // can happen if back scrolling is disabled
791   if(get_pos().x < camera->get_translation().x) {
792     bbox.set_pos(Vector(camera->get_translation().x, get_pos().y));
793     adjust = true;
794   }
795   if(get_pos().x >= camera->get_translation().x + SCREEN_WIDTH - bbox.get_width())
796   {
797     bbox.set_pos(Vector(
798           camera->get_translation().x + SCREEN_WIDTH - bbox.get_width(),
799           get_pos().y));
800     adjust = true;
801   }
802
803   if(adjust) {
804     // FIXME
805 #if 0
806     // squished now?
807     if(collision_object_map(bbox)) {
808       kill(KILL);
809       return;
810     }
811 #endif
812   }
813 }
814
815 void
816 Player::bounce(BadGuy& )
817 {
818   if(controller->hold(Controller::JUMP))
819     physic.set_velocity_y(520);
820   else
821     physic.set_velocity_y(300);
822 }
823
824 //Scripting Functions Below
825
826 void
827 Player::deactivate()
828 {
829   deactivated = true;
830   physic.set_velocity_x(0);
831   physic.set_velocity_y(0);
832 }
833
834 void
835 Player::activate()
836 {
837   deactivated = false;
838 }
839
840 void Player::walk(float speed)
841 {
842   physic.set_velocity_x(speed);
843 }