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