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