rnd-20051123-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 1
108     if (SkipPressedMouseMotionEvent(event))
109       handle_this_event = FALSE;
110 #endif
111
112     if (handle_this_event)
113       return TRUE;
114   }
115
116   return FALSE;
117 }
118
119 void EventLoop(void)
120 {
121   while (1)
122   {
123     if (PendingEvent())         /* got event */
124     {
125       Event event;
126
127       while (NextValidEvent(&event))
128       {
129         switch(event.type)
130         {
131           case EVENT_BUTTONPRESS:
132           case EVENT_BUTTONRELEASE:
133             HandleButtonEvent((ButtonEvent *) &event);
134             break;
135   
136           case EVENT_MOTIONNOTIFY:
137             HandleMotionEvent((MotionEvent *) &event);
138             break;
139   
140           case EVENT_KEYPRESS:
141           case EVENT_KEYRELEASE:
142             HandleKeyEvent((KeyEvent *) &event);
143             break;
144   
145           default:
146             HandleOtherEvents(&event);
147             break;
148         }
149       }
150     }
151     else
152     {
153       /* when playing, display a special mouse pointer inside the playfield */
154       if (game_status == GAME_MODE_PLAYING && !tape.pausing)
155       {
156         if (!playfield_cursor_set && cursor_inside_playfield &&
157             DelayReached(&playfield_cursor_delay, 1000))
158         {
159           SetMouseCursor(CURSOR_PLAYFIELD);
160           playfield_cursor_set = TRUE;
161         }
162       }
163       else if (playfield_cursor_set)
164       {
165         SetMouseCursor(CURSOR_DEFAULT);
166         playfield_cursor_set = FALSE;
167       }
168
169       HandleNoEvent();
170     }
171
172     /* don't use all CPU time when idle; the main loop while playing
173        has its own synchronization and is CPU friendly, too */
174
175     if (game_status == GAME_MODE_PLAYING)
176       HandleGameActions();
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   motion_status = FALSE;
319
320   if (event->type == EVENT_BUTTONPRESS)
321     button_status = event->button;
322   else
323     button_status = MB_RELEASED;
324
325 #if 0
326   printf("::: button %s\n", event->type == EVENT_BUTTONPRESS ?
327         "pressed" : "released");
328 #endif
329
330   HandleButton(event->x, event->y, button_status);
331 }
332
333 void HandleMotionEvent(MotionEvent *event)
334 {
335   if (!PointerInWindow(window))
336     return;     /* window and pointer are on different screens */
337
338 #if 1
339   if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR)
340     return;
341 #endif
342
343   motion_status = TRUE;
344
345 #if 0
346   printf("::: %d, %d\n", event->x, event->y);
347 #endif
348
349   HandleButton(event->x, event->y, button_status);
350 }
351
352 void HandleKeyEvent(KeyEvent *event)
353 {
354   int key_status = (event->type==EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
355   boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
356   Key key = GetEventKey(event, with_modifiers);
357   Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
358
359   HandleKeyModState(keymod, key_status);
360   HandleKey(key, key_status);
361 }
362
363 void HandleFocusEvent(FocusChangeEvent *event)
364 {
365   static int old_joystick_status = -1;
366
367   if (event->type == EVENT_FOCUSOUT)
368   {
369     KeyboardAutoRepeatOn();
370     old_joystick_status = joystick.status;
371     joystick.status = JOYSTICK_NOT_AVAILABLE;
372
373     ClearPlayerAction();
374   }
375   else if (event->type == EVENT_FOCUSIN)
376   {
377     /* When there are two Rocks'n'Diamonds windows which overlap and
378        the player moves the pointer from one game window to the other,
379        a 'FocusOut' event is generated for the window the pointer is
380        leaving and a 'FocusIn' event is generated for the window the
381        pointer is entering. In some cases, it can happen that the
382        'FocusIn' event is handled by the one game process before the
383        'FocusOut' event by the other game process. In this case the
384        X11 environment would end up with activated keyboard auto repeat,
385        because unfortunately this is a global setting and not (which
386        would be far better) set for each X11 window individually.
387        The effect would be keyboard auto repeat while playing the game
388        (game_status == GAME_MODE_PLAYING), which is not desired.
389        To avoid this special case, we just wait 1/10 second before
390        processing the 'FocusIn' event.
391     */
392
393     if (game_status == GAME_MODE_PLAYING)
394     {
395       Delay(100);
396       KeyboardAutoRepeatOffUnlessAutoplay();
397     }
398
399     if (old_joystick_status != -1)
400       joystick.status = old_joystick_status;
401   }
402 }
403
404 void HandleClientMessageEvent(ClientMessageEvent *event)
405 {
406   if (CheckCloseWindowEvent(event))
407     CloseAllAndExit(0);
408 }
409
410 void HandleButton(int mx, int my, int button)
411 {
412   static int old_mx = 0, old_my = 0;
413
414   if (button < 0)
415   {
416     mx = old_mx;
417     my = old_my;
418     button = -button;
419   }
420   else
421   {
422     old_mx = mx;
423     old_my = my;
424   }
425
426   if (HandleGadgets(mx, my, button))
427   {
428     /* do not handle this button event anymore */
429     mx = my = -32;      /* force mouse event to be outside screen tiles */
430   }
431
432   switch(game_status)
433   {
434     case GAME_MODE_MAIN:
435       HandleMainMenu(mx,my, 0,0, button);
436       break;
437
438     case GAME_MODE_PSEUDO_TYPENAME:
439       HandleTypeName(0, KSYM_Return);
440       break;
441
442     case GAME_MODE_LEVELS:
443       HandleChooseLevel(mx,my, 0,0, button);
444       break;
445
446     case GAME_MODE_SCORES:
447       HandleHallOfFame(0,0, 0,0, button);
448       break;
449
450     case GAME_MODE_EDITOR:
451       break;
452
453     case GAME_MODE_INFO:
454       HandleInfoScreen(mx,my, 0,0, button);
455       break;
456
457     case GAME_MODE_SETUP:
458       HandleSetupScreen(mx,my, 0,0, button);
459       break;
460
461     case GAME_MODE_PLAYING:
462 #ifdef DEBUG
463       if (button == MB_PRESSED && !motion_status && IN_GFX_SCREEN(mx, my))
464         DumpTile(LEVELX((mx - SX) / TILEX), LEVELY((my - SY) / TILEY));
465 #endif
466       break;
467
468     default:
469       break;
470   }
471 }
472
473 static boolean is_string_suffix(char *string, char *suffix)
474 {
475   int string_len = strlen(string);
476   int suffix_len = strlen(suffix);
477
478   if (suffix_len > string_len)
479     return FALSE;
480
481   return (strcmp(&string[string_len - suffix_len], suffix) == 0);
482 }
483
484 #define MAX_CHEAT_INPUT_LEN     32
485
486 static void HandleKeysSpecial(Key key)
487 {
488   static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
489   char letter = getCharFromKey(key);
490   int cheat_input_len = strlen(cheat_input);
491   int i;
492
493   if (letter == 0)
494     return;
495
496   if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
497   {
498     for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
499       cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
500
501     cheat_input_len = MAX_CHEAT_INPUT_LEN;
502   }
503
504   cheat_input[cheat_input_len++] = letter;
505   cheat_input[cheat_input_len] = '\0';
506
507 #if 0
508   printf("::: '%s' [%d]\n", cheat_input, cheat_input_len);
509 #endif
510
511   if (game_status == GAME_MODE_MAIN)
512   {
513     if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
514         is_string_suffix(cheat_input, ":ist"))
515     {
516       InsertSolutionTape();
517     }
518     else if (is_string_suffix(cheat_input, ":reload-graphics") ||
519              is_string_suffix(cheat_input, ":rg"))
520     {
521       ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
522       DrawMainMenu();
523     }
524     else if (is_string_suffix(cheat_input, ":reload-sounds") ||
525              is_string_suffix(cheat_input, ":rs"))
526     {
527       ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
528       DrawMainMenu();
529     }
530     else if (is_string_suffix(cheat_input, ":reload-music") ||
531              is_string_suffix(cheat_input, ":rm"))
532     {
533       ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
534       DrawMainMenu();
535     }
536     else if (is_string_suffix(cheat_input, ":reload-artwork") ||
537              is_string_suffix(cheat_input, ":ra"))
538     {
539       ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
540                           1 << ARTWORK_TYPE_SOUNDS |
541                           1 << ARTWORK_TYPE_MUSIC);
542       DrawMainMenu();
543     }
544     else if (is_string_suffix(cheat_input, ":dump-level") ||
545              is_string_suffix(cheat_input, ":dl"))
546     {
547       DumpLevel(&level);
548     }
549     else if (is_string_suffix(cheat_input, ":dump-tape") ||
550              is_string_suffix(cheat_input, ":dt"))
551     {
552       DumpTape(&tape);
553     }
554   }
555   else if (game_status == GAME_MODE_PLAYING)
556   {
557 #ifdef DEBUG
558     if (is_string_suffix(cheat_input, ".q"))
559       for (i = 0; i < MAX_INVENTORY_SIZE; i++)
560         if (local_player->inventory_size < MAX_INVENTORY_SIZE)
561           local_player->inventory_element[local_player->inventory_size++] =
562             EL_DYNAMITE;
563 #endif
564   }
565   else if (game_status == GAME_MODE_EDITOR)
566   {
567     if (is_string_suffix(cheat_input, ":dump-brush") ||
568         is_string_suffix(cheat_input, ":DB"))
569     {
570       DumpBrush();
571     }
572     else if (is_string_suffix(cheat_input, ":DDB"))
573     {
574       DumpBrush_Small();
575     }
576   }
577 }
578
579 void HandleKey(Key key, int key_status)
580 {
581   int joy = 0;
582   boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
583   static struct SetupKeyboardInfo custom_key;
584   static struct
585   {
586     Key *key_custom;
587     Key key_default;
588     byte action;
589   } key_info[] =
590   {
591     { &custom_key.left,  DEFAULT_KEY_LEFT,  JOY_LEFT     },
592     { &custom_key.right, DEFAULT_KEY_RIGHT, JOY_RIGHT    },
593     { &custom_key.up,    DEFAULT_KEY_UP,    JOY_UP       },
594     { &custom_key.down,  DEFAULT_KEY_DOWN,  JOY_DOWN     },
595     { &custom_key.snap,  DEFAULT_KEY_SNAP,  JOY_BUTTON_1 },
596     { &custom_key.drop,  DEFAULT_KEY_DROP,  JOY_BUTTON_2 }
597   };
598
599   if (game_status == GAME_MODE_PLAYING)
600   {
601     /* only needed for single-step tape recording mode */
602     static boolean clear_button_2[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
603     static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
604     int pnr;
605
606     for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
607     {
608       int i;
609       byte key_action = 0;
610
611       if (setup.input[pnr].use_joystick)
612         continue;
613
614       custom_key = setup.input[pnr].key;
615
616       for (i = 0; i < 6; i++)
617         if (key == *key_info[i].key_custom)
618           key_action |= key_info[i].action;
619
620       if (tape.single_step && clear_button_2[pnr])
621       {
622         stored_player[pnr].action &= ~KEY_BUTTON_2;
623         clear_button_2[pnr] = FALSE;
624       }
625
626       if (key_status == KEY_PRESSED)
627         stored_player[pnr].action |= key_action;
628       else
629         stored_player[pnr].action &= ~key_action;
630
631       if (tape.single_step && tape.recording && tape.pausing)
632       {
633         if (key_status == KEY_PRESSED &&
634             (key_action & (KEY_MOTION | KEY_BUTTON_1)))
635         {
636           TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
637
638           if (key_action & KEY_MOTION)
639           {
640             if (stored_player[pnr].action & KEY_BUTTON_2)
641               element_dropped[pnr] = TRUE;
642           }
643         }
644         else if (key_status == KEY_RELEASED &&
645                  (key_action & KEY_BUTTON_2))
646         {
647           if (!element_dropped[pnr])
648           {
649             TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
650
651             stored_player[pnr].action |= KEY_BUTTON_2;
652             clear_button_2[pnr] = TRUE;
653           }
654
655           element_dropped[pnr] = FALSE;
656         }
657       }
658       else if (tape.recording && tape.pausing && (key_action & KEY_ACTION))
659         TapeTogglePause(TAPE_TOGGLE_MANUAL);
660     }
661   }
662   else
663   {
664     int i;
665
666     for (i = 0; i < 6; i++)
667       if (key == key_info[i].key_default)
668         joy |= key_info[i].action;
669   }
670
671   if (joy)
672   {
673     if (key_status == KEY_PRESSED)
674       key_joystick_mapping |= joy;
675     else
676       key_joystick_mapping &= ~joy;
677
678     HandleJoystick();
679   }
680
681   if (game_status != GAME_MODE_PLAYING)
682     key_joystick_mapping = 0;
683
684   if (key_status == KEY_RELEASED)
685     return;
686
687   if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
688       (key == KSYM_Return || key == setup.shortcut.toggle_pause))
689   {
690     CloseDoor(DOOR_CLOSE_1);
691     game_status = GAME_MODE_MAIN;
692     DrawMainMenu();
693
694     return;
695   }
696
697   if (game_status == GAME_MODE_MAIN && key == setup.shortcut.toggle_pause)
698   {
699 #if 1
700     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
701 #else
702     if (setup.autorecord)
703       TapeStartRecording();
704
705 #if defined(NETWORK_AVALIABLE)
706     if (options.network)
707       SendToServer_StartPlaying();
708     else
709 #endif
710     {
711       game_status = GAME_MODE_PLAYING;
712       StopAnimation();
713       InitGame();
714     }
715 #endif
716
717     return;
718   }
719
720   if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
721   {
722     if (key == setup.shortcut.save_game)
723       TapeQuickSave();
724     else if (key == setup.shortcut.load_game)
725       TapeQuickLoad();
726     else if (key == setup.shortcut.toggle_pause)
727       TapeTogglePause(TAPE_TOGGLE_MANUAL);
728   }
729
730   HandleKeysSpecial(key);
731
732   if (HandleGadgetsKeyInput(key))
733   {
734     if (key != KSYM_Escape)     /* always allow ESC key to be handled */
735       key = KSYM_UNDEFINED;
736   }
737
738   switch(game_status)
739   {
740     case GAME_MODE_PSEUDO_TYPENAME:
741       HandleTypeName(0, key);
742       break;
743
744     case GAME_MODE_MAIN:
745     case GAME_MODE_LEVELS:
746     case GAME_MODE_SETUP:
747     case GAME_MODE_INFO:
748       switch(key)
749       {
750         case KSYM_space:
751         case KSYM_Return:
752           if (game_status == GAME_MODE_MAIN)
753             HandleMainMenu(0,0, 0,0, MB_MENU_CHOICE);
754           else if (game_status == GAME_MODE_LEVELS)
755             HandleChooseLevel(0,0, 0,0, MB_MENU_CHOICE);
756           else if (game_status == GAME_MODE_SETUP)
757             HandleSetupScreen(0,0, 0,0, MB_MENU_CHOICE);
758           else if (game_status == GAME_MODE_INFO)
759             HandleInfoScreen(0,0, 0,0, MB_MENU_CHOICE);
760           break;
761
762         case KSYM_Escape:
763           if (game_status == GAME_MODE_LEVELS)
764             HandleChooseLevel(0,0, 0,0, MB_MENU_LEAVE);
765           else if (game_status == GAME_MODE_SETUP)
766             HandleSetupScreen(0,0, 0,0, MB_MENU_LEAVE);
767           else if (game_status == GAME_MODE_INFO)
768             HandleInfoScreen(0,0, 0,0, MB_MENU_LEAVE);
769           break;
770
771         case KSYM_Page_Up:
772           if (game_status == GAME_MODE_LEVELS)
773             HandleChooseLevel(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
774           else if (game_status == GAME_MODE_SETUP)
775             HandleSetupScreen(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
776           else if (game_status == GAME_MODE_INFO)
777             HandleInfoScreen(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
778           break;
779
780         case KSYM_Page_Down:
781           if (game_status == GAME_MODE_LEVELS)
782             HandleChooseLevel(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
783           else if (game_status == GAME_MODE_SETUP)
784             HandleSetupScreen(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
785           else if (game_status == GAME_MODE_INFO)
786             HandleInfoScreen(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
787           break;
788
789         default:
790           break;
791       }
792       break;
793
794     case GAME_MODE_SCORES:
795       switch(key)
796       {
797         case KSYM_space:
798         case KSYM_Return:
799         case KSYM_Escape:
800           game_status = GAME_MODE_MAIN;
801           DrawMainMenu();
802           break;
803
804         case KSYM_Page_Up:
805           HandleHallOfFame(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
806           break;
807
808         case KSYM_Page_Down:
809           HandleHallOfFame(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
810           break;
811
812         default:
813           break;
814       }
815       break;
816
817     case GAME_MODE_EDITOR:
818       if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
819         HandleLevelEditorKeyInput(key);
820       break;
821
822     case GAME_MODE_PLAYING:
823     {
824       switch(key)
825       {
826         case KSYM_Escape:
827           RequestQuitGame(setup.ask_on_escape);
828           break;
829
830 #ifdef DEBUG
831         case KSYM_0:
832         case KSYM_1:
833         case KSYM_2:
834         case KSYM_3:
835         case KSYM_4:
836         case KSYM_5:
837         case KSYM_6:
838         case KSYM_7:
839         case KSYM_8:
840         case KSYM_9:
841           if (key == KSYM_0)
842           {
843             if (GameFrameDelay == 500)
844               GameFrameDelay = GAME_FRAME_DELAY;
845             else
846               GameFrameDelay = 500;
847           }
848           else
849             GameFrameDelay = (key - KSYM_0) * 10;
850           printf("Game speed == %d%% (%d ms delay between two frames)\n",
851                  GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
852           break;
853
854         case KSYM_d:
855           if (options.debug)
856           {
857             options.debug = FALSE;
858             printf("debug mode disabled\n");
859           }
860           else
861           {
862             options.debug = TRUE;
863             printf("debug mode enabled\n");
864           }
865           break;
866
867         case KSYM_S:
868           if (!global.fps_slowdown)
869           {
870             global.fps_slowdown = TRUE;
871             global.fps_slowdown_factor = 2;
872             printf("fps slowdown enabled -- display only every 2nd frame\n");
873           }
874           else if (global.fps_slowdown_factor == 2)
875           {
876             global.fps_slowdown_factor = 4;
877             printf("fps slowdown enabled -- display only every 4th frame\n");
878           }
879           else
880           {
881             global.fps_slowdown = FALSE;
882             global.fps_slowdown_factor = 1;
883             printf("fps slowdown disabled\n");
884           }
885           break;
886
887 #if 0
888         case KSYM_a:
889           if (ScrollStepSize == TILEX/8)
890             ScrollStepSize = TILEX/4;
891           else
892             ScrollStepSize = TILEX/8;
893           printf("ScrollStepSize == %d\n", ScrollStepSize);
894           break;
895 #endif
896
897 #if 0
898         case KSYM_m:
899           if (MoveSpeed == 8)
900           {
901             MoveSpeed = 4;
902             ScrollStepSize = TILEX/4;
903           }
904           else
905           {
906             MoveSpeed = 8;
907             ScrollStepSize = TILEX/8;
908           }
909           printf("MoveSpeed == %d\n", MoveSpeed);
910           break;
911 #endif
912
913         case KSYM_f:
914           ScrollStepSize = TILEX/8;
915           printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
916           break;
917
918         case KSYM_g:
919           ScrollStepSize = TILEX/4;
920           printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
921           break;
922
923         case KSYM_h:
924           ScrollStepSize = TILEX/2;
925           printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
926           break;
927
928         case KSYM_l:
929           ScrollStepSize = TILEX;
930           printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
931           break;
932
933         case KSYM_v:
934           printf("::: currently using game engine version %d\n",
935                  game.engine_version);
936           break;
937
938 #if 0
939
940         case KSYM_z:
941           {
942             int i;
943
944             for (i = 0; i < MAX_PLAYERS; i++)
945             {
946               printf("Player %d:\n", i);
947               printf("  jx == %d, jy == %d\n",
948                      stored_player[i].jx, stored_player[i].jy);
949               printf("  last_jx == %d, last_jy == %d\n",
950                      stored_player[i].last_jx, stored_player[i].last_jy);
951             }
952             printf("\n");
953           }
954
955           break;
956 #endif
957 #endif
958
959         default:
960           break;
961       }
962       break;
963     }
964
965     default:
966       if (key == KSYM_Escape)
967       {
968         game_status = GAME_MODE_MAIN;
969         DrawMainMenu();
970
971         return;
972       }
973   }
974 }
975
976 void HandleNoEvent()
977 {
978   if (button_status && game_status != GAME_MODE_PLAYING)
979   {
980     HandleButton(0, 0, -button_status);
981     return;
982   }
983
984 #if defined(NETWORK_AVALIABLE)
985   if (options.network)
986     HandleNetworking();
987 #endif
988
989   HandleJoystick();
990 }
991
992 static int HandleJoystickForAllPlayers()
993 {
994   int i;
995   int result = 0;
996
997   for (i = 0; i < MAX_PLAYERS; i++)
998   {
999     byte joy_action = 0;
1000
1001     /*
1002     if (!setup.input[i].use_joystick)
1003       continue;
1004       */
1005
1006     joy_action = Joystick(i);
1007     result |= joy_action;
1008
1009     if (!setup.input[i].use_joystick)
1010       continue;
1011
1012     stored_player[i].action = joy_action;
1013   }
1014
1015   return result;
1016 }
1017
1018 void HandleJoystick()
1019 {
1020   int joystick  = HandleJoystickForAllPlayers();
1021   int keyboard  = key_joystick_mapping;
1022   int joy       = (joystick | keyboard);
1023   int left      = joy & JOY_LEFT;
1024   int right     = joy & JOY_RIGHT;
1025   int up        = joy & JOY_UP;
1026   int down      = joy & JOY_DOWN;
1027   int button    = joy & JOY_BUTTON;
1028   int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1029   int dx        = (left ? -1    : right ? 1     : 0);
1030   int dy        = (up   ? -1    : down  ? 1     : 0);
1031
1032   switch(game_status)
1033   {
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_MAIN)
1046         HandleMainMenu(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1047       else if (game_status == GAME_MODE_LEVELS)
1048         HandleChooseLevel(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1049       else if (game_status == GAME_MODE_SETUP)
1050         HandleSetupScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1051       else if (game_status == GAME_MODE_INFO)
1052         HandleInfoScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1053       break;
1054     }
1055
1056     case GAME_MODE_SCORES:
1057       HandleHallOfFame(0,0, dx,dy, !newbutton);
1058       break;
1059
1060     case GAME_MODE_EDITOR:
1061       HandleLevelEditorIdle();
1062       break;
1063
1064     case GAME_MODE_PLAYING:
1065       if (tape.playing || keyboard)
1066         newbutton = ((joy & JOY_BUTTON) != 0);
1067
1068       if (AllPlayersGone && newbutton)
1069       {
1070         CloseDoor(DOOR_CLOSE_1);
1071         game_status = GAME_MODE_MAIN;
1072         DrawMainMenu();
1073         return;
1074       }
1075
1076       break;
1077
1078     default:
1079       break;
1080   }
1081 }