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