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