rnd-20040424-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       while (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 && !tape.pausing)
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_PRESSED && !motion_status && IN_GFX_SCREEN(mx, my))
415         DumpTile(LEVELX((mx - SX) / TILEX), LEVELY((my - SY) / TILEY));
416 #endif
417       break;
418
419     default:
420       break;
421   }
422 }
423
424 static boolean is_string_suffix(char *string, char *suffix)
425 {
426   int string_len = strlen(string);
427   int suffix_len = strlen(suffix);
428
429   if (suffix_len > string_len)
430     return FALSE;
431
432   return (strcmp(&string[string_len - suffix_len], suffix) == 0);
433 }
434
435 #define MAX_CHEAT_INPUT_LEN     32
436
437 static void HandleKeysSpecial(Key key)
438 {
439   static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
440   char letter = getCharFromKey(key);
441   int cheat_input_len = strlen(cheat_input);
442   int i;
443
444   if (letter == 0)
445     return;
446
447   if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
448   {
449     for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
450       cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
451
452     cheat_input_len = MAX_CHEAT_INPUT_LEN;
453   }
454
455   cheat_input[cheat_input_len++] = letter;
456   cheat_input[cheat_input_len] = '\0';
457
458 #if 0
459   printf("::: '%s' [%d]\n", cheat_input, cheat_input_len);
460 #endif
461
462   if (game_status == GAME_MODE_MAIN)
463   {
464     if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
465         is_string_suffix(cheat_input, ":ist"))
466     {
467       InsertSolutionTape();
468     }
469     else if (is_string_suffix(cheat_input, ":reload-graphics") ||
470              is_string_suffix(cheat_input, ":rg"))
471     {
472       ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
473       DrawMainMenu();
474     }
475     else if (is_string_suffix(cheat_input, ":reload-sounds") ||
476              is_string_suffix(cheat_input, ":rs"))
477     {
478       ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
479       DrawMainMenu();
480     }
481     else if (is_string_suffix(cheat_input, ":reload-music") ||
482              is_string_suffix(cheat_input, ":rm"))
483     {
484       ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
485       DrawMainMenu();
486     }
487     else if (is_string_suffix(cheat_input, ":reload-artwork") ||
488              is_string_suffix(cheat_input, ":ra"))
489     {
490       ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
491                           1 << ARTWORK_TYPE_SOUNDS |
492                           1 << ARTWORK_TYPE_MUSIC);
493       DrawMainMenu();
494     }
495     else if (is_string_suffix(cheat_input, ":dump-level") ||
496              is_string_suffix(cheat_input, ":dl"))
497     {
498       DumpLevel(&level);
499     }
500     else if (is_string_suffix(cheat_input, ":dump-tape") ||
501              is_string_suffix(cheat_input, ":dt"))
502     {
503       DumpTape(&tape);
504     }
505   }
506   else if (game_status == GAME_MODE_PLAYING)
507   {
508 #ifdef DEBUG
509     if (is_string_suffix(cheat_input, ".q"))
510       for (i = 0; i < MAX_INVENTORY_SIZE; i++)
511         if (local_player->inventory_size < MAX_INVENTORY_SIZE)
512           local_player->inventory_element[local_player->inventory_size++] =
513             EL_DYNAMITE;
514 #endif
515   }
516   else if (game_status == GAME_MODE_EDITOR)
517   {
518     if (is_string_suffix(cheat_input, ":dump-brush") ||
519         is_string_suffix(cheat_input, ":DB"))
520     {
521       DumpBrush();
522     }
523   }
524 }
525
526 void HandleKey(Key key, int key_status)
527 {
528   int joy = 0;
529   boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
530   static struct SetupKeyboardInfo custom_key;
531   static struct
532   {
533     Key *key_custom;
534     Key key_default;
535     byte action;
536   } key_info[] =
537   {
538     { &custom_key.left,  DEFAULT_KEY_LEFT,  JOY_LEFT     },
539     { &custom_key.right, DEFAULT_KEY_RIGHT, JOY_RIGHT    },
540     { &custom_key.up,    DEFAULT_KEY_UP,    JOY_UP       },
541     { &custom_key.down,  DEFAULT_KEY_DOWN,  JOY_DOWN     },
542     { &custom_key.snap,  DEFAULT_KEY_SNAP,  JOY_BUTTON_1 },
543     { &custom_key.drop,  DEFAULT_KEY_DROP,  JOY_BUTTON_2 }
544   };
545
546   if (game_status == GAME_MODE_PLAYING)
547   {
548     /* only needed for single-step tape recording mode */
549     static boolean clear_button_2[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
550     static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
551     int pnr;
552
553     for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
554     {
555       int i;
556       byte key_action = 0;
557
558       if (setup.input[pnr].use_joystick)
559         continue;
560
561       custom_key = setup.input[pnr].key;
562
563       for (i = 0; i < 6; i++)
564         if (key == *key_info[i].key_custom)
565           key_action |= key_info[i].action;
566
567       if (tape.single_step && clear_button_2[pnr])
568       {
569         stored_player[pnr].action &= ~KEY_BUTTON_2;
570         clear_button_2[pnr] = FALSE;
571       }
572
573       if (key_status == KEY_PRESSED)
574         stored_player[pnr].action |= key_action;
575       else
576         stored_player[pnr].action &= ~key_action;
577
578       if (tape.single_step && tape.recording && tape.pausing)
579       {
580         if (key_status == KEY_PRESSED &&
581             (key_action & (KEY_MOTION | KEY_BUTTON_1)))
582         {
583           TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
584
585           if (key_action & KEY_MOTION)
586           {
587             if (stored_player[pnr].action & KEY_BUTTON_2)
588               element_dropped[pnr] = TRUE;
589           }
590         }
591         else if (key_status == KEY_RELEASED &&
592                  (key_action & KEY_BUTTON_2))
593         {
594           if (!element_dropped[pnr])
595           {
596             TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
597
598             stored_player[pnr].action |= KEY_BUTTON_2;
599             clear_button_2[pnr] = TRUE;
600           }
601
602           element_dropped[pnr] = FALSE;
603         }
604       }
605       else if (tape.recording && tape.pausing && (key_action & KEY_ACTION))
606         TapeTogglePause(TAPE_TOGGLE_MANUAL);
607     }
608   }
609   else
610   {
611     int i;
612
613     for (i = 0; i < 6; i++)
614       if (key == key_info[i].key_default)
615         joy |= key_info[i].action;
616   }
617
618   if (joy)
619   {
620     if (key_status == KEY_PRESSED)
621       key_joystick_mapping |= joy;
622     else
623       key_joystick_mapping &= ~joy;
624
625     HandleJoystick();
626   }
627
628   if (game_status != GAME_MODE_PLAYING)
629     key_joystick_mapping = 0;
630
631   if (key_status == KEY_RELEASED)
632     return;
633
634   if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
635       (key == KSYM_Return || key == setup.shortcut.toggle_pause))
636   {
637     CloseDoor(DOOR_CLOSE_1);
638     game_status = GAME_MODE_MAIN;
639     DrawMainMenu();
640
641     return;
642   }
643
644   if (game_status == GAME_MODE_MAIN && key == setup.shortcut.toggle_pause)
645   {
646     if (setup.autorecord)
647       TapeStartRecording();
648
649 #if defined(PLATFORM_UNIX)
650     if (options.network)
651       SendToServer_StartPlaying();
652     else
653 #endif
654     {
655       game_status = GAME_MODE_PLAYING;
656       StopAnimation();
657       InitGame();
658     }
659
660     return;
661   }
662
663   if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
664   {
665     if (key == setup.shortcut.save_game)
666       TapeQuickSave();
667     else if (key == setup.shortcut.load_game)
668       TapeQuickLoad();
669     else if (key == setup.shortcut.toggle_pause)
670       TapeTogglePause(TAPE_TOGGLE_MANUAL);
671   }
672
673   HandleKeysSpecial(key);
674
675   if (HandleGadgetsKeyInput(key))
676   {
677     if (key != KSYM_Escape)     /* always allow ESC key to be handled */
678       key = KSYM_UNDEFINED;
679   }
680
681   switch(game_status)
682   {
683     case GAME_MODE_PSEUDO_TYPENAME:
684       HandleTypeName(0, key);
685       break;
686
687     case GAME_MODE_MAIN:
688     case GAME_MODE_LEVELS:
689     case GAME_MODE_SETUP:
690     case GAME_MODE_INFO:
691       switch(key)
692       {
693         case KSYM_space:
694         case KSYM_Return:
695           if (game_status == GAME_MODE_MAIN)
696             HandleMainMenu(0,0, 0,0, MB_MENU_CHOICE);
697           else if (game_status == GAME_MODE_LEVELS)
698             HandleChooseLevel(0,0, 0,0, MB_MENU_CHOICE);
699           else if (game_status == GAME_MODE_SETUP)
700             HandleSetupScreen(0,0, 0,0, MB_MENU_CHOICE);
701           else if (game_status == GAME_MODE_INFO)
702             HandleInfoScreen(0,0, 0,0, MB_MENU_CHOICE);
703           break;
704
705         case KSYM_Escape:
706           if (game_status == GAME_MODE_LEVELS)
707             HandleChooseLevel(0,0, 0,0, MB_MENU_LEAVE);
708           else if (game_status == GAME_MODE_SETUP)
709             HandleSetupScreen(0,0, 0,0, MB_MENU_LEAVE);
710           else if (game_status == GAME_MODE_INFO)
711             HandleInfoScreen(0,0, 0,0, MB_MENU_LEAVE);
712           break;
713
714         case KSYM_Page_Up:
715           if (game_status == GAME_MODE_LEVELS)
716             HandleChooseLevel(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
717           else if (game_status == GAME_MODE_SETUP)
718             HandleSetupScreen(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
719           else if (game_status == GAME_MODE_INFO)
720             HandleInfoScreen(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
721           break;
722
723         case KSYM_Page_Down:
724           if (game_status == GAME_MODE_LEVELS)
725             HandleChooseLevel(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
726           else if (game_status == GAME_MODE_SETUP)
727             HandleSetupScreen(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
728           else if (game_status == GAME_MODE_INFO)
729             HandleInfoScreen(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
730           break;
731
732         default:
733           break;
734       }
735       break;
736
737     case GAME_MODE_SCORES:
738       switch(key)
739       {
740         case KSYM_space:
741         case KSYM_Return:
742         case KSYM_Escape:
743           game_status = GAME_MODE_MAIN;
744           DrawMainMenu();
745           break;
746
747         case KSYM_Page_Up:
748           HandleHallOfFame(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
749           break;
750
751         case KSYM_Page_Down:
752           HandleHallOfFame(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
753           break;
754
755         default:
756           break;
757       }
758       break;
759
760     case GAME_MODE_EDITOR:
761       if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
762         HandleLevelEditorKeyInput(key);
763       break;
764
765     case GAME_MODE_PLAYING:
766     {
767       switch(key)
768       {
769         case KSYM_Escape:
770           RequestQuitGame(setup.ask_on_escape);
771           break;
772
773 #ifdef DEBUG
774         case KSYM_0:
775         case KSYM_1:
776         case KSYM_2:
777         case KSYM_3:
778         case KSYM_4:
779         case KSYM_5:
780         case KSYM_6:
781         case KSYM_7:
782         case KSYM_8:
783         case KSYM_9:
784           if (key == KSYM_0)
785           {
786             if (GameFrameDelay == 500)
787               GameFrameDelay = GAME_FRAME_DELAY;
788             else
789               GameFrameDelay = 500;
790           }
791           else
792             GameFrameDelay = (key - KSYM_0) * 10;
793           printf("Game speed == %d%% (%d ms delay between two frames)\n",
794                  GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
795           break;
796
797         case KSYM_d:
798           if (options.debug)
799           {
800             options.debug = FALSE;
801             printf("debug mode disabled\n");
802           }
803           else
804           {
805             options.debug = TRUE;
806             printf("debug mode enabled\n");
807           }
808           break;
809
810         case KSYM_S:
811           if (!global.fps_slowdown)
812           {
813             global.fps_slowdown = TRUE;
814             global.fps_slowdown_factor = 2;
815             printf("fps slowdown enabled -- display only every 2nd frame\n");
816           }
817           else if (global.fps_slowdown_factor == 2)
818           {
819             global.fps_slowdown_factor = 4;
820             printf("fps slowdown enabled -- display only every 4th frame\n");
821           }
822           else
823           {
824             global.fps_slowdown = FALSE;
825             global.fps_slowdown_factor = 1;
826             printf("fps slowdown disabled\n");
827           }
828           break;
829
830 #if 0
831         case KSYM_a:
832           if (ScrollStepSize == TILEX/8)
833             ScrollStepSize = TILEX/4;
834           else
835             ScrollStepSize = TILEX/8;
836           printf("ScrollStepSize == %d\n", ScrollStepSize);
837           break;
838 #endif
839
840 #if 0
841         case KSYM_m:
842           if (MoveSpeed == 8)
843           {
844             MoveSpeed = 4;
845             ScrollStepSize = TILEX/4;
846           }
847           else
848           {
849             MoveSpeed = 8;
850             ScrollStepSize = TILEX/8;
851           }
852           printf("MoveSpeed == %d\n", MoveSpeed);
853           break;
854 #endif
855
856         case KSYM_f:
857           ScrollStepSize = TILEX/8;
858           printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
859           break;
860
861         case KSYM_g:
862           ScrollStepSize = TILEX/4;
863           printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
864           break;
865
866         case KSYM_h:
867           ScrollStepSize = TILEX/2;
868           printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
869           break;
870
871         case KSYM_l:
872           ScrollStepSize = TILEX;
873           printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
874           break;
875
876 #if 0
877
878         case KSYM_z:
879           {
880             int i;
881
882             for (i = 0; i < MAX_PLAYERS; i++)
883             {
884               printf("Player %d:\n", i);
885               printf("  jx == %d, jy == %d\n",
886                      stored_player[i].jx, stored_player[i].jy);
887               printf("  last_jx == %d, last_jy == %d\n",
888                      stored_player[i].last_jx, stored_player[i].last_jy);
889             }
890             printf("\n");
891           }
892
893           break;
894 #endif
895 #endif
896
897         default:
898           break;
899       }
900       break;
901     }
902
903     default:
904       if (key == KSYM_Escape)
905       {
906         game_status = GAME_MODE_MAIN;
907         DrawMainMenu();
908
909         return;
910       }
911   }
912 }
913
914 void HandleNoEvent()
915 {
916   if (button_status && game_status != GAME_MODE_PLAYING)
917   {
918     HandleButton(0, 0, -button_status);
919     return;
920   }
921
922 #if defined(PLATFORM_UNIX)
923   if (options.network)
924     HandleNetworking();
925 #endif
926
927   HandleJoystick();
928 }
929
930 static int HandleJoystickForAllPlayers()
931 {
932   int i;
933   int result = 0;
934
935   for (i = 0; i < MAX_PLAYERS; i++)
936   {
937     byte joy_action = 0;
938
939     /*
940     if (!setup.input[i].use_joystick)
941       continue;
942       */
943
944     joy_action = Joystick(i);
945     result |= joy_action;
946
947     if (!setup.input[i].use_joystick)
948       continue;
949
950     stored_player[i].action = joy_action;
951   }
952
953   return result;
954 }
955
956 void HandleJoystick()
957 {
958   int joystick  = HandleJoystickForAllPlayers();
959   int keyboard  = key_joystick_mapping;
960   int joy       = (joystick | keyboard);
961   int left      = joy & JOY_LEFT;
962   int right     = joy & JOY_RIGHT;
963   int up        = joy & JOY_UP;
964   int down      = joy & JOY_DOWN;
965   int button    = joy & JOY_BUTTON;
966   int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
967   int dx        = (left ? -1    : right ? 1     : 0);
968   int dy        = (up   ? -1    : down  ? 1     : 0);
969
970   switch(game_status)
971   {
972     case GAME_MODE_MAIN:
973     case GAME_MODE_LEVELS:
974     case GAME_MODE_SETUP:
975     case GAME_MODE_INFO:
976     {
977       static unsigned long joystickmove_delay = 0;
978
979       if (joystick && !button &&
980           !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
981         newbutton = dx = dy = 0;
982
983       if (game_status == GAME_MODE_MAIN)
984         HandleMainMenu(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
985       else if (game_status == GAME_MODE_LEVELS)
986         HandleChooseLevel(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
987       else if (game_status == GAME_MODE_SETUP)
988         HandleSetupScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
989       else if (game_status == GAME_MODE_INFO)
990         HandleInfoScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
991       break;
992     }
993
994     case GAME_MODE_SCORES:
995       HandleHallOfFame(0,0, dx,dy, !newbutton);
996       break;
997
998     case GAME_MODE_EDITOR:
999       HandleLevelEditorIdle();
1000       break;
1001
1002     case GAME_MODE_PLAYING:
1003       if (tape.playing || keyboard)
1004         newbutton = ((joy & JOY_BUTTON) != 0);
1005
1006       if (AllPlayersGone && newbutton)
1007       {
1008         CloseDoor(DOOR_CLOSE_1);
1009         game_status = GAME_MODE_MAIN;
1010         DrawMainMenu();
1011         return;
1012       }
1013
1014       break;
1015
1016     default:
1017       break;
1018   }
1019 }