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