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