rnd-20060804-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     {
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 (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
691       (key == KSYM_Return || key == setup.shortcut.toggle_pause))
692   {
693     CloseDoor(DOOR_CLOSE_1);
694     game_status = GAME_MODE_MAIN;
695     DrawMainMenu();
696
697     return;
698   }
699
700   if (game_status == GAME_MODE_MAIN &&
701       (key == setup.shortcut.toggle_pause || key == KSYM_space))
702   {
703     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
704
705     return;
706   }
707
708   if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
709   {
710     if (key == setup.shortcut.save_game)
711       TapeQuickSave();
712     else if (key == setup.shortcut.load_game)
713       TapeQuickLoad();
714     else if (key == setup.shortcut.toggle_pause)
715       TapeTogglePause(TAPE_TOGGLE_MANUAL);
716   }
717
718   if (game_status == GAME_MODE_PLAYING && !network_playing)
719   {
720     int centered_player_nr_next = -999;
721
722     if (key == setup.shortcut.focus_player_all)
723       centered_player_nr_next = -1;
724     else
725       for (i = 0; i < MAX_PLAYERS; i++)
726         if (key == setup.shortcut.focus_player[i])
727           centered_player_nr_next = i;
728
729     if (centered_player_nr_next != -999)
730     {
731       game.centered_player_nr_next = centered_player_nr_next;
732       game.set_centered_player = TRUE;
733
734       if (tape.recording)
735       {
736         tape.centered_player_nr_next = game.centered_player_nr_next;
737         tape.set_centered_player = TRUE;
738       }
739     }
740   }
741
742   HandleKeysSpecial(key);
743
744   if (HandleGadgetsKeyInput(key))
745   {
746     if (key != KSYM_Escape)     /* always allow ESC key to be handled */
747       key = KSYM_UNDEFINED;
748   }
749
750   switch(game_status)
751   {
752     case GAME_MODE_PSEUDO_TYPENAME:
753       HandleTypeName(0, key);
754       break;
755
756     case GAME_MODE_TITLE:
757     case GAME_MODE_MAIN:
758     case GAME_MODE_LEVELS:
759     case GAME_MODE_SETUP:
760     case GAME_MODE_INFO:
761       switch(key)
762       {
763 #if 1
764         case KSYM_space:
765 #else
766         /* !!! only use "space" key to start game from main menu !!! */
767         case KSYM_space:
768 #endif
769         case KSYM_Return:
770           if (game_status == GAME_MODE_TITLE)
771             HandleTitleScreen(0,0, 0,0, MB_MENU_CHOICE);
772           else if (game_status == GAME_MODE_MAIN)
773             HandleMainMenu(0,0, 0,0, MB_MENU_CHOICE);
774           else if (game_status == GAME_MODE_LEVELS)
775             HandleChooseLevel(0,0, 0,0, MB_MENU_CHOICE);
776           else if (game_status == GAME_MODE_SETUP)
777             HandleSetupScreen(0,0, 0,0, MB_MENU_CHOICE);
778           else if (game_status == GAME_MODE_INFO)
779             HandleInfoScreen(0,0, 0,0, MB_MENU_CHOICE);
780           break;
781
782         case KSYM_Escape:
783           if (game_status == GAME_MODE_TITLE)
784             HandleTitleScreen(0,0, 0,0, MB_MENU_LEAVE);
785           else if (game_status == GAME_MODE_LEVELS)
786             HandleChooseLevel(0,0, 0,0, MB_MENU_LEAVE);
787           else if (game_status == GAME_MODE_SETUP)
788             HandleSetupScreen(0,0, 0,0, MB_MENU_LEAVE);
789           else if (game_status == GAME_MODE_INFO)
790             HandleInfoScreen(0,0, 0,0, MB_MENU_LEAVE);
791           break;
792
793         case KSYM_Page_Up:
794           if (game_status == GAME_MODE_LEVELS)
795             HandleChooseLevel(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
796           else if (game_status == GAME_MODE_SETUP)
797             HandleSetupScreen(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
798           else if (game_status == GAME_MODE_INFO)
799             HandleInfoScreen(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
800           break;
801
802         case KSYM_Page_Down:
803           if (game_status == GAME_MODE_LEVELS)
804             HandleChooseLevel(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
805           else if (game_status == GAME_MODE_SETUP)
806             HandleSetupScreen(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
807           else if (game_status == GAME_MODE_INFO)
808             HandleInfoScreen(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
809           break;
810
811 #ifdef DEBUG
812         case KSYM_0:
813           GameFrameDelay = (GameFrameDelay == 500 ? GAME_FRAME_DELAY : 500);
814           break;
815 #endif
816
817         default:
818           break;
819       }
820       break;
821
822     case GAME_MODE_SCORES:
823       switch(key)
824       {
825         case KSYM_space:
826         case KSYM_Return:
827         case KSYM_Escape:
828           game_status = GAME_MODE_MAIN;
829           DrawMainMenu();
830           break;
831
832         case KSYM_Page_Up:
833           HandleHallOfFame(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
834           break;
835
836         case KSYM_Page_Down:
837           HandleHallOfFame(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
838           break;
839
840         default:
841           break;
842       }
843       break;
844
845     case GAME_MODE_EDITOR:
846       if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
847         HandleLevelEditorKeyInput(key);
848       break;
849
850     case GAME_MODE_PLAYING:
851     {
852       switch(key)
853       {
854         case KSYM_Escape:
855           RequestQuitGame(setup.ask_on_escape);
856           break;
857
858 #ifdef DEBUG
859         case KSYM_0:
860 #if 0
861         case KSYM_1:
862         case KSYM_2:
863         case KSYM_3:
864         case KSYM_4:
865         case KSYM_5:
866         case KSYM_6:
867         case KSYM_7:
868         case KSYM_8:
869         case KSYM_9:
870 #endif
871           if (key == KSYM_0)
872           {
873             if (GameFrameDelay == 500)
874               GameFrameDelay = GAME_FRAME_DELAY;
875             else
876               GameFrameDelay = 500;
877           }
878           else
879             GameFrameDelay = (key - KSYM_0) * 10;
880           printf("Game speed == %d%% (%d ms delay between two frames)\n",
881                  GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
882           break;
883
884         case KSYM_d:
885           if (options.debug)
886           {
887             options.debug = FALSE;
888             printf("debug mode disabled\n");
889           }
890           else
891           {
892             options.debug = TRUE;
893             printf("debug mode enabled\n");
894           }
895           break;
896
897         case KSYM_S:
898           if (!global.fps_slowdown)
899           {
900             global.fps_slowdown = TRUE;
901             global.fps_slowdown_factor = 2;
902             printf("fps slowdown enabled -- display only every 2nd frame\n");
903           }
904           else if (global.fps_slowdown_factor == 2)
905           {
906             global.fps_slowdown_factor = 4;
907             printf("fps slowdown enabled -- display only every 4th frame\n");
908           }
909           else
910           {
911             global.fps_slowdown = FALSE;
912             global.fps_slowdown_factor = 1;
913             printf("fps slowdown disabled\n");
914           }
915           break;
916
917         case KSYM_f:
918           ScrollStepSize = TILEX/8;
919           printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
920           break;
921
922         case KSYM_g:
923           ScrollStepSize = TILEX/4;
924           printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
925           break;
926
927         case KSYM_h:
928           ScrollStepSize = TILEX/2;
929           printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
930           break;
931
932         case KSYM_l:
933           ScrollStepSize = TILEX;
934           printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
935           break;
936
937         case KSYM_v:
938           printf("::: currently using game engine version %d\n",
939                  game.engine_version);
940           break;
941 #endif
942
943         default:
944           break;
945       }
946       break;
947     }
948
949     default:
950       if (key == KSYM_Escape)
951       {
952         game_status = GAME_MODE_MAIN;
953         DrawMainMenu();
954
955         return;
956       }
957   }
958 }
959
960 void HandleNoEvent()
961 {
962   if (button_status && game_status != GAME_MODE_PLAYING)
963   {
964     HandleButton(0, 0, -button_status, button_status);
965
966     return;
967   }
968
969 #if defined(NETWORK_AVALIABLE)
970   if (options.network)
971     HandleNetworking();
972 #endif
973
974   HandleJoystick();
975 }
976
977 static int HandleJoystickForAllPlayers()
978 {
979   int i;
980   int result = 0;
981
982   for (i = 0; i < MAX_PLAYERS; i++)
983   {
984     byte joy_action = 0;
985
986     /*
987     if (!setup.input[i].use_joystick)
988       continue;
989       */
990
991     joy_action = Joystick(i);
992     result |= joy_action;
993
994     if (!setup.input[i].use_joystick)
995       continue;
996
997     stored_player[i].action = joy_action;
998   }
999
1000   return result;
1001 }
1002
1003 void HandleJoystick()
1004 {
1005   int joystick  = HandleJoystickForAllPlayers();
1006   int keyboard  = key_joystick_mapping;
1007   int joy       = (joystick | keyboard);
1008   int left      = joy & JOY_LEFT;
1009   int right     = joy & JOY_RIGHT;
1010   int up        = joy & JOY_UP;
1011   int down      = joy & JOY_DOWN;
1012   int button    = joy & JOY_BUTTON;
1013   int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1014   int dx        = (left ? -1    : right ? 1     : 0);
1015   int dy        = (up   ? -1    : down  ? 1     : 0);
1016
1017   switch(game_status)
1018   {
1019     case GAME_MODE_TITLE:
1020     case GAME_MODE_MAIN:
1021     case GAME_MODE_LEVELS:
1022     case GAME_MODE_SETUP:
1023     case GAME_MODE_INFO:
1024     {
1025       static unsigned long joystickmove_delay = 0;
1026
1027       if (joystick && !button &&
1028           !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
1029         newbutton = dx = dy = 0;
1030
1031       if (game_status == GAME_MODE_TITLE)
1032         HandleTitleScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1033       else if (game_status == GAME_MODE_MAIN)
1034         HandleMainMenu(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1035       else if (game_status == GAME_MODE_LEVELS)
1036         HandleChooseLevel(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1037       else if (game_status == GAME_MODE_SETUP)
1038         HandleSetupScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1039       else if (game_status == GAME_MODE_INFO)
1040         HandleInfoScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1041       break;
1042     }
1043
1044     case GAME_MODE_SCORES:
1045       HandleHallOfFame(0,0, dx,dy, !newbutton);
1046       break;
1047
1048     case GAME_MODE_EDITOR:
1049       HandleLevelEditorIdle();
1050       break;
1051
1052     case GAME_MODE_PLAYING:
1053       if (tape.playing || keyboard)
1054         newbutton = ((joy & JOY_BUTTON) != 0);
1055
1056       if (AllPlayersGone && newbutton)
1057       {
1058         CloseDoor(DOOR_CLOSE_1);
1059         game_status = GAME_MODE_MAIN;
1060         DrawMainMenu();
1061         return;
1062       }
1063
1064       break;
1065
1066     default:
1067       break;
1068   }
1069 }