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