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