790d42bdb54b020319b85ea248d830e9116f5dc3
[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 <math.h>
21 #include <iostream>
22 #include <cassert>
23 #include "gameloop.h"
24 #include "globals.h"
25 #include "player.h"
26 #include "defines.h"
27 #include "scene.h"
28 #include "tile.h"
29 #include "sprite.h"
30 #include "screen.h"
31
32 #define AUTOSCROLL_DEAD_INTERVAL 300
33
34 Surface* tux_life;
35
36 Sprite* smalltux_gameover;
37 Sprite* smalltux_star;
38 Sprite* largetux_star;
39
40 PlayerSprite smalltux;
41 PlayerSprite largetux;
42 PlayerSprite icetux;
43 PlayerSprite firetux;
44
45 PlayerKeymap keymap;
46
47 PlayerKeymap::PlayerKeymap()
48 {
49   keymap.jump  = SDLK_UP;
50   keymap.duck  = SDLK_DOWN;
51   keymap.left  = SDLK_LEFT;
52   keymap.right = SDLK_RIGHT;
53   keymap.fire  = SDLK_LCTRL;
54 }
55
56 void player_input_init(player_input_type* pplayer_input)
57 {
58   pplayer_input->down = UP;
59   pplayer_input->fire = UP;
60   pplayer_input->left = UP;
61   pplayer_input->old_fire = UP;
62   pplayer_input->right = UP;
63   pplayer_input->up = UP;
64   pplayer_input->old_up = UP;
65 }
66
67 Player::Player(DisplayManager& display_manager)
68 {
69   display_manager.add_drawable(this, LAYER_OBJECTS);
70   init();
71 }
72
73 Player::~Player()
74 {
75 }
76
77 void
78 Player::init()
79 {
80   Level* plevel = World::current()->get_level();
81
82   holding_something = false;
83
84   base.width = 32;
85   base.height = 32;
86
87   size = SMALL;
88   got_power = NONE_POWER;
89
90   base.x = plevel->start_pos_x;
91   base.y = plevel->start_pos_y;
92   previous_base = old_base = base;
93   dir = RIGHT;
94   old_dir = dir;
95   duck = false;
96
97   dying   = DYING_NOT;
98   jumping = false;
99   can_jump = true;
100   butt_jump = false;
101
102   frame_main = 0;
103   frame_ = 0;
104   
105   player_input_init(&input);
106
107   invincible_timer.init(true);
108   skidding_timer.init(true);
109   safe_timer.init(true);
110   frame_timer.init(true);
111   kick_timer.init(true);
112
113   physic.reset();
114 }
115
116 int
117 Player::key_event(SDLKey key, int state)
118 {
119   if(key == keymap.right)
120     {
121       input.right = state;
122       return true;
123     }
124   else if(key == keymap.left)
125     {
126       input.left = state;
127       return true;
128     }
129   else if(key == keymap.jump)
130     {
131       input.up = state;
132       return true;
133     }
134   else if(key == keymap.duck)
135     {
136       input.down = state;
137       return true;
138     }
139   else if(key == keymap.fire)
140     {
141       if (state == UP)
142         input.old_fire = UP;
143       input.fire = state;
144       return true;
145     }
146   else
147     return false;
148 }
149
150 void
151 Player::level_begin()
152 {
153   base.x  = 100;
154   base.y  = 170;
155   previous_base = old_base = base;
156   duck = false;
157
158   dying = DYING_NOT;
159
160   player_input_init(&input);
161
162   invincible_timer.init(true);
163   skidding_timer.init(true);
164   safe_timer.init(true);
165   frame_timer.init(true);
166
167   physic.reset();
168 }
169
170 void
171 Player::action(float elapsed_time)
172 {
173   bool jumped_in_solid = false;
174
175   if (input.fire == UP)
176     holding_something = false;
177
178   /* Move tux: */
179   previous_base = base;
180
181   /* --- HANDLE TUX! --- */
182   if(dying == DYING_NOT)
183     handle_input();
184
185   physic.apply(elapsed_time, base.x, base.y);
186
187   if(dying == DYING_NOT) 
188     {
189       base_type target = base;
190
191       collision_swept_object_map(&old_base, &base);
192
193       if (!invincible_timer.started()
194           && (isspike(base.x, base.y) || isspike(base.x + base.width, base.y)
195           ||  isspike(base.x, base.y + base.height)
196           ||  isspike(base.x + base.width, base.y + base.height)))
197       {
198          kill(SHRINK);
199       }
200
201       // Don't accelerate Tux if he is running against a wall
202       if (target.x != base.x)
203         {
204           physic.set_velocity_x(0);
205         }
206
207       // special exception for cases where we're stuck under tiles after
208       // being ducked. In this case we drift out
209       if(!duck && on_ground() && old_base.x == base.x && old_base.y == base.y
210          && collision_object_map(base))
211         {
212           base.x += elapsed_time * WALK_SPEED * (dir ? 1: -1);
213           previous_base = old_base = base;
214         }
215
216       // Land:
217       if (!on_ground())
218         {
219           physic.enable_gravity(true);
220           if(under_solid())
221             {
222               // fall down
223               physic.set_velocity_y(0);
224               jumped_in_solid = true;
225             }
226         }
227       else
228         {
229           /* Land: */
230           if (physic.get_velocity_y() < 0)
231             {
232               base.y = (int)(((int)base.y / 32) * 32);
233               physic.set_velocity_y(0);
234             }
235
236           physic.enable_gravity(false);
237           /* Reset score multiplier (for multi-hits): */
238           if (!invincible_timer.started())
239             player_status.score_multiplier = 1;
240         }
241
242       if(jumped_in_solid)
243         {
244           if (isbrick(base.x, base.y) ||
245               isfullbox(base.x, base.y))
246             {
247               World::current()->trygrabdistro(base.x, base.y - 32,BOUNCE);
248               World::current()->trybumpbadguy(base.x, base.y - 64);
249
250               World::current()->trybreakbrick(base.x, base.y, size == SMALL);
251
252               bumpbrick(base.x, base.y);
253               World::current()->tryemptybox(base.x, base.y, RIGHT);
254             }
255
256           if (isbrick(base.x+ 31, base.y) ||
257               isfullbox(base.x+ 31, base.y))
258             {
259               World::current()->trygrabdistro(base.x+ 31, base.y - 32,BOUNCE);
260               World::current()->trybumpbadguy(base.x+ 31, base.y - 64);
261
262               if(size == BIG)
263                 World::current()->trybreakbrick(base.x+ 31, base.y, size == SMALL);
264
265               bumpbrick(base.x+ 31, base.y);
266               World::current()->tryemptybox(base.x+ 31, base.y, LEFT);
267             }
268         }
269
270       grabdistros();
271
272       if (jumped_in_solid)
273         {
274           ++base.y;
275           ++old_base.y;
276           if(on_ground())
277             {
278               /* Make sure jumping is off. */
279               jumping = false;
280             }
281         }
282     }
283
284   /* ---- DONE HANDLING TUX! --- */
285
286   // check some timers
287   skidding_timer.check();
288   invincible_timer.check();
289   safe_timer.check();
290   kick_timer.check();
291 }
292
293 bool
294 Player::on_ground()
295 {
296   return ( issolid(base.x + base.width / 2, base.y + base.height) ||
297            issolid(base.x + 1, base.y + base.height) ||
298            issolid(base.x + base.width - 1, base.y + base.height)  );
299 }
300
301 bool
302 Player::under_solid()
303 {
304   return ( issolid(base.x + base.width / 2, base.y) ||
305            issolid(base.x + 1, base.y) ||
306            issolid(base.x + base.width - 1, base.y)  );
307 }
308
309 void
310 Player::handle_horizontal_input()
311 {
312   float vx = physic.get_velocity_x();
313   float vy = physic.get_velocity_y();
314   float ax = physic.get_acceleration_x();
315   float ay = physic.get_acceleration_y();
316
317   float dirsign = 0;
318   if(input.left == DOWN && input.right == UP && (!duck || physic.get_velocity_y() != 0)) {
319       old_dir = dir;
320       dir = LEFT;
321       dirsign = -1;
322   } else if(input.left == UP && input.right == DOWN && (!duck || physic.get_velocity_y() != 0)) {
323       old_dir = dir;
324       dir = RIGHT;
325       dirsign = 1;
326   }
327
328   if (input.fire == UP) {
329       ax = dirsign * WALK_ACCELERATION_X;
330       // limit speed
331       if(vx >= MAX_WALK_XM && dirsign > 0) {
332         vx = MAX_WALK_XM;
333         ax = 0;
334       } else if(vx <= -MAX_WALK_XM && dirsign < 0) {
335         vx = -MAX_WALK_XM;
336         ax = 0;
337       }
338   } else {
339       ax = dirsign * RUN_ACCELERATION_X;
340       // limit speed
341       if(vx >= MAX_RUN_XM && dirsign > 0) {
342         vx = MAX_RUN_XM;
343         ax = 0;
344       } else if(vx <= -MAX_RUN_XM && dirsign < 0) {
345         vx = -MAX_RUN_XM;
346         ax = 0;
347       }
348   }
349
350   // we can reach WALK_SPEED without any acceleration
351   if(dirsign != 0 && fabs(vx) < WALK_SPEED) {
352     vx = dirsign * WALK_SPEED;
353   }
354
355   // changing directions?
356   if(on_ground() && ((vx < 0 && dirsign >0) || (vx>0 && dirsign<0))) {
357       if(fabs(vx)>SKID_XM && !skidding_timer.check()) {
358           skidding_timer.start(SKID_TIME);
359           play_sound(sounds[SND_SKID], SOUND_CENTER_SPEAKER);
360           ax *= 2.5;
361       } else {
362           ax *= 2;
363       }
364   }
365
366   // we get slower when not pressing any keys
367   if(dirsign == 0) {
368       if(fabs(vx) < WALK_SPEED) {
369           vx = 0;
370           ax = 0;
371       } else if(vx < 0) {
372           ax = WALK_ACCELERATION_X * 1.5;
373       } else {
374           ax = WALK_ACCELERATION_X * -1.5;
375       }
376   }
377
378   // if we're on ice slow down acceleration or deceleration
379   if (isice(base.x, base.y + base.height))
380   {
381     /* the acceleration/deceleration rate on ice is inversely proportional to
382      * the current velocity.
383      */
384
385     // increasing 1 will increase acceleration/deceleration rate
386     // decreasing 1 will decrease acceleration/deceleration rate
387     //  must stay above zero, though
388     if (ax != 0) ax *= 1 / fabs(vx);
389   }
390
391   physic.set_velocity(vx, vy);
392   physic.set_acceleration(ax, ay);
393 }
394
395 void
396 Player::handle_vertical_input()
397 {
398   // Press jump key
399   if(input.up == DOWN && can_jump && on_ground())
400     {
401       if(duck) { // only jump a little bit when in duck mode {
402         physic.set_velocity_y(3);
403       } else {
404         // jump higher if we are running
405         if (fabs(physic.get_velocity_x()) > MAX_WALK_XM)
406           physic.set_velocity_y(5.8);
407         else
408           physic.set_velocity_y(5.2);
409       }
410
411       --base.y;
412       jumping = true;
413       can_jump = false;
414       butt_jump = true;  // player started jumping, enable butt jump
415       if (size == SMALL)
416         play_sound(sounds[SND_JUMP], SOUND_CENTER_SPEAKER);
417       else
418         play_sound(sounds[SND_BIGJUMP], SOUND_CENTER_SPEAKER);
419     }
420   // Let go of jump key
421   else if(input.up == UP && jumping && physic.get_velocity_y() > 0)
422     {
423       jumping = false;
424       physic.set_velocity_y(0);
425       butt_jump = false;  // jump was not full, disable butt jump
426     }
427
428    /* Do butt jump, in case the player has done the combination
429         (full jump and hold DOWN) */
430   if (input.down == UP && physic.get_velocity_y() == World::current()->get_level()->gravity && butt_jump)
431     butt_jump = false;  // in case DOWN is not hold after the full jump, disable it
432   
433   if (input.down == DOWN && butt_jump && on_ground() && size == BIG)
434   {
435     if(World::current()->trybreakbrick(base.x, base.y + base.height, false)
436       || World::current()->trybreakbrick(
437           base.x + base.width, base.y + base.height, false)) {
438         // make tux jumping a little bit again after breaking the bricks
439         physic.set_velocity_y(2);
440     }
441 //    butt_jump = false;
442   }
443
444   if ( (issolid(base.x + base.width / 2, base.y + base.height + 64) ||
445         issolid(base.x + 1, base.y + base.height + 64) ||
446         issolid(base.x + base.width - 1, base.y + base.height + 64))
447        && jumping  == false
448        && can_jump == false
449        && input.up == DOWN
450        && input.old_up == UP)
451     {
452       can_jump = true;
453     }
454
455   if(on_ground())   /* Make sure jumping is off. */
456     jumping = false;
457
458   input.old_up = input.up;
459 }
460
461 void
462 Player::handle_input()
463 {
464   /* Handle horizontal movement: */
465     handle_horizontal_input();
466
467   /* Jump/jumping? */
468
469   if (on_ground() && input.up == UP)
470     can_jump = true;
471   handle_vertical_input();
472
473   /* Shoot! */
474   if (input.fire == DOWN && input.old_fire == UP && got_power != NONE_POWER)
475     {
476       holding_something = true;
477       World::current()->add_bullet(Vector(base.x, base.y + (base.height/2)),
478           physic.get_velocity_x(), dir);
479       input.old_fire = DOWN;
480     }
481
482   /* tux animations: */
483   if(!frame_timer.check())
484     {
485       frame_timer.start(25);
486       if (input.right == UP && input.left == UP)
487         {
488           frame_main = 1;
489           frame_ = 1;
490         }
491       else
492         {
493           if ((input.fire == DOWN && (global_frame_counter % 2) == 0) ||
494               (global_frame_counter % 4) == 0)
495             frame_main = (frame_main + 1) % 4;
496
497           frame_ = frame_main;
498
499           if (frame_ == 3)
500             frame_ = 1;
501         }
502     }
503
504   /* Duck! */
505   if (input.down == DOWN && size == BIG && !duck && physic.get_velocity_y() == 0 && on_ground())
506     {
507       duck = true;
508       base.height = 32;                             
509       base.y += 32;
510       // changing base size confuses collision otherwise
511       old_base = previous_base = base;
512     }
513   else if(input.down == UP && size == BIG && duck)
514     {
515       // try if we can really unduck
516       base.y -= 32;
517       base.height = 64;
518       // when unducking in air we need some space to do so
519       if(on_ground() || !collision_object_map(base)) {
520         duck = false;
521         // changing base size confuses collision otherwise
522         old_base = previous_base = base;                                
523       } else {
524         // undo the ducking changes
525         base.y += 32;
526         base.height = 32;
527       }   
528     }
529 }
530
531 void
532 Player::grow()
533 {
534   if(size == BIG)
535     return;
536   
537   size = BIG;
538   base.height = 64;
539   base.y -= 32;
540
541   old_base = previous_base = base;
542 }
543
544 void
545 Player::grabdistros()
546 {
547   /* Grab distros: */
548   if (!dying)
549     {
550       World::current()->trygrabdistro(base.x, base.y, NO_BOUNCE);
551       World::current()->trygrabdistro(base.x+ 31, base.y, NO_BOUNCE);
552
553       World::current()->trygrabdistro(base.x, base.y + base.height, NO_BOUNCE);
554       World::current()->trygrabdistro(base.x+ 31, base.y + base.height, NO_BOUNCE);
555
556       if(size == BIG)
557         {
558           World::current()->trygrabdistro(base.x, base.y + base.height / 2, NO_BOUNCE);
559           World::current()->trygrabdistro(base.x+ 31, base.y + base.height / 2, NO_BOUNCE);
560         }
561
562     }
563
564   /* Enough distros for a One-up? */
565   if (player_status.distros >= DISTROS_LIFEUP)
566     {
567       player_status.distros = player_status.distros - DISTROS_LIFEUP;
568       if(player_status.lives < MAX_LIVES)
569         ++player_status.lives;
570       /*We want to hear the sound even, if MAX_LIVES is reached*/
571       play_sound(sounds[SND_LIFEUP], SOUND_CENTER_SPEAKER);
572     }
573 }
574
575 void
576 Player::draw(ViewPort& viewport, int )
577 {
578   Vector pos = viewport.world2screen(Vector(base.x, base.y));
579
580   if (!safe_timer.started() || (global_frame_counter % 2) == 0)
581     {
582       if (dying == DYING_SQUISHED)
583         {
584           smalltux_gameover->draw(pos);
585         }
586       else
587         {
588           PlayerSprite* sprite;
589           
590           if (size == SMALL)
591             sprite = &smalltux;
592           else if (got_power == FIRE_POWER)
593             sprite = &firetux;
594           else if (got_power == ICE_POWER)
595             sprite = &icetux;
596           else
597             sprite = &largetux;
598           
599           if (duck && size != SMALL)
600             {
601               if (dir == RIGHT)
602                 sprite->duck_right->draw(pos);
603               else 
604                 sprite->duck_left->draw(pos);
605             }
606           else if (skidding_timer.started())
607             {
608               if (dir == RIGHT)
609                 sprite->skid_right->draw(pos);
610               else
611                 sprite->skid_left->draw(pos); 
612             }
613           else if (kick_timer.started())
614             {
615               if (dir == RIGHT)
616                 sprite->kick_right->draw(pos);
617               else
618                 sprite->kick_left->draw(pos); 
619             }
620           else if (physic.get_velocity_y() != 0)
621             {
622               if (dir == RIGHT)
623                 sprite->jump_right->draw(pos);
624               else
625                 sprite->jump_left->draw(pos);                   
626             }
627           else
628             {
629               if (fabsf(physic.get_velocity_x()) < 1.0f) // standing
630                 {
631                   if (dir == RIGHT)
632                     sprite->stand_right->draw(pos);
633                   else
634                     sprite->stand_left->draw(pos);
635                 }
636               else // moving
637                 {
638                   if (dir == RIGHT)
639                     sprite->walk_right->draw(pos);
640                   else
641                     sprite->walk_left->draw(pos);
642                 }
643             }
644                       
645           // Draw arm overlay graphics when Tux is holding something
646           if (holding_something && physic.get_velocity_y() == 0)
647             {
648               if (dir == RIGHT)
649                 sprite->grab_right->draw(pos);
650               else
651                 sprite->grab_left->draw(pos);
652             }
653
654           // Draw blinking star overlay
655           if (invincible_timer.started() &&
656              (invincible_timer.get_left() > TUX_INVINCIBLE_TIME_WARNING || global_frame_counter % 3))
657             {
658               if (size == SMALL || duck)
659                 smalltux_star->draw(pos);
660               else
661                 largetux_star->draw(pos);
662             }
663         }
664     }     
665   
666   if (debug_mode)
667     fillrect(base.x - scroll_x, base.y - scroll_y, 
668              base.width, base.height, 75,75,75, 150);
669 }
670
671 void
672 Player::collision(const MovingObject& other, int collision_type)
673 {
674   (void) other;
675   (void) collision_type;
676   // will be implemented later
677 }
678
679 void
680 Player::collision(void* p_c_object, int c_object)
681 {
682   BadGuy* pbad_c = NULL;
683   Trampoline* ptramp_c = NULL;
684
685   switch (c_object)
686     {
687     case CO_BADGUY:
688       pbad_c = (BadGuy*) p_c_object;
689
690      /* Hurt player if he touches a badguy */
691       if (!pbad_c->dying && !dying &&
692           !safe_timer.started() &&
693           pbad_c->mode != BadGuy::HELD)
694         {
695           if (pbad_c->mode == BadGuy::FLAT && input.fire == DOWN
696                && !holding_something)
697             {
698               holding_something = true;
699               pbad_c->mode = BadGuy::HELD;
700               pbad_c->base.y-=8;
701             }
702           else if (pbad_c->mode == BadGuy::FLAT)
703             {
704               // Don't get hurt if we're kicking a flat badguy!
705             }
706           else if (pbad_c->mode == BadGuy::KICK)
707             {
708               /* Hurt if you get hit by kicked laptop: */
709               if (!invincible_timer.started())
710                 {
711                   kill(SHRINK);
712                 }
713               else
714                 pbad_c->kill_me(20);
715             }
716           else if (pbad_c->frozen_timer.check() && (pbad_c->kind == BAD_MRBOMB
717               || pbad_c->kind == BAD_JUMPY || pbad_c->kind == BAD_FISH
718               || pbad_c->kind == BAD_SPIKY))
719                 pbad_c->kill_me(20);
720           else
721             {
722               if (!invincible_timer.started())
723                 {
724                   kill(SHRINK);
725                 }
726               else
727                 {
728                   pbad_c->kill_me(25);
729                 }
730             }
731           player_status.score_multiplier++;
732         }
733       break;
734
735     case CO_TRAMPOLINE:
736       ptramp_c = (Trampoline*) p_c_object;
737       
738       // Pick up trampoline
739       if (ptramp_c->mode != Trampoline::M_HELD && input.fire == DOWN && !holding_something && on_ground())
740       {
741         holding_something = true;
742         ptramp_c->mode = Trampoline::M_HELD;
743         ptramp_c->base.y -= 8;
744       }
745       // Set down trampoline
746       else if (ptramp_c->mode == Trampoline::M_HELD && input.fire != DOWN)
747       {
748         holding_something = false;
749         ptramp_c->mode = Trampoline::M_NORMAL;
750         ptramp_c->base.y += 8;
751         ptramp_c->physic.set_velocity(physic.get_velocity_x(), physic.get_velocity_y());
752
753         //if (dir == RIGHT)
754         //  ptramp_c->base.x = base.x + base.width+1;
755         //else /* LEFT */
756         //  ptramp_c->base.x = base.x - base.width-1;
757       }
758 /*
759       // Don't let tux walk through trampoline
760       else if (ptramp_c->mode != Trampoline::M_HELD && on_ground())
761       {
762         if (physic.get_velocity_x() > 0) // RIGHT
763         {
764           physic.set_velocity_x(0);
765           base.x = ptramp_c->base.x - base.width;
766         }
767         else if (physic.get_velocity_x() < 0) // LEFT
768         {
769           physic.set_velocity_x(0);
770           base.x = ptramp_c->base.x + ptramp_c->base.width;
771         }
772       }
773 */
774
775       break;
776
777     default:
778       break;
779     }
780
781 }
782
783 /* Kill Player! */
784
785 void
786 Player::kill(HurtMode mode)
787 {
788   play_sound(sounds[SND_HURT], SOUND_CENTER_SPEAKER);
789
790   physic.set_velocity_x(0);
791
792   if (mode == SHRINK && size == BIG)
793     {
794       if (got_power != NONE_POWER)
795         {
796           got_power = NONE_POWER;
797         }
798       else
799         {
800           size = SMALL;
801           base.height = 32;
802           duck = false;
803         }
804       safe_timer.start(TUX_SAFE_TIME);
805     }
806   else
807     {
808       physic.enable_gravity(true);
809       physic.set_acceleration(0, 0);
810       physic.set_velocity(0, 7);
811       if(dying != DYING_SQUISHED)
812       --player_status.lives;
813       dying = DYING_SQUISHED;
814     }
815 }
816
817 void
818 Player::is_dying()
819 {
820   remove_powerups();
821   dying = DYING_NOT;
822 }
823
824 bool Player::is_dead()
825 {
826   if(base.y > screen->h + scroll_y || base.y > World::current()->get_level()->height*32 ||
827       base.x < scroll_x - AUTOSCROLL_DEAD_INTERVAL)  // can happen in auto-scrolling
828     return true;
829   else
830     return false;
831 }
832
833 /* Remove Tux's power ups */
834 void
835 Player::remove_powerups()
836 {
837   got_power = NONE_POWER;
838   size = SMALL;
839   base.height = 32;
840 }
841
842 void
843 Player::check_bounds(bool back_scrolling, bool hor_autoscroll)
844 {
845   /* Keep tux in bounds: */
846   if (base.x < 0)
847     { // Lock Tux to the size of the level, so that he doesn't fall of
848       // on the left side
849       base.x = 0;
850     }
851
852   /* Keep in-bounds, vertically: */
853   if (base.y > World::current()->get_level()->height * /*TILE_HEIGHT*/ 32)
854     {
855       kill(KILL);
856     }
857
858   if(base.x < scroll_x && (!back_scrolling || hor_autoscroll))  // can happen if back scrolling is disabled
859     base.x = scroll_x;
860
861   if(hor_autoscroll)
862     {
863     if(base.x == scroll_x)
864       if(issolid(base.x+32, base.y) || (size != SMALL && issolid(base.x+32, base.y+32)))
865         kill(KILL);
866
867     if(base.x + base.width > scroll_x + screen->w)
868       base.x = scroll_x + screen->w - base.width;
869     }
870     
871 }
872
873 // EOF //
874