rnd-20060313-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       HandleGameActions();
175     else
176     {
177       SyncDisplay();
178       if (!PendingEvent())      /* delay only if no pending events */
179         Delay(10);
180     }
181
182     /* refresh window contents from drawing buffer, if needed */
183     BackToFront();
184
185     if (game_status == GAME_MODE_QUIT)
186       return;
187   }
188 }
189
190 void HandleOtherEvents(Event *event)
191 {
192   switch(event->type)
193   {
194     case EVENT_EXPOSE:
195       HandleExposeEvent((ExposeEvent *) event);
196       break;
197
198     case EVENT_UNMAPNOTIFY:
199 #if 0
200       /* This causes the game to stop not only when iconified, but also
201          when on another virtual desktop, which might be not desired. */
202       SleepWhileUnmapped();
203 #endif
204       break;
205
206     case EVENT_FOCUSIN:
207     case EVENT_FOCUSOUT:
208       HandleFocusEvent((FocusChangeEvent *) event);
209       break;
210
211     case EVENT_CLIENTMESSAGE:
212       HandleClientMessageEvent((ClientMessageEvent *) event);
213       break;
214
215 #if defined(TARGET_SDL)
216     case SDL_JOYAXISMOTION:
217     case SDL_JOYBUTTONDOWN:
218     case SDL_JOYBUTTONUP:
219       HandleJoystickEvent(event);
220       break;
221 #endif
222
223     default:
224       break;
225   }
226 }
227
228 void ClearEventQueue()
229 {
230   while (PendingEvent())
231   {
232     Event event;
233
234     NextEvent(&event);
235
236     switch(event.type)
237     {
238       case EVENT_BUTTONRELEASE:
239         button_status = MB_RELEASED;
240         break;
241
242       case EVENT_KEYRELEASE:
243         key_joystick_mapping = 0;
244         break;
245
246       default:
247         HandleOtherEvents(&event);
248         break;
249     }
250   }
251 }
252
253 void ClearPlayerAction()
254 {
255   int i;
256
257   /* simulate key release events for still pressed keys */
258   key_joystick_mapping = 0;
259   for (i = 0; i < MAX_PLAYERS; i++)
260     stored_player[i].action = 0;
261 }
262
263 void SleepWhileUnmapped()
264 {
265   boolean window_unmapped = TRUE;
266
267   KeyboardAutoRepeatOn();
268
269   while (window_unmapped)
270   {
271     Event event;
272
273     NextEvent(&event);
274
275     switch(event.type)
276     {
277       case EVENT_BUTTONRELEASE:
278         button_status = MB_RELEASED;
279         break;
280
281       case EVENT_KEYRELEASE:
282         key_joystick_mapping = 0;
283         break;
284
285       case EVENT_MAPNOTIFY:
286         window_unmapped = FALSE;
287         break;
288
289       case EVENT_UNMAPNOTIFY:
290         /* this is only to surely prevent the 'should not happen' case
291          * of recursively looping between 'SleepWhileUnmapped()' and
292          * 'HandleOtherEvents()' which usually calls this funtion.
293          */
294         break;
295
296       default:
297         HandleOtherEvents(&event);
298         break;
299     }
300   }
301
302   if (game_status == GAME_MODE_PLAYING)
303     KeyboardAutoRepeatOffUnlessAutoplay();
304 }
305
306 void HandleExposeEvent(ExposeEvent *event)
307 {
308 #ifndef TARGET_SDL
309   RedrawPlayfield(FALSE, event->x, event->y, event->width, event->height);
310   FlushDisplay();
311 #endif
312 }
313
314 void HandleButtonEvent(ButtonEvent *event)
315 {
316   motion_status = FALSE;
317
318   if (event->type == EVENT_BUTTONPRESS)
319     button_status = event->button;
320   else
321     button_status = MB_RELEASED;
322
323   HandleButton(event->x, event->y, button_status);
324 }
325
326 void HandleMotionEvent(MotionEvent *event)
327 {
328   if (!PointerInWindow(window))
329     return;     /* window and pointer are on different screens */
330
331   if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR)
332     return;
333
334   motion_status = TRUE;
335
336   HandleButton(event->x, event->y, button_status);
337 }
338
339 void HandleKeyEvent(KeyEvent *event)
340 {
341   int key_status = (event->type==EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
342   boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
343   Key key = GetEventKey(event, with_modifiers);
344   Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
345
346   HandleKeyModState(keymod, key_status);
347   HandleKey(key, key_status);
348 }
349
350 void HandleFocusEvent(FocusChangeEvent *event)
351 {
352   static int old_joystick_status = -1;
353
354   if (event->type == EVENT_FOCUSOUT)
355   {
356     KeyboardAutoRepeatOn();
357     old_joystick_status = joystick.status;
358     joystick.status = JOYSTICK_NOT_AVAILABLE;
359
360     ClearPlayerAction();
361   }
362   else if (event->type == EVENT_FOCUSIN)
363   {
364     /* When there are two Rocks'n'Diamonds windows which overlap and
365        the player moves the pointer from one game window to the other,
366        a 'FocusOut' event is generated for the window the pointer is
367        leaving and a 'FocusIn' event is generated for the window the
368        pointer is entering. In some cases, it can happen that the
369        'FocusIn' event is handled by the one game process before the
370        'FocusOut' event by the other game process. In this case the
371        X11 environment would end up with activated keyboard auto repeat,
372        because unfortunately this is a global setting and not (which
373        would be far better) set for each X11 window individually.
374        The effect would be keyboard auto repeat while playing the game
375        (game_status == GAME_MODE_PLAYING), which is not desired.
376        To avoid this special case, we just wait 1/10 second before
377        processing the 'FocusIn' event.
378     */
379
380     if (game_status == GAME_MODE_PLAYING)
381     {
382       Delay(100);
383       KeyboardAutoRepeatOffUnlessAutoplay();
384     }
385
386     if (old_joystick_status != -1)
387       joystick.status = old_joystick_status;
388   }
389 }
390
391 void HandleClientMessageEvent(ClientMessageEvent *event)
392 {
393   if (CheckCloseWindowEvent(event))
394     CloseAllAndExit(0);
395 }
396
397 void HandleButton(int mx, int my, int button)
398 {
399   static int old_mx = 0, old_my = 0;
400
401   if (button < 0)
402   {
403     mx = old_mx;
404     my = old_my;
405     button = -button;
406   }
407   else
408   {
409     old_mx = mx;
410     old_my = my;
411   }
412
413   if (HandleGadgets(mx, my, button))
414   {
415     /* do not handle this button event anymore */
416     mx = my = -32;      /* force mouse event to be outside screen tiles */
417   }
418
419   switch(game_status)
420   {
421     case GAME_MODE_MAIN:
422       HandleMainMenu(mx,my, 0,0, button);
423       break;
424
425     case GAME_MODE_PSEUDO_TYPENAME:
426       HandleTypeName(0, KSYM_Return);
427       break;
428
429     case GAME_MODE_LEVELS:
430       HandleChooseLevel(mx,my, 0,0, button);
431       break;
432
433     case GAME_MODE_SCORES:
434       HandleHallOfFame(0,0, 0,0, button);
435       break;
436
437     case GAME_MODE_EDITOR:
438       HandleLevelEditorIdle();
439       break;
440
441     case GAME_MODE_INFO:
442       HandleInfoScreen(mx,my, 0,0, button);
443       break;
444
445     case GAME_MODE_SETUP:
446       HandleSetupScreen(mx,my, 0,0, button);
447       break;
448
449     case GAME_MODE_PLAYING:
450 #ifdef DEBUG
451       if (button == MB_PRESSED && !motion_status && IN_GFX_SCREEN(mx, my))
452         DumpTile(LEVELX((mx - SX) / TILEX), LEVELY((my - SY) / TILEY));
453 #endif
454       break;
455
456     default:
457       break;
458   }
459 }
460
461 static boolean is_string_suffix(char *string, char *suffix)
462 {
463   int string_len = strlen(string);
464   int suffix_len = strlen(suffix);
465
466   if (suffix_len > string_len)
467     return FALSE;
468
469   return (strcmp(&string[string_len - suffix_len], suffix) == 0);
470 }
471
472 #define MAX_CHEAT_INPUT_LEN     32
473
474 static void HandleKeysSpecial(Key key)
475 {
476   static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
477   char letter = getCharFromKey(key);
478   int cheat_input_len = strlen(cheat_input);
479   int i;
480
481   if (letter == 0)
482     return;
483
484   if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
485   {
486     for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
487       cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
488
489     cheat_input_len = MAX_CHEAT_INPUT_LEN;
490   }
491
492   cheat_input[cheat_input_len++] = letter;
493   cheat_input[cheat_input_len] = '\0';
494
495 #if 0
496   printf("::: '%s' [%d]\n", cheat_input, cheat_input_len);
497 #endif
498
499   if (game_status == GAME_MODE_MAIN)
500   {
501     if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
502         is_string_suffix(cheat_input, ":ist"))
503     {
504       InsertSolutionTape();
505     }
506     else if (is_string_suffix(cheat_input, ":reload-graphics") ||
507              is_string_suffix(cheat_input, ":rg"))
508     {
509       ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
510       DrawMainMenu();
511     }
512     else if (is_string_suffix(cheat_input, ":reload-sounds") ||
513              is_string_suffix(cheat_input, ":rs"))
514     {
515       ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
516       DrawMainMenu();
517     }
518     else if (is_string_suffix(cheat_input, ":reload-music") ||
519              is_string_suffix(cheat_input, ":rm"))
520     {
521       ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
522       DrawMainMenu();
523     }
524     else if (is_string_suffix(cheat_input, ":reload-artwork") ||
525              is_string_suffix(cheat_input, ":ra"))
526     {
527       ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
528                           1 << ARTWORK_TYPE_SOUNDS |
529                           1 << ARTWORK_TYPE_MUSIC);
530       DrawMainMenu();
531     }
532     else if (is_string_suffix(cheat_input, ":dump-level") ||
533              is_string_suffix(cheat_input, ":dl"))
534     {
535       DumpLevel(&level);
536     }
537     else if (is_string_suffix(cheat_input, ":dump-tape") ||
538              is_string_suffix(cheat_input, ":dt"))
539     {
540       DumpTape(&tape);
541     }
542   }
543   else if (game_status == GAME_MODE_PLAYING)
544   {
545 #ifdef DEBUG
546     if (is_string_suffix(cheat_input, ".q"))
547       for (i = 0; i < MAX_INVENTORY_SIZE; i++)
548         if (local_player->inventory_size < MAX_INVENTORY_SIZE)
549           local_player->inventory_element[local_player->inventory_size++] =
550             EL_DYNAMITE;
551 #endif
552   }
553   else if (game_status == GAME_MODE_EDITOR)
554   {
555     if (is_string_suffix(cheat_input, ":dump-brush") ||
556         is_string_suffix(cheat_input, ":DB"))
557     {
558       DumpBrush();
559     }
560     else if (is_string_suffix(cheat_input, ":DDB"))
561     {
562       DumpBrush_Small();
563     }
564   }
565 }
566
567 void HandleKey(Key key, int key_status)
568 {
569   boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
570   static struct SetupKeyboardInfo custom_key;
571   static struct
572   {
573     Key *key_custom;
574     Key key_default;
575     byte action;
576   } key_info[] =
577   {
578     { &custom_key.left,  DEFAULT_KEY_LEFT,  JOY_LEFT     },
579     { &custom_key.right, DEFAULT_KEY_RIGHT, JOY_RIGHT    },
580     { &custom_key.up,    DEFAULT_KEY_UP,    JOY_UP       },
581     { &custom_key.down,  DEFAULT_KEY_DOWN,  JOY_DOWN     },
582     { &custom_key.snap,  DEFAULT_KEY_SNAP,  JOY_BUTTON_1 },
583     { &custom_key.drop,  DEFAULT_KEY_DROP,  JOY_BUTTON_2 }
584   };
585   int joy = 0;
586   int i;
587
588   if (game_status == GAME_MODE_PLAYING)
589   {
590     /* only needed for single-step tape recording mode */
591     static boolean clear_button_2[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
592     static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
593     int pnr;
594
595     for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
596     {
597       byte key_action = 0;
598
599       if (setup.input[pnr].use_joystick)
600         continue;
601
602       custom_key = setup.input[pnr].key;
603
604       for (i = 0; i < 6; i++)
605         if (key == *key_info[i].key_custom)
606           key_action |= key_info[i].action;
607
608       if (tape.single_step && clear_button_2[pnr])
609       {
610         stored_player[pnr].action &= ~KEY_BUTTON_2;
611         clear_button_2[pnr] = FALSE;
612       }
613
614       if (key_status == KEY_PRESSED)
615         stored_player[pnr].action |= key_action;
616       else
617         stored_player[pnr].action &= ~key_action;
618
619       if (tape.single_step && tape.recording && tape.pausing)
620       {
621         if (key_status == KEY_PRESSED &&
622             (key_action & (KEY_MOTION | KEY_BUTTON_1)))
623         {
624           TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
625
626           if (key_action & KEY_MOTION)
627           {
628             if (stored_player[pnr].action & KEY_BUTTON_2)
629               element_dropped[pnr] = TRUE;
630           }
631         }
632         else if (key_status == KEY_RELEASED &&
633                  (key_action & KEY_BUTTON_2))
634         {
635           if (!element_dropped[pnr])
636           {
637             TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
638
639             stored_player[pnr].action |= KEY_BUTTON_2;
640             clear_button_2[pnr] = TRUE;
641           }
642
643           element_dropped[pnr] = FALSE;
644         }
645       }
646       else if (tape.recording && tape.pausing && (key_action & KEY_ACTION))
647         TapeTogglePause(TAPE_TOGGLE_MANUAL);
648     }
649   }
650   else
651   {
652     for (i = 0; i < 6; i++)
653       if (key == key_info[i].key_default)
654         joy |= key_info[i].action;
655   }
656
657   if (joy)
658   {
659     if (key_status == KEY_PRESSED)
660       key_joystick_mapping |= joy;
661     else
662       key_joystick_mapping &= ~joy;
663
664     HandleJoystick();
665   }
666
667   if (game_status != GAME_MODE_PLAYING)
668     key_joystick_mapping = 0;
669
670   if (key_status == KEY_RELEASED)
671     return;
672
673   if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
674       (key == KSYM_Return || key == setup.shortcut.toggle_pause))
675   {
676     CloseDoor(DOOR_CLOSE_1);
677     game_status = GAME_MODE_MAIN;
678     DrawMainMenu();
679
680     return;
681   }
682
683   if (game_status == GAME_MODE_MAIN &&
684       (key == setup.shortcut.toggle_pause || key == KSYM_space))
685   {
686     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
687
688     return;
689   }
690
691   if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
692   {
693     if (key == setup.shortcut.save_game)
694       TapeQuickSave();
695     else if (key == setup.shortcut.load_game)
696       TapeQuickLoad();
697     else if (key == setup.shortcut.toggle_pause)
698       TapeTogglePause(TAPE_TOGGLE_MANUAL);
699   }
700
701   if (game_status == GAME_MODE_PLAYING)
702   {
703     int centered_player_nr_next = -999;
704
705     if (key == setup.shortcut.focus_player_all)
706       centered_player_nr_next = -1;
707     else
708       for (i = 0; i < MAX_PLAYERS; i++)
709         if (key == setup.shortcut.focus_player[i])
710           centered_player_nr_next = i;
711
712     if (centered_player_nr_next != -999)
713     {
714       game.centered_player_nr_next = centered_player_nr_next;
715       game.set_centered_player = TRUE;
716     }
717   }
718
719   HandleKeysSpecial(key);
720
721   if (HandleGadgetsKeyInput(key))
722   {
723     if (key != KSYM_Escape)     /* always allow ESC key to be handled */
724       key = KSYM_UNDEFINED;
725   }
726
727   switch(game_status)
728   {
729     case GAME_MODE_PSEUDO_TYPENAME:
730       HandleTypeName(0, key);
731       break;
732
733     case GAME_MODE_MAIN:
734     case GAME_MODE_LEVELS:
735     case GAME_MODE_SETUP:
736     case GAME_MODE_INFO:
737       switch(key)
738       {
739 #if 1
740         case KSYM_space:
741 #else
742         /* !!! only use "space" key to start game from main menu !!! */
743         case KSYM_space:
744 #endif
745         case KSYM_Return:
746           if (game_status == GAME_MODE_MAIN)
747             HandleMainMenu(0,0, 0,0, MB_MENU_CHOICE);
748           else if (game_status == GAME_MODE_LEVELS)
749             HandleChooseLevel(0,0, 0,0, MB_MENU_CHOICE);
750           else if (game_status == GAME_MODE_SETUP)
751             HandleSetupScreen(0,0, 0,0, MB_MENU_CHOICE);
752           else if (game_status == GAME_MODE_INFO)
753             HandleInfoScreen(0,0, 0,0, MB_MENU_CHOICE);
754           break;
755
756         case KSYM_Escape:
757           if (game_status == GAME_MODE_LEVELS)
758             HandleChooseLevel(0,0, 0,0, MB_MENU_LEAVE);
759           else if (game_status == GAME_MODE_SETUP)
760             HandleSetupScreen(0,0, 0,0, MB_MENU_LEAVE);
761           else if (game_status == GAME_MODE_INFO)
762             HandleInfoScreen(0,0, 0,0, MB_MENU_LEAVE);
763           break;
764
765         case KSYM_Page_Up:
766           if (game_status == GAME_MODE_LEVELS)
767             HandleChooseLevel(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
768           else if (game_status == GAME_MODE_SETUP)
769             HandleSetupScreen(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
770           else if (game_status == GAME_MODE_INFO)
771             HandleInfoScreen(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
772           break;
773
774         case KSYM_Page_Down:
775           if (game_status == GAME_MODE_LEVELS)
776             HandleChooseLevel(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
777           else if (game_status == GAME_MODE_SETUP)
778             HandleSetupScreen(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
779           else if (game_status == GAME_MODE_INFO)
780             HandleInfoScreen(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
781           break;
782
783 #ifdef DEBUG
784         case KSYM_0:
785           GameFrameDelay = (GameFrameDelay == 500 ? GAME_FRAME_DELAY : 500);
786           break;
787 #endif
788
789         default:
790           break;
791       }
792       break;
793
794     case GAME_MODE_SCORES:
795       switch(key)
796       {
797         case KSYM_space:
798         case KSYM_Return:
799         case KSYM_Escape:
800           game_status = GAME_MODE_MAIN;
801           DrawMainMenu();
802           break;
803
804         case KSYM_Page_Up:
805           HandleHallOfFame(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
806           break;
807
808         case KSYM_Page_Down:
809           HandleHallOfFame(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
810           break;
811
812         default:
813           break;
814       }
815       break;
816
817     case GAME_MODE_EDITOR:
818       if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
819         HandleLevelEditorKeyInput(key);
820       break;
821
822     case GAME_MODE_PLAYING:
823     {
824       switch(key)
825       {
826         case KSYM_Escape:
827           RequestQuitGame(setup.ask_on_escape);
828           break;
829
830 #ifdef DEBUG
831         case KSYM_0:
832 #if 0
833         case KSYM_1:
834         case KSYM_2:
835         case KSYM_3:
836         case KSYM_4:
837         case KSYM_5:
838         case KSYM_6:
839         case KSYM_7:
840         case KSYM_8:
841         case KSYM_9:
842 #endif
843           if (key == KSYM_0)
844           {
845             if (GameFrameDelay == 500)
846               GameFrameDelay = GAME_FRAME_DELAY;
847             else
848               GameFrameDelay = 500;
849           }
850           else
851             GameFrameDelay = (key - KSYM_0) * 10;
852           printf("Game speed == %d%% (%d ms delay between two frames)\n",
853                  GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
854           break;
855
856         case KSYM_d:
857           if (options.debug)
858           {
859             options.debug = FALSE;
860             printf("debug mode disabled\n");
861           }
862           else
863           {
864             options.debug = TRUE;
865             printf("debug mode enabled\n");
866           }
867           break;
868
869         case KSYM_S:
870           if (!global.fps_slowdown)
871           {
872             global.fps_slowdown = TRUE;
873             global.fps_slowdown_factor = 2;
874             printf("fps slowdown enabled -- display only every 2nd frame\n");
875           }
876           else if (global.fps_slowdown_factor == 2)
877           {
878             global.fps_slowdown_factor = 4;
879             printf("fps slowdown enabled -- display only every 4th frame\n");
880           }
881           else
882           {
883             global.fps_slowdown = FALSE;
884             global.fps_slowdown_factor = 1;
885             printf("fps slowdown disabled\n");
886           }
887           break;
888
889         case KSYM_f:
890           ScrollStepSize = TILEX/8;
891           printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
892           break;
893
894         case KSYM_g:
895           ScrollStepSize = TILEX/4;
896           printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
897           break;
898
899         case KSYM_h:
900           ScrollStepSize = TILEX/2;
901           printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
902           break;
903
904         case KSYM_l:
905           ScrollStepSize = TILEX;
906           printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
907           break;
908
909         case KSYM_v:
910           printf("::: currently using game engine version %d\n",
911                  game.engine_version);
912           break;
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(NETWORK_AVALIABLE)
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 }