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