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