rnd-20050424-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   }
573 }
574
575 void HandleKey(Key key, int key_status)
576 {
577   int joy = 0;
578   boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
579   static struct SetupKeyboardInfo custom_key;
580   static struct
581   {
582     Key *key_custom;
583     Key key_default;
584     byte action;
585   } key_info[] =
586   {
587     { &custom_key.left,  DEFAULT_KEY_LEFT,  JOY_LEFT     },
588     { &custom_key.right, DEFAULT_KEY_RIGHT, JOY_RIGHT    },
589     { &custom_key.up,    DEFAULT_KEY_UP,    JOY_UP       },
590     { &custom_key.down,  DEFAULT_KEY_DOWN,  JOY_DOWN     },
591     { &custom_key.snap,  DEFAULT_KEY_SNAP,  JOY_BUTTON_1 },
592     { &custom_key.drop,  DEFAULT_KEY_DROP,  JOY_BUTTON_2 }
593   };
594
595   if (game_status == GAME_MODE_PLAYING)
596   {
597     /* only needed for single-step tape recording mode */
598     static boolean clear_button_2[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
599     static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
600     int pnr;
601
602     for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
603     {
604       int i;
605       byte key_action = 0;
606
607       if (setup.input[pnr].use_joystick)
608         continue;
609
610       custom_key = setup.input[pnr].key;
611
612       for (i = 0; i < 6; i++)
613         if (key == *key_info[i].key_custom)
614           key_action |= key_info[i].action;
615
616       if (tape.single_step && clear_button_2[pnr])
617       {
618         stored_player[pnr].action &= ~KEY_BUTTON_2;
619         clear_button_2[pnr] = FALSE;
620       }
621
622       if (key_status == KEY_PRESSED)
623         stored_player[pnr].action |= key_action;
624       else
625         stored_player[pnr].action &= ~key_action;
626
627       if (tape.single_step && tape.recording && tape.pausing)
628       {
629         if (key_status == KEY_PRESSED &&
630             (key_action & (KEY_MOTION | KEY_BUTTON_1)))
631         {
632           TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
633
634           if (key_action & KEY_MOTION)
635           {
636             if (stored_player[pnr].action & KEY_BUTTON_2)
637               element_dropped[pnr] = TRUE;
638           }
639         }
640         else if (key_status == KEY_RELEASED &&
641                  (key_action & KEY_BUTTON_2))
642         {
643           if (!element_dropped[pnr])
644           {
645             TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
646
647             stored_player[pnr].action |= KEY_BUTTON_2;
648             clear_button_2[pnr] = TRUE;
649           }
650
651           element_dropped[pnr] = FALSE;
652         }
653       }
654       else if (tape.recording && tape.pausing && (key_action & KEY_ACTION))
655         TapeTogglePause(TAPE_TOGGLE_MANUAL);
656     }
657   }
658   else
659   {
660     int i;
661
662     for (i = 0; i < 6; i++)
663       if (key == key_info[i].key_default)
664         joy |= key_info[i].action;
665   }
666
667   if (joy)
668   {
669     if (key_status == KEY_PRESSED)
670       key_joystick_mapping |= joy;
671     else
672       key_joystick_mapping &= ~joy;
673
674     HandleJoystick();
675   }
676
677   if (game_status != GAME_MODE_PLAYING)
678     key_joystick_mapping = 0;
679
680   if (key_status == KEY_RELEASED)
681     return;
682
683   if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
684       (key == KSYM_Return || key == setup.shortcut.toggle_pause))
685   {
686     CloseDoor(DOOR_CLOSE_1);
687     game_status = GAME_MODE_MAIN;
688     DrawMainMenu();
689
690     return;
691   }
692
693   if (game_status == GAME_MODE_MAIN && key == setup.shortcut.toggle_pause)
694   {
695     if (setup.autorecord)
696       TapeStartRecording();
697
698 #if defined(NETWORK_AVALIABLE)
699     if (options.network)
700       SendToServer_StartPlaying();
701     else
702 #endif
703     {
704       game_status = GAME_MODE_PLAYING;
705       StopAnimation();
706       InitGame();
707     }
708
709     return;
710   }
711
712   if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
713   {
714     if (key == setup.shortcut.save_game)
715       TapeQuickSave();
716     else if (key == setup.shortcut.load_game)
717       TapeQuickLoad();
718     else if (key == setup.shortcut.toggle_pause)
719       TapeTogglePause(TAPE_TOGGLE_MANUAL);
720   }
721
722   HandleKeysSpecial(key);
723
724   if (HandleGadgetsKeyInput(key))
725   {
726     if (key != KSYM_Escape)     /* always allow ESC key to be handled */
727       key = KSYM_UNDEFINED;
728   }
729
730   switch(game_status)
731   {
732     case GAME_MODE_PSEUDO_TYPENAME:
733       HandleTypeName(0, key);
734       break;
735
736     case GAME_MODE_MAIN:
737     case GAME_MODE_LEVELS:
738     case GAME_MODE_SETUP:
739     case GAME_MODE_INFO:
740       switch(key)
741       {
742         case KSYM_space:
743         case KSYM_Return:
744           if (game_status == GAME_MODE_MAIN)
745             HandleMainMenu(0,0, 0,0, MB_MENU_CHOICE);
746           else if (game_status == GAME_MODE_LEVELS)
747             HandleChooseLevel(0,0, 0,0, MB_MENU_CHOICE);
748           else if (game_status == GAME_MODE_SETUP)
749             HandleSetupScreen(0,0, 0,0, MB_MENU_CHOICE);
750           else if (game_status == GAME_MODE_INFO)
751             HandleInfoScreen(0,0, 0,0, MB_MENU_CHOICE);
752           break;
753
754         case KSYM_Escape:
755           if (game_status == GAME_MODE_LEVELS)
756             HandleChooseLevel(0,0, 0,0, MB_MENU_LEAVE);
757           else if (game_status == GAME_MODE_SETUP)
758             HandleSetupScreen(0,0, 0,0, MB_MENU_LEAVE);
759           else if (game_status == GAME_MODE_INFO)
760             HandleInfoScreen(0,0, 0,0, MB_MENU_LEAVE);
761           break;
762
763         case KSYM_Page_Up:
764           if (game_status == GAME_MODE_LEVELS)
765             HandleChooseLevel(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
766           else if (game_status == GAME_MODE_SETUP)
767             HandleSetupScreen(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
768           else if (game_status == GAME_MODE_INFO)
769             HandleInfoScreen(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
770           break;
771
772         case KSYM_Page_Down:
773           if (game_status == GAME_MODE_LEVELS)
774             HandleChooseLevel(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
775           else if (game_status == GAME_MODE_SETUP)
776             HandleSetupScreen(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
777           else if (game_status == GAME_MODE_INFO)
778             HandleInfoScreen(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
779           break;
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         case KSYM_1:
825         case KSYM_2:
826         case KSYM_3:
827         case KSYM_4:
828         case KSYM_5:
829         case KSYM_6:
830         case KSYM_7:
831         case KSYM_8:
832         case KSYM_9:
833           if (key == KSYM_0)
834           {
835             if (GameFrameDelay == 500)
836               GameFrameDelay = GAME_FRAME_DELAY;
837             else
838               GameFrameDelay = 500;
839           }
840           else
841             GameFrameDelay = (key - KSYM_0) * 10;
842           printf("Game speed == %d%% (%d ms delay between two frames)\n",
843                  GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
844           break;
845
846         case KSYM_d:
847           if (options.debug)
848           {
849             options.debug = FALSE;
850             printf("debug mode disabled\n");
851           }
852           else
853           {
854             options.debug = TRUE;
855             printf("debug mode enabled\n");
856           }
857           break;
858
859         case KSYM_S:
860           if (!global.fps_slowdown)
861           {
862             global.fps_slowdown = TRUE;
863             global.fps_slowdown_factor = 2;
864             printf("fps slowdown enabled -- display only every 2nd frame\n");
865           }
866           else if (global.fps_slowdown_factor == 2)
867           {
868             global.fps_slowdown_factor = 4;
869             printf("fps slowdown enabled -- display only every 4th frame\n");
870           }
871           else
872           {
873             global.fps_slowdown = FALSE;
874             global.fps_slowdown_factor = 1;
875             printf("fps slowdown disabled\n");
876           }
877           break;
878
879 #if 0
880         case KSYM_a:
881           if (ScrollStepSize == TILEX/8)
882             ScrollStepSize = TILEX/4;
883           else
884             ScrollStepSize = TILEX/8;
885           printf("ScrollStepSize == %d\n", ScrollStepSize);
886           break;
887 #endif
888
889 #if 0
890         case KSYM_m:
891           if (MoveSpeed == 8)
892           {
893             MoveSpeed = 4;
894             ScrollStepSize = TILEX/4;
895           }
896           else
897           {
898             MoveSpeed = 8;
899             ScrollStepSize = TILEX/8;
900           }
901           printf("MoveSpeed == %d\n", MoveSpeed);
902           break;
903 #endif
904
905         case KSYM_f:
906           ScrollStepSize = TILEX/8;
907           printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
908           break;
909
910         case KSYM_g:
911           ScrollStepSize = TILEX/4;
912           printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
913           break;
914
915         case KSYM_h:
916           ScrollStepSize = TILEX/2;
917           printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
918           break;
919
920         case KSYM_l:
921           ScrollStepSize = TILEX;
922           printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
923           break;
924
925         case KSYM_v:
926           printf("::: currently using game engine version %d\n",
927                  game.engine_version);
928           break;
929
930 #if 0
931
932         case KSYM_z:
933           {
934             int i;
935
936             for (i = 0; i < MAX_PLAYERS; i++)
937             {
938               printf("Player %d:\n", i);
939               printf("  jx == %d, jy == %d\n",
940                      stored_player[i].jx, stored_player[i].jy);
941               printf("  last_jx == %d, last_jy == %d\n",
942                      stored_player[i].last_jx, stored_player[i].last_jy);
943             }
944             printf("\n");
945           }
946
947           break;
948 #endif
949 #endif
950
951         default:
952           break;
953       }
954       break;
955     }
956
957     default:
958       if (key == KSYM_Escape)
959       {
960         game_status = GAME_MODE_MAIN;
961         DrawMainMenu();
962
963         return;
964       }
965   }
966 }
967
968 void HandleNoEvent()
969 {
970   if (button_status && game_status != GAME_MODE_PLAYING)
971   {
972     HandleButton(0, 0, -button_status);
973     return;
974   }
975
976 #if defined(NETWORK_AVALIABLE)
977   if (options.network)
978     HandleNetworking();
979 #endif
980
981   HandleJoystick();
982 }
983
984 static int HandleJoystickForAllPlayers()
985 {
986   int i;
987   int result = 0;
988
989   for (i = 0; i < MAX_PLAYERS; i++)
990   {
991     byte joy_action = 0;
992
993     /*
994     if (!setup.input[i].use_joystick)
995       continue;
996       */
997
998     joy_action = Joystick(i);
999     result |= joy_action;
1000
1001     if (!setup.input[i].use_joystick)
1002       continue;
1003
1004     stored_player[i].action = joy_action;
1005   }
1006
1007   return result;
1008 }
1009
1010 void HandleJoystick()
1011 {
1012   int joystick  = HandleJoystickForAllPlayers();
1013   int keyboard  = key_joystick_mapping;
1014   int joy       = (joystick | keyboard);
1015   int left      = joy & JOY_LEFT;
1016   int right     = joy & JOY_RIGHT;
1017   int up        = joy & JOY_UP;
1018   int down      = joy & JOY_DOWN;
1019   int button    = joy & JOY_BUTTON;
1020   int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1021   int dx        = (left ? -1    : right ? 1     : 0);
1022   int dy        = (up   ? -1    : down  ? 1     : 0);
1023
1024   switch(game_status)
1025   {
1026     case GAME_MODE_MAIN:
1027     case GAME_MODE_LEVELS:
1028     case GAME_MODE_SETUP:
1029     case GAME_MODE_INFO:
1030     {
1031       static unsigned long joystickmove_delay = 0;
1032
1033       if (joystick && !button &&
1034           !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
1035         newbutton = dx = dy = 0;
1036
1037       if (game_status == GAME_MODE_MAIN)
1038         HandleMainMenu(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1039       else if (game_status == GAME_MODE_LEVELS)
1040         HandleChooseLevel(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1041       else if (game_status == GAME_MODE_SETUP)
1042         HandleSetupScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1043       else if (game_status == GAME_MODE_INFO)
1044         HandleInfoScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1045       break;
1046     }
1047
1048     case GAME_MODE_SCORES:
1049       HandleHallOfFame(0,0, dx,dy, !newbutton);
1050       break;
1051
1052     case GAME_MODE_EDITOR:
1053       HandleLevelEditorIdle();
1054       break;
1055
1056     case GAME_MODE_PLAYING:
1057       if (tape.playing || keyboard)
1058         newbutton = ((joy & JOY_BUTTON) != 0);
1059
1060       if (AllPlayersGone && newbutton)
1061       {
1062         CloseDoor(DOOR_CLOSE_1);
1063         game_status = GAME_MODE_MAIN;
1064         DrawMainMenu();
1065         return;
1066       }
1067
1068       break;
1069
1070     default:
1071       break;
1072   }
1073 }