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