rnd-20060430-1-src
[rocksndiamonds.git] / src / events.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * events.c                                                 *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "events.h"
17 #include "init.h"
18 #include "screens.h"
19 #include "tools.h"
20 #include "game.h"
21 #include "editor.h"
22 #include "files.h"
23 #include "tape.h"
24 #include "network.h"
25
26
27 static boolean cursor_inside_playfield = FALSE;
28 static boolean playfield_cursor_set = FALSE;
29 static unsigned long playfield_cursor_delay = 0;
30
31
32 /* event filter especially needed for SDL event filtering due to
33    delay problems with lots of mouse motion events when mouse button
34    not pressed (X11 can handle this with 'PointerMotionHintMask') */
35
36 int FilterMouseMotionEvents(const Event *event)
37 {
38   MotionEvent *motion;
39
40   /* non-motion events are directly passed to event handler functions */
41   if (event->type != EVENT_MOTIONNOTIFY)
42     return 1;
43
44   motion = (MotionEvent *)event;
45   cursor_inside_playfield = (motion->x >= SX && motion->x < SX + SXSIZE &&
46                              motion->y >= SY && motion->y < SY + SYSIZE);
47
48   if (game_status == GAME_MODE_PLAYING && playfield_cursor_set)
49   {
50     SetMouseCursor(CURSOR_DEFAULT);
51     playfield_cursor_set = FALSE;
52     DelayReached(&playfield_cursor_delay, 0);
53   }
54
55   /* skip mouse motion events without pressed button outside level editor */
56   if (button_status == MB_RELEASED &&
57       game_status != GAME_MODE_EDITOR && game_status != GAME_MODE_PLAYING)
58     return 0;
59   else
60     return 1;
61 }
62
63 /* to prevent delay problems, skip mouse motion events if the very next
64    event is also a mouse motion event (and therefore effectively only
65    handling the last of a row of mouse motion events in the event queue) */
66
67 boolean SkipPressedMouseMotionEvent(const Event *event)
68 {
69   /* nothing to do if the current event is not a mouse motion event */
70   if (event->type != EVENT_MOTIONNOTIFY)
71     return FALSE;
72
73   /* only skip motion events with pressed button outside level editor */
74   if (button_status == MB_RELEASED ||
75       game_status == GAME_MODE_EDITOR || game_status == GAME_MODE_PLAYING)
76     return FALSE;
77
78   if (PendingEvent())
79   {
80     Event next_event;
81
82     PeekEvent(&next_event);
83
84     /* if next event is also a mouse motion event, skip the current one */
85     if (next_event.type == EVENT_MOTIONNOTIFY)
86       return TRUE;
87   }
88
89   return FALSE;
90 }
91
92 /* this is only really needed for non-SDL targets to filter unwanted events;
93    when using SDL with properly installed event filter, this function can be
94    replaced with a simple "NextEvent()" call, but it doesn't hurt either */
95
96 static boolean NextValidEvent(Event *event)
97 {
98   while (PendingEvent())
99   {
100     boolean handle_this_event = FALSE;
101
102     NextEvent(event);
103
104     if (FilterMouseMotionEvents(event))
105       handle_this_event = TRUE;
106
107     if (SkipPressedMouseMotionEvent(event))
108       handle_this_event = FALSE;
109
110     if (handle_this_event)
111       return TRUE;
112   }
113
114   return FALSE;
115 }
116
117 void EventLoop(void)
118 {
119   while (1)
120   {
121     if (PendingEvent())         /* got event */
122     {
123       Event event;
124
125       while (NextValidEvent(&event))
126       {
127         switch(event.type)
128         {
129           case EVENT_BUTTONPRESS:
130           case EVENT_BUTTONRELEASE:
131             HandleButtonEvent((ButtonEvent *) &event);
132             break;
133   
134           case EVENT_MOTIONNOTIFY:
135             HandleMotionEvent((MotionEvent *) &event);
136             break;
137   
138           case EVENT_KEYPRESS:
139           case EVENT_KEYRELEASE:
140             HandleKeyEvent((KeyEvent *) &event);
141             break;
142   
143           default:
144             HandleOtherEvents(&event);
145             break;
146         }
147       }
148     }
149     else
150     {
151       /* when playing, display a special mouse pointer inside the playfield */
152       if (game_status == GAME_MODE_PLAYING && !tape.pausing)
153       {
154         if (!playfield_cursor_set && cursor_inside_playfield &&
155             DelayReached(&playfield_cursor_delay, 1000))
156         {
157           SetMouseCursor(CURSOR_PLAYFIELD);
158           playfield_cursor_set = TRUE;
159         }
160       }
161       else if (playfield_cursor_set)
162       {
163         SetMouseCursor(CURSOR_DEFAULT);
164         playfield_cursor_set = FALSE;
165       }
166
167       HandleNoEvent();
168     }
169
170     /* don't use all CPU time when idle; the main loop while playing
171        has its own synchronization and is CPU friendly, too */
172
173     if (game_status == GAME_MODE_PLAYING)
174     {
175       HandleGameActions();
176     }
177     else
178     {
179       SyncDisplay();
180       if (!PendingEvent())      /* delay only if no pending events */
181         Delay(10);
182     }
183
184     /* refresh window contents from drawing buffer, if needed */
185     BackToFront();
186
187     if (game_status == GAME_MODE_QUIT)
188       return;
189   }
190 }
191
192 void HandleOtherEvents(Event *event)
193 {
194   switch(event->type)
195   {
196     case EVENT_EXPOSE:
197       HandleExposeEvent((ExposeEvent *) event);
198       break;
199
200     case EVENT_UNMAPNOTIFY:
201 #if 0
202       /* This causes the game to stop not only when iconified, but also
203          when on another virtual desktop, which might be not desired. */
204       SleepWhileUnmapped();
205 #endif
206       break;
207
208     case EVENT_FOCUSIN:
209     case EVENT_FOCUSOUT:
210       HandleFocusEvent((FocusChangeEvent *) event);
211       break;
212
213     case EVENT_CLIENTMESSAGE:
214       HandleClientMessageEvent((ClientMessageEvent *) event);
215       break;
216
217 #if defined(TARGET_SDL)
218     case SDL_JOYAXISMOTION:
219     case SDL_JOYBUTTONDOWN:
220     case SDL_JOYBUTTONUP:
221       HandleJoystickEvent(event);
222       break;
223 #endif
224
225     default:
226       break;
227   }
228 }
229
230 void ClearEventQueue()
231 {
232   while (PendingEvent())
233   {
234     Event event;
235
236     NextEvent(&event);
237
238     switch(event.type)
239     {
240       case EVENT_BUTTONRELEASE:
241         button_status = MB_RELEASED;
242         break;
243
244       case EVENT_KEYRELEASE:
245         key_joystick_mapping = 0;
246         break;
247
248       default:
249         HandleOtherEvents(&event);
250         break;
251     }
252   }
253 }
254
255 void ClearPlayerAction()
256 {
257   int i;
258
259   /* simulate key release events for still pressed keys */
260   key_joystick_mapping = 0;
261   for (i = 0; i < MAX_PLAYERS; i++)
262     stored_player[i].action = 0;
263 }
264
265 void SleepWhileUnmapped()
266 {
267   boolean window_unmapped = TRUE;
268
269   KeyboardAutoRepeatOn();
270
271   while (window_unmapped)
272   {
273     Event event;
274
275     NextEvent(&event);
276
277     switch(event.type)
278     {
279       case EVENT_BUTTONRELEASE:
280         button_status = MB_RELEASED;
281         break;
282
283       case EVENT_KEYRELEASE:
284         key_joystick_mapping = 0;
285         break;
286
287       case EVENT_MAPNOTIFY:
288         window_unmapped = FALSE;
289         break;
290
291       case EVENT_UNMAPNOTIFY:
292         /* this is only to surely prevent the 'should not happen' case
293          * of recursively looping between 'SleepWhileUnmapped()' and
294          * 'HandleOtherEvents()' which usually calls this funtion.
295          */
296         break;
297
298       default:
299         HandleOtherEvents(&event);
300         break;
301     }
302   }
303
304   if (game_status == GAME_MODE_PLAYING)
305     KeyboardAutoRepeatOffUnlessAutoplay();
306 }
307
308 void HandleExposeEvent(ExposeEvent *event)
309 {
310 #ifndef TARGET_SDL
311   RedrawPlayfield(FALSE, event->x, event->y, event->width, event->height);
312   FlushDisplay();
313 #endif
314 }
315
316 void HandleButtonEvent(ButtonEvent *event)
317 {
318   motion_status = FALSE;
319
320   if (event->type == EVENT_BUTTONPRESS)
321     button_status = event->button;
322   else
323     button_status = MB_RELEASED;
324
325   HandleButton(event->x, event->y, button_status);
326 }
327
328 void HandleMotionEvent(MotionEvent *event)
329 {
330   if (!PointerInWindow(window))
331     return;     /* window and pointer are on different screens */
332
333   if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR)
334     return;
335
336   motion_status = TRUE;
337
338   HandleButton(event->x, event->y, button_status);
339 }
340
341 void HandleKeyEvent(KeyEvent *event)
342 {
343   int key_status = (event->type==EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
344   boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
345   Key key = GetEventKey(event, with_modifiers);
346   Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
347
348   HandleKeyModState(keymod, key_status);
349   HandleKey(key, key_status);
350 }
351
352 void HandleFocusEvent(FocusChangeEvent *event)
353 {
354   static int old_joystick_status = -1;
355
356   if (event->type == EVENT_FOCUSOUT)
357   {
358     KeyboardAutoRepeatOn();
359     old_joystick_status = joystick.status;
360     joystick.status = JOYSTICK_NOT_AVAILABLE;
361
362     ClearPlayerAction();
363   }
364   else if (event->type == EVENT_FOCUSIN)
365   {
366     /* When there are two Rocks'n'Diamonds windows which overlap and
367        the player moves the pointer from one game window to the other,
368        a 'FocusOut' event is generated for the window the pointer is
369        leaving and a 'FocusIn' event is generated for the window the
370        pointer is entering. In some cases, it can happen that the
371        'FocusIn' event is handled by the one game process before the
372        'FocusOut' event by the other game process. In this case the
373        X11 environment would end up with activated keyboard auto repeat,
374        because unfortunately this is a global setting and not (which
375        would be far better) set for each X11 window individually.
376        The effect would be keyboard auto repeat while playing the game
377        (game_status == GAME_MODE_PLAYING), which is not desired.
378        To avoid this special case, we just wait 1/10 second before
379        processing the 'FocusIn' event.
380     */
381
382     if (game_status == GAME_MODE_PLAYING)
383     {
384       Delay(100);
385       KeyboardAutoRepeatOffUnlessAutoplay();
386     }
387
388     if (old_joystick_status != -1)
389       joystick.status = old_joystick_status;
390   }
391 }
392
393 void HandleClientMessageEvent(ClientMessageEvent *event)
394 {
395   if (CheckCloseWindowEvent(event))
396     CloseAllAndExit(0);
397 }
398
399 void HandleButton(int mx, int my, int button)
400 {
401   static int old_mx = 0, old_my = 0;
402
403   if (button < 0)
404   {
405     mx = old_mx;
406     my = old_my;
407     button = -button;
408   }
409   else
410   {
411     old_mx = mx;
412     old_my = my;
413   }
414
415   if (HandleGadgets(mx, my, button))
416   {
417     /* do not handle this button event anymore */
418     mx = my = -32;      /* force mouse event to be outside screen tiles */
419   }
420
421   switch(game_status)
422   {
423     case GAME_MODE_TITLE:
424       HandleTitleScreen(mx,my, 0,0, button);
425       break;
426
427     case GAME_MODE_MAIN:
428       HandleMainMenu(mx,my, 0,0, button);
429       break;
430
431     case GAME_MODE_PSEUDO_TYPENAME:
432       HandleTypeName(0, KSYM_Return);
433       break;
434
435     case GAME_MODE_LEVELS:
436       HandleChooseLevel(mx,my, 0,0, button);
437       break;
438
439     case GAME_MODE_SCORES:
440       HandleHallOfFame(0,0, 0,0, button);
441       break;
442
443     case GAME_MODE_EDITOR:
444       HandleLevelEditorIdle();
445       break;
446
447     case GAME_MODE_INFO:
448       HandleInfoScreen(mx,my, 0,0, button);
449       break;
450
451     case GAME_MODE_SETUP:
452       HandleSetupScreen(mx,my, 0,0, button);
453       break;
454
455     case GAME_MODE_PLAYING:
456 #ifdef DEBUG
457       if (button == MB_PRESSED && !motion_status && IN_GFX_SCREEN(mx, my))
458         DumpTile(LEVELX((mx - SX) / TILEX), LEVELY((my - SY) / TILEY));
459 #endif
460       break;
461
462     default:
463       break;
464   }
465 }
466
467 static boolean is_string_suffix(char *string, char *suffix)
468 {
469   int string_len = strlen(string);
470   int suffix_len = strlen(suffix);
471
472   if (suffix_len > string_len)
473     return FALSE;
474
475   return (strEqual(&string[string_len - suffix_len], suffix));
476 }
477
478 #define MAX_CHEAT_INPUT_LEN     32
479
480 static void HandleKeysSpecial(Key key)
481 {
482   static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
483   char letter = getCharFromKey(key);
484   int cheat_input_len = strlen(cheat_input);
485   int i;
486
487   if (letter == 0)
488     return;
489
490   if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
491   {
492     for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
493       cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
494
495     cheat_input_len = MAX_CHEAT_INPUT_LEN;
496   }
497
498   cheat_input[cheat_input_len++] = letter;
499   cheat_input[cheat_input_len] = '\0';
500
501 #if 0
502   printf("::: '%s' [%d]\n", cheat_input, cheat_input_len);
503 #endif
504
505   if (game_status == GAME_MODE_MAIN)
506   {
507     if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
508         is_string_suffix(cheat_input, ":ist"))
509     {
510       InsertSolutionTape();
511     }
512     else if (is_string_suffix(cheat_input, ":reload-graphics") ||
513              is_string_suffix(cheat_input, ":rg"))
514     {
515       ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
516       DrawMainMenu();
517     }
518     else if (is_string_suffix(cheat_input, ":reload-sounds") ||
519              is_string_suffix(cheat_input, ":rs"))
520     {
521       ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
522       DrawMainMenu();
523     }
524     else if (is_string_suffix(cheat_input, ":reload-music") ||
525              is_string_suffix(cheat_input, ":rm"))
526     {
527       ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
528       DrawMainMenu();
529     }
530     else if (is_string_suffix(cheat_input, ":reload-artwork") ||
531              is_string_suffix(cheat_input, ":ra"))
532     {
533       ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
534                           1 << ARTWORK_TYPE_SOUNDS |
535                           1 << ARTWORK_TYPE_MUSIC);
536       DrawMainMenu();
537     }
538     else if (is_string_suffix(cheat_input, ":dump-level") ||
539              is_string_suffix(cheat_input, ":dl"))
540     {
541       DumpLevel(&level);
542     }
543     else if (is_string_suffix(cheat_input, ":dump-tape") ||
544              is_string_suffix(cheat_input, ":dt"))
545     {
546       DumpTape(&tape);
547     }
548   }
549   else if (game_status == GAME_MODE_PLAYING)
550   {
551 #ifdef DEBUG
552     if (is_string_suffix(cheat_input, ".q"))
553       DEBUG_SetMaximumDynamite();
554 #endif
555   }
556   else if (game_status == GAME_MODE_EDITOR)
557   {
558     if (is_string_suffix(cheat_input, ":dump-brush") ||
559         is_string_suffix(cheat_input, ":DB"))
560     {
561       DumpBrush();
562     }
563     else if (is_string_suffix(cheat_input, ":DDB"))
564     {
565       DumpBrush_Small();
566     }
567   }
568 }
569
570 void HandleKey(Key key, int key_status)
571 {
572   boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
573   static struct SetupKeyboardInfo custom_key;
574   static struct
575   {
576     Key *key_custom;
577     Key key_default;
578     byte action;
579   } key_info[] =
580   {
581     { &custom_key.left,  DEFAULT_KEY_LEFT,  JOY_LEFT     },
582     { &custom_key.right, DEFAULT_KEY_RIGHT, JOY_RIGHT    },
583     { &custom_key.up,    DEFAULT_KEY_UP,    JOY_UP       },
584     { &custom_key.down,  DEFAULT_KEY_DOWN,  JOY_DOWN     },
585     { &custom_key.snap,  DEFAULT_KEY_SNAP,  JOY_BUTTON_1 },
586     { &custom_key.drop,  DEFAULT_KEY_DROP,  JOY_BUTTON_2 }
587   };
588   int joy = 0;
589   int i;
590
591   if (game_status == GAME_MODE_PLAYING)
592   {
593     /* only needed for single-step tape recording mode */
594     static boolean clear_button_2[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
595     static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
596     int pnr;
597
598     for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
599     {
600       byte key_action = 0;
601
602       if (setup.input[pnr].use_joystick)
603         continue;
604
605       custom_key = setup.input[pnr].key;
606
607       for (i = 0; i < 6; i++)
608         if (key == *key_info[i].key_custom)
609           key_action |= key_info[i].action;
610
611       if (tape.single_step && clear_button_2[pnr])
612       {
613         stored_player[pnr].action &= ~KEY_BUTTON_2;
614         clear_button_2[pnr] = FALSE;
615       }
616
617       if (key_status == KEY_PRESSED)
618         stored_player[pnr].action |= key_action;
619       else
620         stored_player[pnr].action &= ~key_action;
621
622       if (tape.single_step && tape.recording && tape.pausing)
623       {
624         if (key_status == KEY_PRESSED &&
625             (key_action & (KEY_MOTION | KEY_BUTTON_1)))
626         {
627           TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
628
629           if (key_action & KEY_MOTION)
630           {
631             if (stored_player[pnr].action & KEY_BUTTON_2)
632               element_dropped[pnr] = TRUE;
633           }
634         }
635         else if (key_status == KEY_RELEASED &&
636                  (key_action & KEY_BUTTON_2))
637         {
638           if (!element_dropped[pnr])
639           {
640             TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
641
642             stored_player[pnr].action |= KEY_BUTTON_2;
643             clear_button_2[pnr] = TRUE;
644           }
645
646           element_dropped[pnr] = FALSE;
647         }
648       }
649       else if (tape.recording && tape.pausing && (key_action & KEY_ACTION))
650         TapeTogglePause(TAPE_TOGGLE_MANUAL);
651     }
652   }
653   else
654   {
655     for (i = 0; i < 6; i++)
656       if (key == key_info[i].key_default)
657         joy |= key_info[i].action;
658   }
659
660   if (joy)
661   {
662     if (key_status == KEY_PRESSED)
663       key_joystick_mapping |= joy;
664     else
665       key_joystick_mapping &= ~joy;
666
667     HandleJoystick();
668   }
669
670   if (game_status != GAME_MODE_PLAYING)
671     key_joystick_mapping = 0;
672
673   if (key_status == KEY_RELEASED)
674     return;
675
676   if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
677       (key == KSYM_Return || key == setup.shortcut.toggle_pause))
678   {
679     CloseDoor(DOOR_CLOSE_1);
680     game_status = GAME_MODE_MAIN;
681     DrawMainMenu();
682
683     return;
684   }
685
686   if (game_status == GAME_MODE_MAIN &&
687       (key == setup.shortcut.toggle_pause || key == KSYM_space))
688   {
689     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
690
691     return;
692   }
693
694   if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
695   {
696     if (key == setup.shortcut.save_game)
697       TapeQuickSave();
698     else if (key == setup.shortcut.load_game)
699       TapeQuickLoad();
700     else if (key == setup.shortcut.toggle_pause)
701       TapeTogglePause(TAPE_TOGGLE_MANUAL);
702   }
703
704   if (game_status == GAME_MODE_PLAYING && !network_playing)
705   {
706     int centered_player_nr_next = -999;
707
708     if (key == setup.shortcut.focus_player_all)
709       centered_player_nr_next = -1;
710     else
711       for (i = 0; i < MAX_PLAYERS; i++)
712         if (key == setup.shortcut.focus_player[i])
713           centered_player_nr_next = i;
714
715     if (centered_player_nr_next != -999)
716     {
717       game.centered_player_nr_next = centered_player_nr_next;
718       game.set_centered_player = TRUE;
719
720       if (tape.recording)
721       {
722         tape.centered_player_nr_next = game.centered_player_nr_next;
723         tape.set_centered_player = TRUE;
724       }
725     }
726   }
727
728   HandleKeysSpecial(key);
729
730   if (HandleGadgetsKeyInput(key))
731   {
732     if (key != KSYM_Escape)     /* always allow ESC key to be handled */
733       key = KSYM_UNDEFINED;
734   }
735
736   switch(game_status)
737   {
738     case GAME_MODE_PSEUDO_TYPENAME:
739       HandleTypeName(0, key);
740       break;
741
742     case GAME_MODE_TITLE:
743     case GAME_MODE_MAIN:
744     case GAME_MODE_LEVELS:
745     case GAME_MODE_SETUP:
746     case GAME_MODE_INFO:
747       switch(key)
748       {
749 #if 1
750         case KSYM_space:
751 #else
752         /* !!! only use "space" key to start game from main menu !!! */
753         case KSYM_space:
754 #endif
755         case KSYM_Return:
756           if (game_status == GAME_MODE_TITLE)
757             HandleTitleScreen(0,0, 0,0, MB_MENU_CHOICE);
758           else if (game_status == GAME_MODE_MAIN)
759             HandleMainMenu(0,0, 0,0, MB_MENU_CHOICE);
760           else if (game_status == GAME_MODE_LEVELS)
761             HandleChooseLevel(0,0, 0,0, MB_MENU_CHOICE);
762           else if (game_status == GAME_MODE_SETUP)
763             HandleSetupScreen(0,0, 0,0, MB_MENU_CHOICE);
764           else if (game_status == GAME_MODE_INFO)
765             HandleInfoScreen(0,0, 0,0, MB_MENU_CHOICE);
766           break;
767
768         case KSYM_Escape:
769           if (game_status == GAME_MODE_TITLE)
770             HandleTitleScreen(0,0, 0,0, MB_MENU_LEAVE);
771           else if (game_status == GAME_MODE_LEVELS)
772             HandleChooseLevel(0,0, 0,0, MB_MENU_LEAVE);
773           else if (game_status == GAME_MODE_SETUP)
774             HandleSetupScreen(0,0, 0,0, MB_MENU_LEAVE);
775           else if (game_status == GAME_MODE_INFO)
776             HandleInfoScreen(0,0, 0,0, MB_MENU_LEAVE);
777           break;
778
779         case KSYM_Page_Up:
780           if (game_status == GAME_MODE_LEVELS)
781             HandleChooseLevel(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
782           else if (game_status == GAME_MODE_SETUP)
783             HandleSetupScreen(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
784           else if (game_status == GAME_MODE_INFO)
785             HandleInfoScreen(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
786           break;
787
788         case KSYM_Page_Down:
789           if (game_status == GAME_MODE_LEVELS)
790             HandleChooseLevel(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
791           else if (game_status == GAME_MODE_SETUP)
792             HandleSetupScreen(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
793           else if (game_status == GAME_MODE_INFO)
794             HandleInfoScreen(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
795           break;
796
797 #ifdef DEBUG
798         case KSYM_0:
799           GameFrameDelay = (GameFrameDelay == 500 ? GAME_FRAME_DELAY : 500);
800           break;
801 #endif
802
803         default:
804           break;
805       }
806       break;
807
808     case GAME_MODE_SCORES:
809       switch(key)
810       {
811         case KSYM_space:
812         case KSYM_Return:
813         case KSYM_Escape:
814           game_status = GAME_MODE_MAIN;
815           DrawMainMenu();
816           break;
817
818         case KSYM_Page_Up:
819           HandleHallOfFame(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
820           break;
821
822         case KSYM_Page_Down:
823           HandleHallOfFame(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
824           break;
825
826         default:
827           break;
828       }
829       break;
830
831     case GAME_MODE_EDITOR:
832       if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
833         HandleLevelEditorKeyInput(key);
834       break;
835
836     case GAME_MODE_PLAYING:
837     {
838       switch(key)
839       {
840         case KSYM_Escape:
841           RequestQuitGame(setup.ask_on_escape);
842           break;
843
844 #ifdef DEBUG
845         case KSYM_0:
846 #if 0
847         case KSYM_1:
848         case KSYM_2:
849         case KSYM_3:
850         case KSYM_4:
851         case KSYM_5:
852         case KSYM_6:
853         case KSYM_7:
854         case KSYM_8:
855         case KSYM_9:
856 #endif
857           if (key == KSYM_0)
858           {
859             if (GameFrameDelay == 500)
860               GameFrameDelay = GAME_FRAME_DELAY;
861             else
862               GameFrameDelay = 500;
863           }
864           else
865             GameFrameDelay = (key - KSYM_0) * 10;
866           printf("Game speed == %d%% (%d ms delay between two frames)\n",
867                  GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
868           break;
869
870         case KSYM_d:
871           if (options.debug)
872           {
873             options.debug = FALSE;
874             printf("debug mode disabled\n");
875           }
876           else
877           {
878             options.debug = TRUE;
879             printf("debug mode enabled\n");
880           }
881           break;
882
883         case KSYM_S:
884           if (!global.fps_slowdown)
885           {
886             global.fps_slowdown = TRUE;
887             global.fps_slowdown_factor = 2;
888             printf("fps slowdown enabled -- display only every 2nd frame\n");
889           }
890           else if (global.fps_slowdown_factor == 2)
891           {
892             global.fps_slowdown_factor = 4;
893             printf("fps slowdown enabled -- display only every 4th frame\n");
894           }
895           else
896           {
897             global.fps_slowdown = FALSE;
898             global.fps_slowdown_factor = 1;
899             printf("fps slowdown disabled\n");
900           }
901           break;
902
903         case KSYM_f:
904           ScrollStepSize = TILEX/8;
905           printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
906           break;
907
908         case KSYM_g:
909           ScrollStepSize = TILEX/4;
910           printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
911           break;
912
913         case KSYM_h:
914           ScrollStepSize = TILEX/2;
915           printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
916           break;
917
918         case KSYM_l:
919           ScrollStepSize = TILEX;
920           printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
921           break;
922
923         case KSYM_v:
924           printf("::: currently using game engine version %d\n",
925                  game.engine_version);
926           break;
927 #endif
928
929         default:
930           break;
931       }
932       break;
933     }
934
935     default:
936       if (key == KSYM_Escape)
937       {
938         game_status = GAME_MODE_MAIN;
939         DrawMainMenu();
940
941         return;
942       }
943   }
944 }
945
946 void HandleNoEvent()
947 {
948   if (button_status && game_status != GAME_MODE_PLAYING)
949   {
950     HandleButton(0, 0, -button_status);
951     return;
952   }
953
954 #if defined(NETWORK_AVALIABLE)
955   if (options.network)
956     HandleNetworking();
957 #endif
958
959   HandleJoystick();
960 }
961
962 static int HandleJoystickForAllPlayers()
963 {
964   int i;
965   int result = 0;
966
967   for (i = 0; i < MAX_PLAYERS; i++)
968   {
969     byte joy_action = 0;
970
971     /*
972     if (!setup.input[i].use_joystick)
973       continue;
974       */
975
976     joy_action = Joystick(i);
977     result |= joy_action;
978
979     if (!setup.input[i].use_joystick)
980       continue;
981
982     stored_player[i].action = joy_action;
983   }
984
985   return result;
986 }
987
988 void HandleJoystick()
989 {
990   int joystick  = HandleJoystickForAllPlayers();
991   int keyboard  = key_joystick_mapping;
992   int joy       = (joystick | keyboard);
993   int left      = joy & JOY_LEFT;
994   int right     = joy & JOY_RIGHT;
995   int up        = joy & JOY_UP;
996   int down      = joy & JOY_DOWN;
997   int button    = joy & JOY_BUTTON;
998   int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
999   int dx        = (left ? -1    : right ? 1     : 0);
1000   int dy        = (up   ? -1    : down  ? 1     : 0);
1001
1002   switch(game_status)
1003   {
1004     case GAME_MODE_MAIN:
1005     case GAME_MODE_LEVELS:
1006     case GAME_MODE_SETUP:
1007     case GAME_MODE_INFO:
1008     {
1009       static unsigned long joystickmove_delay = 0;
1010
1011       if (joystick && !button &&
1012           !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
1013         newbutton = dx = dy = 0;
1014
1015       if (game_status == GAME_MODE_TITLE)
1016         HandleTitleScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1017       else if (game_status == GAME_MODE_MAIN)
1018         HandleMainMenu(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1019       else if (game_status == GAME_MODE_LEVELS)
1020         HandleChooseLevel(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1021       else if (game_status == GAME_MODE_SETUP)
1022         HandleSetupScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1023       else if (game_status == GAME_MODE_INFO)
1024         HandleInfoScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1025       break;
1026     }
1027
1028     case GAME_MODE_SCORES:
1029       HandleHallOfFame(0,0, dx,dy, !newbutton);
1030       break;
1031
1032     case GAME_MODE_EDITOR:
1033       HandleLevelEditorIdle();
1034       break;
1035
1036     case GAME_MODE_PLAYING:
1037       if (tape.playing || keyboard)
1038         newbutton = ((joy & JOY_BUTTON) != 0);
1039
1040       if (AllPlayersGone && newbutton)
1041       {
1042         CloseDoor(DOOR_CLOSE_1);
1043         game_status = GAME_MODE_MAIN;
1044         DrawMainMenu();
1045         return;
1046       }
1047
1048       break;
1049
1050     default:
1051       break;
1052   }
1053 }