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