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