rnd-20030404-4-src
[rocksndiamonds.git] / src / events.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * events.c                                                 *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "events.h"
17 #include "init.h"
18 #include "screens.h"
19 #include "tools.h"
20 #include "game.h"
21 #include "editor.h"
22 #include "files.h"
23 #include "tape.h"
24 #include "network.h"
25
26 /* values for key_status */
27 #define KEY_NOT_PRESSED         FALSE
28 #define KEY_RELEASED            FALSE
29 #define KEY_PRESSED             TRUE
30
31
32 /* event filter especially needed for SDL event filtering due to
33    delay problems with lots of mouse motion events when mouse button
34    not pressed (X11 can handle this with 'PointerMotionHintMask') */
35
36 int FilterMouseMotionEvents(const Event *event)
37 {
38   /* non-motion events are directly passed to event handler functions */
39   if (event->type != EVENT_MOTIONNOTIFY)
40     return 1;
41
42   /* when playing, display a different mouse pointer inside the playfield */
43   if (game_status == PLAYING)
44   {
45     static boolean inside_field = FALSE;
46     MotionEvent *motion = (MotionEvent *)event;
47
48     if ((motion->x >= SX && motion->x < SX + SXSIZE &&
49          motion->y >= SY && motion->y < SY + SYSIZE) != inside_field)
50     {
51       inside_field = !inside_field;
52
53       SetMouseCursor(inside_field ? CURSOR_PLAYFIELD : CURSOR_DEFAULT);
54     }
55   }
56
57   /* skip mouse motion events without pressed button outside level editor */
58   if (button_status == MB_RELEASED && game_status != LEVELED)
59     return 0;
60   else
61     return 1;
62 }
63
64 /* this is only really needed for non-SDL targets to filter unwanted events;
65    when using SDL with properly installed event filter, this function can be
66    replaced with a simple "NextEvent()" call, but it doesn't hurt either */
67
68 static boolean NextValidEvent(Event *event)
69 {
70   while (PendingEvent())
71   {
72     NextEvent(event);
73
74     if (FilterMouseMotionEvents(event))
75       return TRUE;
76   }
77
78   return FALSE;
79 }
80
81 void EventLoop(void)
82 {
83   while(1)
84   {
85     if (PendingEvent())         /* got event */
86     {
87       Event event;
88
89       if (NextValidEvent(&event))
90       {
91         switch(event.type)
92         {
93           case EVENT_BUTTONPRESS:
94           case EVENT_BUTTONRELEASE:
95             HandleButtonEvent((ButtonEvent *) &event);
96             break;
97   
98           case EVENT_MOTIONNOTIFY:
99             HandleMotionEvent((MotionEvent *) &event);
100             break;
101   
102           case EVENT_KEYPRESS:
103           case EVENT_KEYRELEASE:
104             HandleKeyEvent((KeyEvent *) &event);
105             break;
106   
107           default:
108             HandleOtherEvents(&event);
109             break;
110         }
111       }
112     }
113     else
114       HandleNoEvent();
115
116     /* don't use all CPU time when idle; the main loop while playing
117        has its own synchronization and is CPU friendly, too */
118
119     if (game_status == PLAYING)
120       HandleGameActions();
121     else
122     {
123       SyncDisplay();
124       if (!PendingEvent())      /* delay only if no pending events */
125         Delay(10);
126     }
127
128     /* refresh window contents from drawing buffer, if needed */
129     BackToFront();
130
131     if (game_status == EXITGAME)
132       return;
133   }
134 }
135
136 void HandleOtherEvents(Event *event)
137 {
138   switch(event->type)
139   {
140     case EVENT_EXPOSE:
141       HandleExposeEvent((ExposeEvent *) event);
142       break;
143
144     case EVENT_UNMAPNOTIFY:
145 #if 0
146       /* This causes the game to stop not only when iconified, but also
147          when on another virtual desktop, which might be not desired. */
148       SleepWhileUnmapped();
149 #endif
150       break;
151
152     case EVENT_FOCUSIN:
153     case EVENT_FOCUSOUT:
154       HandleFocusEvent((FocusChangeEvent *) event);
155       break;
156
157     case EVENT_CLIENTMESSAGE:
158       HandleClientMessageEvent((ClientMessageEvent *) event);
159       break;
160
161 #if defined(TARGET_SDL)
162     case SDL_JOYAXISMOTION:
163     case SDL_JOYBUTTONDOWN:
164     case SDL_JOYBUTTONUP:
165       HandleJoystickEvent(event);
166       break;
167 #endif
168
169     default:
170       break;
171   }
172 }
173
174 void ClearEventQueue()
175 {
176   while (PendingEvent())
177   {
178     Event event;
179
180     NextEvent(&event);
181
182     switch(event.type)
183     {
184       case EVENT_BUTTONRELEASE:
185         button_status = MB_RELEASED;
186         break;
187
188       case EVENT_KEYRELEASE:
189         key_joystick_mapping = 0;
190         break;
191
192       default:
193         HandleOtherEvents(&event);
194         break;
195     }
196   }
197 }
198
199 void ClearPlayerAction()
200 {
201   int i;
202
203   /* simulate key release events for still pressed keys */
204   key_joystick_mapping = 0;
205   for (i=0; i<MAX_PLAYERS; i++)
206     stored_player[i].action = 0;
207 }
208
209 void SleepWhileUnmapped()
210 {
211   boolean window_unmapped = TRUE;
212
213   KeyboardAutoRepeatOn();
214
215   while(window_unmapped)
216   {
217     Event event;
218
219     NextEvent(&event);
220
221     switch(event.type)
222     {
223       case EVENT_BUTTONRELEASE:
224         button_status = MB_RELEASED;
225         break;
226
227       case EVENT_KEYRELEASE:
228         key_joystick_mapping = 0;
229         break;
230
231       case EVENT_MAPNOTIFY:
232         window_unmapped = FALSE;
233         break;
234
235       case EVENT_UNMAPNOTIFY:
236         /* this is only to surely prevent the 'should not happen' case
237          * of recursively looping between 'SleepWhileUnmapped()' and
238          * 'HandleOtherEvents()' which usually calls this funtion.
239          */
240         break;
241
242       default:
243         HandleOtherEvents(&event);
244         break;
245     }
246   }
247
248   if (game_status == PLAYING)
249     KeyboardAutoRepeatOff();
250 }
251
252 void HandleExposeEvent(ExposeEvent *event)
253 {
254 #ifndef TARGET_SDL
255   RedrawPlayfield(FALSE, event->x, event->y, event->width, event->height);
256   FlushDisplay();
257 #endif
258 }
259
260 void HandleButtonEvent(ButtonEvent *event)
261 {
262   motion_status = FALSE;
263
264   if (event->type == EVENT_BUTTONPRESS)
265     button_status = event->button;
266   else
267     button_status = MB_RELEASED;
268
269   HandleButton(event->x, event->y, button_status);
270 }
271
272 void HandleMotionEvent(MotionEvent *event)
273 {
274   if (!PointerInWindow(window))
275     return;     /* window and pointer are on different screens */
276
277 #if 1
278   if (button_status == MB_RELEASED && game_status != LEVELED)
279     return;
280 #endif
281
282   motion_status = TRUE;
283
284   HandleButton(event->x, event->y, button_status);
285 }
286
287 void HandleKeyEvent(KeyEvent *event)
288 {
289   int key_status = (event->type==EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
290   boolean with_modifiers = (game_status == PLAYING ? FALSE : TRUE);
291   Key key = GetEventKey(event, with_modifiers);
292
293   HandleKey(key, key_status);
294 }
295
296 void HandleFocusEvent(FocusChangeEvent *event)
297 {
298   static int old_joystick_status = -1;
299
300   if (event->type == EVENT_FOCUSOUT)
301   {
302     KeyboardAutoRepeatOn();
303     old_joystick_status = joystick.status;
304     joystick.status = JOYSTICK_NOT_AVAILABLE;
305
306     ClearPlayerAction();
307   }
308   else if (event->type == EVENT_FOCUSIN)
309   {
310     /* When there are two Rocks'n'Diamonds windows which overlap and
311        the player moves the pointer from one game window to the other,
312        a 'FocusOut' event is generated for the window the pointer is
313        leaving and a 'FocusIn' event is generated for the window the
314        pointer is entering. In some cases, it can happen that the
315        'FocusIn' event is handled by the one game process before the
316        'FocusOut' event by the other game process. In this case the
317        X11 environment would end up with activated keyboard auto repeat,
318        because unfortunately this is a global setting and not (which
319        would be far better) set for each X11 window individually.
320        The effect would be keyboard auto repeat while playing the game
321        (game_status == PLAYING), which is not desired.
322        To avoid this special case, we just wait 1/10 second before
323        processing the 'FocusIn' event.
324     */
325
326     if (game_status == PLAYING)
327     {
328       Delay(100);
329       KeyboardAutoRepeatOff();
330     }
331     if (old_joystick_status != -1)
332       joystick.status = old_joystick_status;
333   }
334 }
335
336 void HandleClientMessageEvent(ClientMessageEvent *event)
337 {
338   if (CheckCloseWindowEvent(event))
339     CloseAllAndExit(0);
340 }
341
342 void HandleButton(int mx, int my, int button)
343 {
344   static int old_mx = 0, old_my = 0;
345
346   if (button < 0)
347   {
348     mx = old_mx;
349     my = old_my;
350     button = -button;
351   }
352   else
353   {
354     old_mx = mx;
355     old_my = my;
356   }
357
358   HandleGadgets(mx, my, button);
359
360   switch(game_status)
361   {
362     case MAINMENU:
363       HandleMainMenu(mx,my, 0,0, button);
364       break;
365
366     case TYPENAME:
367       HandleTypeName(0, KSYM_Return);
368       break;
369
370     case CHOOSELEVEL:
371       HandleChooseLevel(mx,my, 0,0, button);
372       break;
373
374     case HALLOFFAME:
375       HandleHallOfFame(0,0, 0,0, button);
376       break;
377
378     case LEVELED:
379       break;
380
381     case HELPSCREEN:
382       HandleHelpScreen(button);
383       break;
384
385     case SETUP:
386       HandleSetupScreen(mx,my, 0,0, button);
387       break;
388
389     case PLAYING:
390 #ifdef DEBUG
391       if (button == MB_RELEASED)
392       {
393         int sx = (mx - SX) / TILEX;
394         int sy = (my - SY) / TILEY;
395
396         if (IN_VIS_FIELD(sx,sy))
397         {
398           int x = LEVELX(sx);
399           int y = LEVELY(sy);
400
401           printf("INFO: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
402
403           if (!IN_LEV_FIELD(x, y))
404             break;
405
406           printf("      Feld[%d][%d] == %d\n", x,y, Feld[x][y]);
407           printf("      Store[%d][%d] == %d\n", x,y, Store[x][y]);
408           printf("      Store2[%d][%d] == %d\n", x,y, Store2[x][y]);
409           printf("      StorePlayer[%d][%d] == %d\n", x,y, StorePlayer[x][y]);
410           printf("      MovPos[%d][%d] == %d\n", x,y, MovPos[x][y]);
411           printf("      MovDir[%d][%d] == %d\n", x,y, MovDir[x][y]);
412           printf("      MovDelay[%d][%d] == %d\n", x,y, MovDelay[x][y]);
413           printf("\n");
414         }
415       }
416 #endif
417       break;
418
419     default:
420       break;
421   }
422 }
423
424 void HandleKey(Key key, int key_status)
425 {
426   int joy = 0;
427   boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
428   static struct SetupKeyboardInfo custom_key;
429   static struct
430   {
431     Key *key_custom;
432     Key key_default;
433     byte action;
434   } key_info[] =
435   {
436     { &custom_key.left,  DEFAULT_KEY_LEFT,  JOY_LEFT     },
437     { &custom_key.right, DEFAULT_KEY_RIGHT, JOY_RIGHT    },
438     { &custom_key.up,    DEFAULT_KEY_UP,    JOY_UP       },
439     { &custom_key.down,  DEFAULT_KEY_DOWN,  JOY_DOWN     },
440     { &custom_key.snap,  DEFAULT_KEY_SNAP,  JOY_BUTTON_1 },
441     { &custom_key.bomb,  DEFAULT_KEY_BOMB,  JOY_BUTTON_2 }
442   };
443
444   if (game_status == PLAYING)
445   {
446     /* only needed for single-step tape recording mode */
447     static boolean clear_button_2[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
448     static boolean bomb_placed[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
449     int pnr;
450
451     for (pnr=0; pnr<MAX_PLAYERS; pnr++)
452     {
453       int i;
454       byte key_action = 0;
455
456       if (setup.input[pnr].use_joystick)
457         continue;
458
459       custom_key = setup.input[pnr].key;
460
461       for (i=0; i<6; i++)
462         if (key == *key_info[i].key_custom)
463           key_action |= key_info[i].action;
464
465       if (tape.single_step && clear_button_2[pnr])
466       {
467         stored_player[pnr].action &= ~KEY_BUTTON_2;
468         clear_button_2[pnr] = FALSE;
469       }
470
471       if (key_status == KEY_PRESSED)
472         stored_player[pnr].action |= key_action;
473       else
474         stored_player[pnr].action &= ~key_action;
475
476       if (tape.single_step && tape.recording && tape.pausing)
477       {
478         if (key_status == KEY_PRESSED &&
479             (key_action & (KEY_MOTION | KEY_BUTTON_1)))
480         {
481           TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
482
483           if (key_action & KEY_MOTION)
484           {
485             if (stored_player[pnr].action & KEY_BUTTON_2)
486               bomb_placed[pnr] = TRUE;
487           }
488         }
489         else if (key_status == KEY_RELEASED &&
490                  (key_action & KEY_BUTTON_2))
491         {
492           if (!bomb_placed[pnr])
493           {
494             TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
495
496             stored_player[pnr].action |= KEY_BUTTON_2;
497             clear_button_2[pnr] = TRUE;
498           }
499
500           bomb_placed[pnr] = FALSE;
501         }
502       }
503       else if (tape.recording && tape.pausing && (key_action & KEY_ACTION))
504         TapeTogglePause(TAPE_TOGGLE_MANUAL);
505     }
506   }
507   else
508   {
509     int i;
510
511     for (i=0; i<6; i++)
512       if (key == key_info[i].key_default)
513         joy |= key_info[i].action;
514   }
515
516   if (joy)
517   {
518     if (key_status == KEY_PRESSED)
519       key_joystick_mapping |= joy;
520     else
521       key_joystick_mapping &= ~joy;
522
523     HandleJoystick();
524   }
525
526   if (game_status != PLAYING)
527     key_joystick_mapping = 0;
528
529   if (key_status == KEY_RELEASED)
530     return;
531
532   if ((key == KSYM_Return || key == setup.shortcut.toggle_pause) &&
533       game_status == PLAYING && AllPlayersGone)
534   {
535     CloseDoor(DOOR_CLOSE_1);
536     game_status = MAINMENU;
537     DrawMainMenu();
538     return;
539   }
540
541   /* allow quick escape to the main menu with the Escape key */
542   if (key == KSYM_Escape &&
543       game_status != MAINMENU &&
544       game_status != PLAYING &&
545       game_status != LEVELED &&
546       game_status != CHOOSELEVEL &&
547       game_status != SETUP)
548   {
549     game_status = MAINMENU;
550     DrawMainMenu();
551     return;
552   }
553
554   /* special key shortcuts */
555   if (game_status == MAINMENU || game_status == PLAYING)
556   {
557     if (key == setup.shortcut.save_game)
558       TapeQuickSave();
559     else if (key == setup.shortcut.load_game)
560       TapeQuickLoad();
561     else if (key == setup.shortcut.toggle_pause)
562       TapeTogglePause(TAPE_TOGGLE_MANUAL);
563   }
564
565 #if 0
566 #ifndef DEBUG
567
568   if (game_status == PLAYING && (tape.playing || tape.pausing))
569     return;
570
571 #endif
572 #endif
573
574
575   HandleGadgetsKeyInput(key);
576
577   switch(game_status)
578   {
579     case TYPENAME:
580       HandleTypeName(0, key);
581       break;
582
583     case MAINMENU:
584     case CHOOSELEVEL:
585     case SETUP:
586       switch(key)
587       {
588         case KSYM_Return:
589           if (game_status == MAINMENU)
590             HandleMainMenu(0,0, 0,0, MB_MENU_CHOICE);
591           else if (game_status == CHOOSELEVEL)
592             HandleChooseLevel(0,0, 0,0, MB_MENU_CHOICE);
593           else if (game_status == SETUP)
594             HandleSetupScreen(0,0, 0,0, MB_MENU_CHOICE);
595           break;
596
597         case KSYM_Escape:
598           if (game_status == CHOOSELEVEL)
599             HandleChooseLevel(0,0, 0,0, MB_MENU_LEAVE);
600           else if (game_status == SETUP)
601             HandleSetupScreen(0,0, 0,0, MB_MENU_LEAVE);
602           break;
603
604         case KSYM_Page_Up:
605           if (game_status == CHOOSELEVEL)
606             HandleChooseLevel(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
607           else if (game_status == SETUP)
608             HandleSetupScreen(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
609           break;
610
611         case KSYM_Page_Down:
612           if (game_status == CHOOSELEVEL)
613             HandleChooseLevel(0,0, 0,SCR_FIELDY, MB_MENU_MARK);
614           else if (game_status == SETUP)
615             HandleSetupScreen(0,0, 0,SCR_FIELDY, MB_MENU_MARK);
616           break;
617
618 #ifdef DEBUG
619         case KSYM_t:
620           DumpTape(&tape);
621           break;
622 #endif
623
624         default:
625           break;
626       }
627       break;
628
629     case HELPSCREEN:
630       HandleHelpScreen(MB_RELEASED);
631       break;
632
633     case HALLOFFAME:
634       switch(key)
635       {
636         case KSYM_Return:
637           game_status = MAINMENU;
638           DrawMainMenu();
639           BackToFront();
640           break;
641
642         case KSYM_Page_Up:
643           HandleHallOfFame(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
644           break;
645
646         case KSYM_Page_Down:
647           HandleHallOfFame(0,0, 0,SCR_FIELDY, MB_MENU_MARK);
648           break;
649
650         default:
651           break;
652       }
653       break;
654
655     case LEVELED:
656       if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
657         HandleLevelEditorKeyInput(key);
658       break;
659
660     case PLAYING:
661     {
662       switch(key)
663       {
664         case KSYM_Escape:
665           RequestQuitGame(setup.ask_on_escape);
666           break;
667
668 #ifdef DEBUG
669         case KSYM_0:
670         case KSYM_1:
671         case KSYM_2:
672         case KSYM_3:
673         case KSYM_4:
674         case KSYM_5:
675         case KSYM_6:
676         case KSYM_7:
677         case KSYM_8:
678         case KSYM_9:
679           if (key == KSYM_0)
680           {
681             if (GameFrameDelay == 500)
682               GameFrameDelay = GAME_FRAME_DELAY;
683             else
684               GameFrameDelay = 500;
685           }
686           else
687             GameFrameDelay = (key - KSYM_0) * 10;
688           printf("Game speed == %d%% (%d ms delay between two frames)\n",
689                  GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
690           break;
691
692         case KSYM_d:
693           if (options.debug)
694           {
695             options.debug = FALSE;
696             printf("debug mode disabled\n");
697           }
698           else
699           {
700             options.debug = TRUE;
701             printf("debug mode enabled\n");
702           }
703           break;
704
705         case KSYM_s:
706           if (!global.fps_slowdown)
707           {
708             global.fps_slowdown = TRUE;
709             global.fps_slowdown_factor = 2;
710             printf("fps slowdown enabled -- display only every 2nd frame\n");
711           }
712           else if (global.fps_slowdown_factor == 2)
713           {
714             global.fps_slowdown_factor = 4;
715             printf("fps slowdown enabled -- display only every 4th frame\n");
716           }
717           else
718           {
719             global.fps_slowdown = FALSE;
720             global.fps_slowdown_factor = 1;
721             printf("fps slowdown disabled\n");
722           }
723           break;
724
725 #if 0
726         case KSYM_a:
727           if (ScrollStepSize == TILEX/8)
728             ScrollStepSize = TILEX/4;
729           else
730             ScrollStepSize = TILEX/8;
731           printf("ScrollStepSize == %d\n", ScrollStepSize);
732           break;
733 #endif
734
735 #if 0
736         case KSYM_m:
737           if (MoveSpeed == 8)
738           {
739             MoveSpeed = 4;
740             ScrollStepSize = TILEX/4;
741           }
742           else
743           {
744             MoveSpeed = 8;
745             ScrollStepSize = TILEX/8;
746           }
747           printf("MoveSpeed == %d\n", MoveSpeed);
748           break;
749 #endif
750
751         case KSYM_f:
752           ScrollStepSize = TILEX/8;
753           printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
754           break;
755
756         case KSYM_g:
757           ScrollStepSize = TILEX/4;
758           printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
759           break;
760
761         case KSYM_h:
762           ScrollStepSize = TILEX/2;
763           printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
764           break;
765
766         case KSYM_l:
767           ScrollStepSize = TILEX;
768           printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
769           break;
770
771         case KSYM_Q:
772         case KSYM_q:
773           local_player->dynamite = 1000;
774           break;
775
776
777
778 #if 0
779
780         case KSYM_z:
781           {
782             int i;
783
784             for(i=0; i<MAX_PLAYERS; i++)
785             {
786               printf("Player %d:\n", i);
787               printf("  jx == %d, jy == %d\n",
788                      stored_player[i].jx, stored_player[i].jy);
789               printf("  last_jx == %d, last_jy == %d\n",
790                      stored_player[i].last_jx, stored_player[i].last_jy);
791             }
792             printf("\n");
793           }
794
795           break;
796 #endif
797 #endif
798
799         default:
800           break;
801       }
802       break;
803     }
804     default:
805       break;
806   }
807 }
808
809 void HandleNoEvent()
810 {
811   if (button_status && game_status != PLAYING)
812   {
813     HandleButton(0, 0, -button_status);
814     return;
815   }
816
817 #if defined(PLATFORM_UNIX)
818   if (options.network)
819     HandleNetworking();
820 #endif
821
822   HandleJoystick();
823 }
824
825 static int HandleJoystickForAllPlayers()
826 {
827   int i;
828   int result = 0;
829
830   for (i=0; i<MAX_PLAYERS; i++)
831   {
832     byte joy_action = 0;
833
834     /*
835     if (!setup.input[i].use_joystick)
836       continue;
837       */
838
839     joy_action = Joystick(i);
840     result |= joy_action;
841
842     if (!setup.input[i].use_joystick)
843       continue;
844
845     stored_player[i].action = joy_action;
846   }
847
848   return result;
849 }
850
851 void HandleJoystick()
852 {
853   int joystick  = HandleJoystickForAllPlayers();
854   int keyboard  = key_joystick_mapping;
855   int joy       = (joystick | keyboard);
856   int left      = joy & JOY_LEFT;
857   int right     = joy & JOY_RIGHT;
858   int up        = joy & JOY_UP;
859   int down      = joy & JOY_DOWN;
860   int button    = joy & JOY_BUTTON;
861   int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
862   int dx        = (left ? -1    : right ? 1     : 0);
863   int dy        = (up   ? -1    : down  ? 1     : 0);
864
865   switch(game_status)
866   {
867     case MAINMENU:
868     case CHOOSELEVEL:
869     case SETUP:
870     {
871       static unsigned long joystickmove_delay = 0;
872
873       if (joystick && !button &&
874           !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
875         newbutton = dx = dy = 0;
876
877       if (game_status == MAINMENU)
878         HandleMainMenu(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
879       else if (game_status == CHOOSELEVEL)
880         HandleChooseLevel(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
881       else if (game_status == SETUP)
882         HandleSetupScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
883       break;
884     }
885
886     case HALLOFFAME:
887       HandleHallOfFame(0,0, dx,dy, !newbutton);
888       break;
889
890     case HELPSCREEN:
891       HandleHelpScreen(!newbutton);
892       break;
893
894     case LEVELED:
895       HandleLevelEditorIdle();
896       break;
897
898     case PLAYING:
899       if (tape.playing || keyboard)
900         newbutton = ((joy & JOY_BUTTON) != 0);
901
902       if (AllPlayersGone && newbutton)
903       {
904         CloseDoor(DOOR_CLOSE_1);
905         game_status = MAINMENU;
906         DrawMainMenu();
907         return;
908       }
909
910       break;
911
912     default:
913       break;
914   }
915 }