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