rnd-20060226-3-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       HandleLevelEditorIdle();
439       break;
440
441     case GAME_MODE_INFO:
442       HandleInfoScreen(mx,my, 0,0, button);
443       break;
444
445     case GAME_MODE_SETUP:
446       HandleSetupScreen(mx,my, 0,0, button);
447       break;
448
449     case GAME_MODE_PLAYING:
450 #ifdef DEBUG
451       if (button == MB_PRESSED && !motion_status && IN_GFX_SCREEN(mx, my))
452         DumpTile(LEVELX((mx - SX) / TILEX), LEVELY((my - SY) / TILEY));
453 #endif
454       break;
455
456     default:
457       break;
458   }
459 }
460
461 static boolean is_string_suffix(char *string, char *suffix)
462 {
463   int string_len = strlen(string);
464   int suffix_len = strlen(suffix);
465
466   if (suffix_len > string_len)
467     return FALSE;
468
469   return (strcmp(&string[string_len - suffix_len], suffix) == 0);
470 }
471
472 #define MAX_CHEAT_INPUT_LEN     32
473
474 static void HandleKeysSpecial(Key key)
475 {
476   static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
477   char letter = getCharFromKey(key);
478   int cheat_input_len = strlen(cheat_input);
479   int i;
480
481   if (letter == 0)
482     return;
483
484   if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
485   {
486     for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
487       cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
488
489     cheat_input_len = MAX_CHEAT_INPUT_LEN;
490   }
491
492   cheat_input[cheat_input_len++] = letter;
493   cheat_input[cheat_input_len] = '\0';
494
495 #if 0
496   printf("::: '%s' [%d]\n", cheat_input, cheat_input_len);
497 #endif
498
499   if (game_status == GAME_MODE_MAIN)
500   {
501     if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
502         is_string_suffix(cheat_input, ":ist"))
503     {
504       InsertSolutionTape();
505     }
506     else if (is_string_suffix(cheat_input, ":reload-graphics") ||
507              is_string_suffix(cheat_input, ":rg"))
508     {
509       ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
510       DrawMainMenu();
511     }
512     else if (is_string_suffix(cheat_input, ":reload-sounds") ||
513              is_string_suffix(cheat_input, ":rs"))
514     {
515       ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
516       DrawMainMenu();
517     }
518     else if (is_string_suffix(cheat_input, ":reload-music") ||
519              is_string_suffix(cheat_input, ":rm"))
520     {
521       ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
522       DrawMainMenu();
523     }
524     else if (is_string_suffix(cheat_input, ":reload-artwork") ||
525              is_string_suffix(cheat_input, ":ra"))
526     {
527       ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
528                           1 << ARTWORK_TYPE_SOUNDS |
529                           1 << ARTWORK_TYPE_MUSIC);
530       DrawMainMenu();
531     }
532     else if (is_string_suffix(cheat_input, ":dump-level") ||
533              is_string_suffix(cheat_input, ":dl"))
534     {
535       DumpLevel(&level);
536     }
537     else if (is_string_suffix(cheat_input, ":dump-tape") ||
538              is_string_suffix(cheat_input, ":dt"))
539     {
540       DumpTape(&tape);
541     }
542   }
543   else if (game_status == GAME_MODE_PLAYING)
544   {
545 #ifdef DEBUG
546     if (is_string_suffix(cheat_input, ".q"))
547       for (i = 0; i < MAX_INVENTORY_SIZE; i++)
548         if (local_player->inventory_size < MAX_INVENTORY_SIZE)
549           local_player->inventory_element[local_player->inventory_size++] =
550             EL_DYNAMITE;
551 #endif
552   }
553   else if (game_status == GAME_MODE_EDITOR)
554   {
555     if (is_string_suffix(cheat_input, ":dump-brush") ||
556         is_string_suffix(cheat_input, ":DB"))
557     {
558       DumpBrush();
559     }
560     else if (is_string_suffix(cheat_input, ":DDB"))
561     {
562       DumpBrush_Small();
563     }
564   }
565 }
566
567 void HandleKey(Key key, int key_status)
568 {
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   int joy = 0;
586   int i;
587
588   if (game_status == GAME_MODE_PLAYING)
589   {
590     /* only needed for single-step tape recording mode */
591     static boolean clear_button_2[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
592     static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
593     int pnr;
594
595     for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
596     {
597       byte key_action = 0;
598
599       if (setup.input[pnr].use_joystick)
600         continue;
601
602       custom_key = setup.input[pnr].key;
603
604       for (i = 0; i < 6; i++)
605         if (key == *key_info[i].key_custom)
606           key_action |= key_info[i].action;
607
608       if (tape.single_step && clear_button_2[pnr])
609       {
610         stored_player[pnr].action &= ~KEY_BUTTON_2;
611         clear_button_2[pnr] = FALSE;
612       }
613
614       if (key_status == KEY_PRESSED)
615         stored_player[pnr].action |= key_action;
616       else
617         stored_player[pnr].action &= ~key_action;
618
619       if (tape.single_step && tape.recording && tape.pausing)
620       {
621         if (key_status == KEY_PRESSED &&
622             (key_action & (KEY_MOTION | KEY_BUTTON_1)))
623         {
624           TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
625
626           if (key_action & KEY_MOTION)
627           {
628             if (stored_player[pnr].action & KEY_BUTTON_2)
629               element_dropped[pnr] = TRUE;
630           }
631         }
632         else if (key_status == KEY_RELEASED &&
633                  (key_action & KEY_BUTTON_2))
634         {
635           if (!element_dropped[pnr])
636           {
637             TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
638
639             stored_player[pnr].action |= KEY_BUTTON_2;
640             clear_button_2[pnr] = TRUE;
641           }
642
643           element_dropped[pnr] = FALSE;
644         }
645       }
646       else if (tape.recording && tape.pausing && (key_action & KEY_ACTION))
647         TapeTogglePause(TAPE_TOGGLE_MANUAL);
648     }
649   }
650   else
651   {
652     for (i = 0; i < 6; i++)
653       if (key == key_info[i].key_default)
654         joy |= key_info[i].action;
655   }
656
657   if (joy)
658   {
659     if (key_status == KEY_PRESSED)
660       key_joystick_mapping |= joy;
661     else
662       key_joystick_mapping &= ~joy;
663
664     HandleJoystick();
665   }
666
667   if (game_status != GAME_MODE_PLAYING)
668     key_joystick_mapping = 0;
669
670   if (key_status == KEY_RELEASED)
671     return;
672
673   if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
674       (key == KSYM_Return || key == setup.shortcut.toggle_pause))
675   {
676     CloseDoor(DOOR_CLOSE_1);
677     game_status = GAME_MODE_MAIN;
678     DrawMainMenu();
679
680     return;
681   }
682
683   if (game_status == GAME_MODE_MAIN &&
684       (key == setup.shortcut.toggle_pause || key == KSYM_space))
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   if (game_status == GAME_MODE_PLAYING)
702   {
703     if (key == setup.shortcut.focus_player_all)
704       game.centered_player_nr_next = -1;
705     else
706       for (i = 0; i < MAX_PLAYERS; i++)
707         if (key == setup.shortcut.focus_player[i])
708           game.centered_player_nr_next = i;
709   }
710
711   HandleKeysSpecial(key);
712
713   if (HandleGadgetsKeyInput(key))
714   {
715     if (key != KSYM_Escape)     /* always allow ESC key to be handled */
716       key = KSYM_UNDEFINED;
717   }
718
719   switch(game_status)
720   {
721     case GAME_MODE_PSEUDO_TYPENAME:
722       HandleTypeName(0, key);
723       break;
724
725     case GAME_MODE_MAIN:
726     case GAME_MODE_LEVELS:
727     case GAME_MODE_SETUP:
728     case GAME_MODE_INFO:
729       switch(key)
730       {
731 #if 1
732         case KSYM_space:
733 #else
734         /* !!! only use "space" key to start game from main menu !!! */
735         case KSYM_space:
736 #endif
737         case KSYM_Return:
738           if (game_status == GAME_MODE_MAIN)
739             HandleMainMenu(0,0, 0,0, MB_MENU_CHOICE);
740           else if (game_status == GAME_MODE_LEVELS)
741             HandleChooseLevel(0,0, 0,0, MB_MENU_CHOICE);
742           else if (game_status == GAME_MODE_SETUP)
743             HandleSetupScreen(0,0, 0,0, MB_MENU_CHOICE);
744           else if (game_status == GAME_MODE_INFO)
745             HandleInfoScreen(0,0, 0,0, MB_MENU_CHOICE);
746           break;
747
748         case KSYM_Escape:
749           if (game_status == GAME_MODE_LEVELS)
750             HandleChooseLevel(0,0, 0,0, MB_MENU_LEAVE);
751           else if (game_status == GAME_MODE_SETUP)
752             HandleSetupScreen(0,0, 0,0, MB_MENU_LEAVE);
753           else if (game_status == GAME_MODE_INFO)
754             HandleInfoScreen(0,0, 0,0, MB_MENU_LEAVE);
755           break;
756
757         case KSYM_Page_Up:
758           if (game_status == GAME_MODE_LEVELS)
759             HandleChooseLevel(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
760           else if (game_status == GAME_MODE_SETUP)
761             HandleSetupScreen(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
762           else if (game_status == GAME_MODE_INFO)
763             HandleInfoScreen(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
764           break;
765
766         case KSYM_Page_Down:
767           if (game_status == GAME_MODE_LEVELS)
768             HandleChooseLevel(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
769           else if (game_status == GAME_MODE_SETUP)
770             HandleSetupScreen(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
771           else if (game_status == GAME_MODE_INFO)
772             HandleInfoScreen(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
773           break;
774
775 #ifdef DEBUG
776         case KSYM_0:
777           GameFrameDelay = (GameFrameDelay == 500 ? GAME_FRAME_DELAY : 500);
778           break;
779 #endif
780
781         default:
782           break;
783       }
784       break;
785
786     case GAME_MODE_SCORES:
787       switch(key)
788       {
789         case KSYM_space:
790         case KSYM_Return:
791         case KSYM_Escape:
792           game_status = GAME_MODE_MAIN;
793           DrawMainMenu();
794           break;
795
796         case KSYM_Page_Up:
797           HandleHallOfFame(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
798           break;
799
800         case KSYM_Page_Down:
801           HandleHallOfFame(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
802           break;
803
804         default:
805           break;
806       }
807       break;
808
809     case GAME_MODE_EDITOR:
810       if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
811         HandleLevelEditorKeyInput(key);
812       break;
813
814     case GAME_MODE_PLAYING:
815     {
816       switch(key)
817       {
818         case KSYM_Escape:
819           RequestQuitGame(setup.ask_on_escape);
820           break;
821
822 #ifdef DEBUG
823         case KSYM_0:
824 #if 0
825         case KSYM_1:
826         case KSYM_2:
827         case KSYM_3:
828         case KSYM_4:
829         case KSYM_5:
830         case KSYM_6:
831         case KSYM_7:
832         case KSYM_8:
833         case KSYM_9:
834 #endif
835           if (key == KSYM_0)
836           {
837             if (GameFrameDelay == 500)
838               GameFrameDelay = GAME_FRAME_DELAY;
839             else
840               GameFrameDelay = 500;
841           }
842           else
843             GameFrameDelay = (key - KSYM_0) * 10;
844           printf("Game speed == %d%% (%d ms delay between two frames)\n",
845                  GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
846           break;
847
848         case KSYM_d:
849           if (options.debug)
850           {
851             options.debug = FALSE;
852             printf("debug mode disabled\n");
853           }
854           else
855           {
856             options.debug = TRUE;
857             printf("debug mode enabled\n");
858           }
859           break;
860
861         case KSYM_S:
862           if (!global.fps_slowdown)
863           {
864             global.fps_slowdown = TRUE;
865             global.fps_slowdown_factor = 2;
866             printf("fps slowdown enabled -- display only every 2nd frame\n");
867           }
868           else if (global.fps_slowdown_factor == 2)
869           {
870             global.fps_slowdown_factor = 4;
871             printf("fps slowdown enabled -- display only every 4th frame\n");
872           }
873           else
874           {
875             global.fps_slowdown = FALSE;
876             global.fps_slowdown_factor = 1;
877             printf("fps slowdown disabled\n");
878           }
879           break;
880
881         case KSYM_f:
882           ScrollStepSize = TILEX/8;
883           printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
884           break;
885
886         case KSYM_g:
887           ScrollStepSize = TILEX/4;
888           printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
889           break;
890
891         case KSYM_h:
892           ScrollStepSize = TILEX/2;
893           printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
894           break;
895
896         case KSYM_l:
897           ScrollStepSize = TILEX;
898           printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
899           break;
900
901         case KSYM_v:
902           printf("::: currently using game engine version %d\n",
903                  game.engine_version);
904           break;
905 #endif
906
907         default:
908           break;
909       }
910       break;
911     }
912
913     default:
914       if (key == KSYM_Escape)
915       {
916         game_status = GAME_MODE_MAIN;
917         DrawMainMenu();
918
919         return;
920       }
921   }
922 }
923
924 void HandleNoEvent()
925 {
926   if (button_status && game_status != GAME_MODE_PLAYING)
927   {
928     HandleButton(0, 0, -button_status);
929     return;
930   }
931
932 #if defined(NETWORK_AVALIABLE)
933   if (options.network)
934     HandleNetworking();
935 #endif
936
937   HandleJoystick();
938 }
939
940 static int HandleJoystickForAllPlayers()
941 {
942   int i;
943   int result = 0;
944
945   for (i = 0; i < MAX_PLAYERS; i++)
946   {
947     byte joy_action = 0;
948
949     /*
950     if (!setup.input[i].use_joystick)
951       continue;
952       */
953
954     joy_action = Joystick(i);
955     result |= joy_action;
956
957     if (!setup.input[i].use_joystick)
958       continue;
959
960     stored_player[i].action = joy_action;
961   }
962
963   return result;
964 }
965
966 void HandleJoystick()
967 {
968   int joystick  = HandleJoystickForAllPlayers();
969   int keyboard  = key_joystick_mapping;
970   int joy       = (joystick | keyboard);
971   int left      = joy & JOY_LEFT;
972   int right     = joy & JOY_RIGHT;
973   int up        = joy & JOY_UP;
974   int down      = joy & JOY_DOWN;
975   int button    = joy & JOY_BUTTON;
976   int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
977   int dx        = (left ? -1    : right ? 1     : 0);
978   int dy        = (up   ? -1    : down  ? 1     : 0);
979
980   switch(game_status)
981   {
982     case GAME_MODE_MAIN:
983     case GAME_MODE_LEVELS:
984     case GAME_MODE_SETUP:
985     case GAME_MODE_INFO:
986     {
987       static unsigned long joystickmove_delay = 0;
988
989       if (joystick && !button &&
990           !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
991         newbutton = dx = dy = 0;
992
993       if (game_status == GAME_MODE_MAIN)
994         HandleMainMenu(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
995       else if (game_status == GAME_MODE_LEVELS)
996         HandleChooseLevel(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
997       else if (game_status == GAME_MODE_SETUP)
998         HandleSetupScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
999       else if (game_status == GAME_MODE_INFO)
1000         HandleInfoScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1001       break;
1002     }
1003
1004     case GAME_MODE_SCORES:
1005       HandleHallOfFame(0,0, dx,dy, !newbutton);
1006       break;
1007
1008     case GAME_MODE_EDITOR:
1009       HandleLevelEditorIdle();
1010       break;
1011
1012     case GAME_MODE_PLAYING:
1013       if (tape.playing || keyboard)
1014         newbutton = ((joy & JOY_BUTTON) != 0);
1015
1016       if (AllPlayersGone && newbutton)
1017       {
1018         CloseDoor(DOOR_CLOSE_1);
1019         game_status = GAME_MODE_MAIN;
1020         DrawMainMenu();
1021         return;
1022       }
1023
1024       break;
1025
1026     default:
1027       break;
1028   }
1029 }