1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
27 #define DEBUG_EVENTS 0
30 static boolean cursor_inside_playfield = FALSE;
31 static boolean playfield_cursor_set = FALSE;
32 static unsigned int playfield_cursor_delay = 0;
35 /* event filter especially needed for SDL event filtering due to
36 delay problems with lots of mouse motion events when mouse button
37 not pressed (X11 can handle this with 'PointerMotionHintMask') */
39 /* event filter addition for SDL2: as SDL2 does not have a function to enable
40 or disable keyboard auto-repeat, filter repeated keyboard events instead */
42 static int FilterEventsExt(const Event *event)
46 #if defined(TARGET_SDL2)
47 /* skip repeated key press events if keyboard auto-repeat is disabled */
48 if (event->type == EVENT_KEYPRESS &&
54 /* non-motion events are directly passed to event handler functions */
55 if (event->type != EVENT_MOTIONNOTIFY)
58 motion = (MotionEvent *)event;
59 cursor_inside_playfield = (motion->x >= SX && motion->x < SX + SXSIZE &&
60 motion->y >= SY && motion->y < SY + SYSIZE);
62 if (game_status == GAME_MODE_PLAYING && playfield_cursor_set)
64 SetMouseCursor(CURSOR_DEFAULT);
65 playfield_cursor_set = FALSE;
66 DelayReached(&playfield_cursor_delay, 0);
69 /* skip mouse motion events without pressed button outside level editor */
70 if (button_status == MB_RELEASED &&
71 game_status != GAME_MODE_EDITOR && game_status != GAME_MODE_PLAYING)
77 #if defined(TARGET_SDL2)
78 int FilterEvents(void *userdata, Event *event)
80 return FilterEventsExt(event);
83 int FilterEvents(const Event *event)
85 return FilterEventsExt(event);
89 /* to prevent delay problems, skip mouse motion events if the very next
90 event is also a mouse motion event (and therefore effectively only
91 handling the last of a row of mouse motion events in the event queue) */
93 boolean SkipPressedMouseMotionEvent(const Event *event)
95 /* nothing to do if the current event is not a mouse motion event */
96 if (event->type != EVENT_MOTIONNOTIFY)
99 /* only skip motion events with pressed button outside level editor */
100 if (button_status == MB_RELEASED ||
101 game_status == GAME_MODE_EDITOR || game_status == GAME_MODE_PLAYING)
108 PeekEvent(&next_event);
110 /* if next event is also a mouse motion event, skip the current one */
111 if (next_event.type == EVENT_MOTIONNOTIFY)
118 /* this is only really needed for non-SDL targets to filter unwanted events;
119 when using SDL with properly installed event filter, this function can be
120 replaced with a simple "NextEvent()" call, but it doesn't hurt either */
122 static boolean NextValidEvent(Event *event)
124 while (PendingEvent())
126 boolean handle_this_event = FALSE;
130 if (FilterEventsExt(event))
131 handle_this_event = TRUE;
133 if (SkipPressedMouseMotionEvent(event))
134 handle_this_event = FALSE;
136 if (handle_this_event)
147 if (PendingEvent()) /* got event */
151 while (NextValidEvent(&event))
155 case EVENT_BUTTONPRESS:
156 case EVENT_BUTTONRELEASE:
157 HandleButtonEvent((ButtonEvent *) &event);
160 case EVENT_MOTIONNOTIFY:
161 HandleMotionEvent((MotionEvent *) &event);
164 #if defined(TARGET_SDL2)
165 case EVENT_FINGERPRESS:
166 case EVENT_FINGERRELEASE:
167 case EVENT_FINGERMOTION:
168 HandleFingerEvent((FingerEvent *) &event);
171 case EVENT_TEXTINPUT:
172 HandleTextEvent((TextEvent *) &event);
177 case EVENT_KEYRELEASE:
178 HandleKeyEvent((KeyEvent *) &event);
182 HandleOtherEvents(&event);
189 /* when playing, display a special mouse pointer inside the playfield */
190 if (game_status == GAME_MODE_PLAYING && !tape.pausing)
192 if (!playfield_cursor_set && cursor_inside_playfield &&
193 DelayReached(&playfield_cursor_delay, 1000))
195 SetMouseCursor(CURSOR_PLAYFIELD);
196 playfield_cursor_set = TRUE;
199 else if (playfield_cursor_set)
201 SetMouseCursor(CURSOR_DEFAULT);
202 playfield_cursor_set = FALSE;
208 /* don't use all CPU time when idle; the main loop while playing
209 has its own synchronization and is CPU friendly, too */
211 if (game_status == GAME_MODE_PLAYING)
218 if (!PendingEvent()) /* delay only if no pending events */
222 /* refresh window contents from drawing buffer, if needed */
225 if (game_status == GAME_MODE_QUIT)
230 void HandleOtherEvents(Event *event)
235 HandleExposeEvent((ExposeEvent *) event);
238 case EVENT_UNMAPNOTIFY:
240 /* This causes the game to stop not only when iconified, but also
241 when on another virtual desktop, which might be not desired. */
242 SleepWhileUnmapped();
248 HandleFocusEvent((FocusChangeEvent *) event);
251 case EVENT_CLIENTMESSAGE:
252 HandleClientMessageEvent((ClientMessageEvent *) event);
255 #if defined(TARGET_SDL)
256 case SDL_JOYAXISMOTION:
257 case SDL_JOYBUTTONDOWN:
258 case SDL_JOYBUTTONUP:
259 HandleJoystickEvent(event);
263 HandleWindowManagerEvent(event);
272 void ClearEventQueue()
274 while (PendingEvent())
282 case EVENT_BUTTONRELEASE:
283 button_status = MB_RELEASED;
286 case EVENT_KEYRELEASE:
290 key_joystick_mapping = 0;
295 HandleOtherEvents(&event);
301 void ClearPlayerAction()
305 /* simulate key release events for still pressed keys */
306 key_joystick_mapping = 0;
307 for (i = 0; i < MAX_PLAYERS; i++)
308 stored_player[i].action = 0;
311 void SleepWhileUnmapped()
313 boolean window_unmapped = TRUE;
315 KeyboardAutoRepeatOn();
317 while (window_unmapped)
325 case EVENT_BUTTONRELEASE:
326 button_status = MB_RELEASED;
329 case EVENT_KEYRELEASE:
330 key_joystick_mapping = 0;
333 case EVENT_MAPNOTIFY:
334 window_unmapped = FALSE;
337 case EVENT_UNMAPNOTIFY:
338 /* this is only to surely prevent the 'should not happen' case
339 * of recursively looping between 'SleepWhileUnmapped()' and
340 * 'HandleOtherEvents()' which usually calls this funtion.
345 HandleOtherEvents(&event);
350 if (game_status == GAME_MODE_PLAYING)
351 KeyboardAutoRepeatOffUnlessAutoplay();
354 void HandleExposeEvent(ExposeEvent *event)
356 #if !defined(TARGET_SDL)
357 RedrawPlayfield(FALSE, event->x, event->y, event->width, event->height);
362 void HandleButtonEvent(ButtonEvent *event)
365 Error(ERR_DEBUG, "BUTTON EVENT: button %d %s, x/y %d/%d\n",
367 event->type == EVENT_BUTTONPRESS ? "pressed" : "released",
371 motion_status = FALSE;
373 if (event->type == EVENT_BUTTONPRESS)
374 button_status = event->button;
376 button_status = MB_RELEASED;
378 HandleButton(event->x, event->y, button_status, event->button);
381 void HandleMotionEvent(MotionEvent *event)
383 if (!PointerInWindow(window))
384 return; /* window and pointer are on different screens */
386 if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR)
389 motion_status = TRUE;
391 Error(ERR_DEBUG, "MOTION EVENT: button %d moved, x/y %d/%d\n",
392 button_status, event->x, event->y);
394 HandleButton(event->x, event->y, button_status, button_status);
397 #if defined(TARGET_SDL2)
398 void HandleFingerEvent(FingerEvent *event)
401 static int num_events = 0;
406 Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
407 event->type == EVENT_FINGERPRESS ? "pressed" :
408 event->type == EVENT_FINGERRELEASE ? "released" : "moved",
412 event->dx, event->dy,
417 int x = (int)(event->x * video.width);
418 int y = (int)(event->y * video.height);
419 int button = MB_LEFTBUTTON;
421 Error(ERR_DEBUG, "=> screen x/y %d/%d", x, y);
425 if (++num_events >= max_events)
431 if (event->type == EVENT_FINGERPRESS ||
432 event->type == EVENT_FINGERMOTION)
433 button_status = button;
435 button_status = MB_RELEASED;
437 int max_x = SX + SXSIZE;
438 int max_y = SY + SYSIZE;
442 if (game_status == GAME_MODE_PLAYING)
444 if (game_status == GAME_MODE_PLAYING &&
448 int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
451 Key key = (event->y < 1.0 / 3.0 ? setup.input[0].key.up :
452 event->y > 2.0 / 3.0 ? setup.input[0].key.down :
453 event->x < 1.0 / 3.0 ? setup.input[0].key.left :
454 event->x > 2.0 / 3.0 ? setup.input[0].key.right :
455 setup.input[0].key.drop);
457 Key key = (y < max_y / 3 ? setup.input[0].key.up :
458 y > 2 * max_y / 3 ? setup.input[0].key.down :
459 x < max_x / 3 ? setup.input[0].key.left :
460 x > 2 * max_x / 3 ? setup.input[0].key.right :
461 setup.input[0].key.drop);
464 Error(ERR_DEBUG, "=> key == %d, key_status == %d", key, key_status);
466 HandleKey(key, key_status);
471 Error(ERR_DEBUG, "::: button_status == %d, button == %d\n",
472 button_status, button);
474 HandleButton(x, y, button_status, button);
480 static boolean checkTextInputKeyModState()
482 // when playing, only handle raw key events and ignore text input
483 if (game_status == GAME_MODE_PLAYING)
486 return ((GetKeyModState() & KMOD_TextInput) != KMOD_None);
489 void HandleTextEvent(TextEvent *event)
491 char *text = event->text;
492 Key key = getKeyFromKeyName(text);
495 Error(ERR_DEBUG, "TEXT EVENT: text == '%s' [%d byte(s), '%c'/%d], resulting key == %d (%s)",
498 text[0], (int)(text[0]),
500 getKeyNameFromKey(key));
503 // if (game_status != GAME_MODE_PLAYING && GetKeyModState() != KMOD_None)
505 if (game_status != GAME_MODE_PLAYING &&
506 (GetKeyModState() & KMOD_TextInput) != KMOD_None)
508 if (checkTextInputKeyModState())
510 HandleKey(key, KEY_PRESSED);
511 HandleKey(key, KEY_RELEASED);
516 void HandleKeyEvent(KeyEvent *event)
518 int key_status = (event->type == EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
519 boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
520 Key key = GetEventKey(event, with_modifiers);
521 Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
524 Error(ERR_DEBUG, "KEY EVENT: key was %s, keysym.scancode == %d, keysym.sym == %d, keymod = %d, GetKeyModState() = 0x%04x, resulting key == %d (%s)",
525 event->type == EVENT_KEYPRESS ? "pressed" : "released",
526 event->keysym.scancode,
531 getKeyNameFromKey(key));
535 if (key == KSYM_Menu)
536 Error(ERR_DEBUG, "menu key pressed");
537 else if (key == KSYM_Back)
538 Error(ERR_DEBUG, "back key pressed");
541 #if defined(PLATFORM_ANDROID)
542 // always map the "back" button to the "escape" key on Android devices
543 if (key == KSYM_Back)
547 HandleKeyModState(keymod, key_status);
549 #if defined(TARGET_SDL2)
551 // if (game_status == GAME_MODE_PLAYING || GetKeyModState() == KMOD_None)
553 if (game_status == GAME_MODE_PLAYING ||
554 (GetKeyModState() & KMOD_TextInput) == KMOD_None)
556 if (!checkTextInputKeyModState())
557 HandleKey(key, key_status);
559 HandleKey(key, key_status);
563 void HandleFocusEvent(FocusChangeEvent *event)
565 static int old_joystick_status = -1;
567 if (event->type == EVENT_FOCUSOUT)
569 KeyboardAutoRepeatOn();
570 old_joystick_status = joystick.status;
571 joystick.status = JOYSTICK_NOT_AVAILABLE;
575 else if (event->type == EVENT_FOCUSIN)
577 /* When there are two Rocks'n'Diamonds windows which overlap and
578 the player moves the pointer from one game window to the other,
579 a 'FocusOut' event is generated for the window the pointer is
580 leaving and a 'FocusIn' event is generated for the window the
581 pointer is entering. In some cases, it can happen that the
582 'FocusIn' event is handled by the one game process before the
583 'FocusOut' event by the other game process. In this case the
584 X11 environment would end up with activated keyboard auto repeat,
585 because unfortunately this is a global setting and not (which
586 would be far better) set for each X11 window individually.
587 The effect would be keyboard auto repeat while playing the game
588 (game_status == GAME_MODE_PLAYING), which is not desired.
589 To avoid this special case, we just wait 1/10 second before
590 processing the 'FocusIn' event.
593 if (game_status == GAME_MODE_PLAYING)
596 KeyboardAutoRepeatOffUnlessAutoplay();
599 if (old_joystick_status != -1)
600 joystick.status = old_joystick_status;
604 void HandleClientMessageEvent(ClientMessageEvent *event)
606 if (CheckCloseWindowEvent(event))
610 void HandleWindowManagerEvent(Event *event)
612 #if defined(TARGET_SDL)
613 SDLHandleWindowManagerEvent(event);
617 void HandleButton(int mx, int my, int button, int button_nr)
619 static int old_mx = 0, old_my = 0;
633 if (HandleGadgets(mx, my, button))
635 /* do not handle this button event anymore */
636 mx = my = -32; /* force mouse event to be outside screen tiles */
639 /* do not use scroll wheel button events for anything other than gadgets */
640 if (IS_WHEEL_BUTTON(button_nr))
643 Error(ERR_DEBUG, "::: game_status == %d", game_status);
647 case GAME_MODE_TITLE:
648 HandleTitleScreen(mx, my, 0, 0, button);
652 HandleMainMenu(mx, my, 0, 0, button);
655 case GAME_MODE_PSEUDO_TYPENAME:
656 HandleTypeName(0, KSYM_Return);
659 case GAME_MODE_LEVELS:
660 HandleChooseLevelSet(mx, my, 0, 0, button);
663 case GAME_MODE_LEVELNR:
664 HandleChooseLevelNr(mx, my, 0, 0, button);
667 case GAME_MODE_SCORES:
668 HandleHallOfFame(0, 0, 0, 0, button);
671 case GAME_MODE_EDITOR:
672 HandleLevelEditorIdle();
676 HandleInfoScreen(mx, my, 0, 0, button);
679 case GAME_MODE_SETUP:
680 HandleSetupScreen(mx, my, 0, 0, button);
683 case GAME_MODE_PLAYING:
685 if (button == MB_PRESSED && !motion_status && IN_GFX_SCREEN(mx, my))
686 DumpTile(LEVELX((mx - SX) / TILEX), LEVELY((my - SY) / TILEY));
695 static boolean is_string_suffix(char *string, char *suffix)
697 int string_len = strlen(string);
698 int suffix_len = strlen(suffix);
700 if (suffix_len > string_len)
703 return (strEqual(&string[string_len - suffix_len], suffix));
706 #define MAX_CHEAT_INPUT_LEN 32
708 static void HandleKeysSpecial(Key key)
710 static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
711 char letter = getCharFromKey(key);
712 int cheat_input_len = strlen(cheat_input);
718 if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
720 for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
721 cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
723 cheat_input_len = MAX_CHEAT_INPUT_LEN;
726 cheat_input[cheat_input_len++] = letter;
727 cheat_input[cheat_input_len] = '\0';
730 Error(ERR_DEBUG, "SPECIAL KEY '%s' [%d]\n", cheat_input, cheat_input_len);
733 if (game_status == GAME_MODE_MAIN)
735 if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
736 is_string_suffix(cheat_input, ":ist"))
738 InsertSolutionTape();
740 else if (is_string_suffix(cheat_input, ":reload-graphics") ||
741 is_string_suffix(cheat_input, ":rg"))
743 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
746 else if (is_string_suffix(cheat_input, ":reload-sounds") ||
747 is_string_suffix(cheat_input, ":rs"))
749 ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
752 else if (is_string_suffix(cheat_input, ":reload-music") ||
753 is_string_suffix(cheat_input, ":rm"))
755 ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
758 else if (is_string_suffix(cheat_input, ":reload-artwork") ||
759 is_string_suffix(cheat_input, ":ra"))
761 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
762 1 << ARTWORK_TYPE_SOUNDS |
763 1 << ARTWORK_TYPE_MUSIC);
766 else if (is_string_suffix(cheat_input, ":dump-level") ||
767 is_string_suffix(cheat_input, ":dl"))
771 else if (is_string_suffix(cheat_input, ":dump-tape") ||
772 is_string_suffix(cheat_input, ":dt"))
776 else if (is_string_suffix(cheat_input, ":save-native-level") ||
777 is_string_suffix(cheat_input, ":snl"))
779 SaveNativeLevel(&level);
782 else if (game_status == GAME_MODE_PLAYING)
785 if (is_string_suffix(cheat_input, ".q"))
786 DEBUG_SetMaximumDynamite();
789 else if (game_status == GAME_MODE_EDITOR)
791 if (is_string_suffix(cheat_input, ":dump-brush") ||
792 is_string_suffix(cheat_input, ":DB"))
796 else if (is_string_suffix(cheat_input, ":DDB"))
803 void HandleKey(Key key, int key_status)
805 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
806 static struct SetupKeyboardInfo ski;
807 static struct SetupShortcutInfo ssi;
816 { &ski.left, &ssi.snap_left, DEFAULT_KEY_LEFT, JOY_LEFT },
817 { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
818 { &ski.up, &ssi.snap_up, DEFAULT_KEY_UP, JOY_UP },
819 { &ski.down, &ssi.snap_down, DEFAULT_KEY_DOWN, JOY_DOWN },
820 { &ski.snap, NULL, DEFAULT_KEY_SNAP, JOY_BUTTON_SNAP },
821 { &ski.drop, NULL, DEFAULT_KEY_DROP, JOY_BUTTON_DROP }
826 if (game_status == GAME_MODE_PLAYING)
828 /* only needed for single-step tape recording mode */
829 static boolean clear_snap_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
830 static boolean clear_drop_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
831 static boolean element_snapped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
832 static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
835 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
839 if (setup.input[pnr].use_joystick)
842 ski = setup.input[pnr].key;
844 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
845 if (key == *key_info[i].key_custom)
846 key_action |= key_info[i].action;
848 /* use combined snap+direction keys for the first player only */
851 ssi = setup.shortcut;
853 for (i = 0; i < NUM_DIRECTIONS; i++)
854 if (key == *key_info[i].key_snap)
855 key_action |= key_info[i].action | JOY_BUTTON_SNAP;
858 /* clear delayed snap and drop actions in single step mode (see below) */
859 if (tape.single_step)
861 if (clear_snap_button[pnr])
863 stored_player[pnr].action &= ~KEY_BUTTON_SNAP;
864 clear_snap_button[pnr] = FALSE;
867 if (clear_drop_button[pnr])
869 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
870 clear_drop_button[pnr] = FALSE;
874 if (key_status == KEY_PRESSED)
875 stored_player[pnr].action |= key_action;
877 stored_player[pnr].action &= ~key_action;
879 if (tape.single_step && tape.recording && tape.pausing)
881 if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
883 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
885 /* if snap key already pressed, don't snap when releasing (below) */
886 if (stored_player[pnr].action & KEY_BUTTON_SNAP)
887 element_snapped[pnr] = TRUE;
889 /* if drop key already pressed, don't drop when releasing (below) */
890 if (stored_player[pnr].action & KEY_BUTTON_DROP)
891 element_dropped[pnr] = TRUE;
894 else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
896 if (level.game_engine_type == GAME_ENGINE_TYPE_EM ||
897 level.game_engine_type == GAME_ENGINE_TYPE_SP)
900 printf("::: drop key pressed\n");
903 if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
904 getRedDiskReleaseFlag_SP() == 0)
905 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
907 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
911 else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON)
913 if (key_action & KEY_BUTTON_SNAP)
915 /* if snap key was released without moving (see above), snap now */
916 if (!element_snapped[pnr])
918 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
920 stored_player[pnr].action |= KEY_BUTTON_SNAP;
922 /* clear delayed snap button on next event */
923 clear_snap_button[pnr] = TRUE;
926 element_snapped[pnr] = FALSE;
930 if (key_action & KEY_BUTTON_DROP &&
931 level.game_engine_type == GAME_ENGINE_TYPE_RND)
933 /* if drop key was released without moving (see above), drop now */
934 if (!element_dropped[pnr])
936 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
938 if (level.game_engine_type != GAME_ENGINE_TYPE_SP ||
939 getRedDiskReleaseFlag_SP() != 0)
940 stored_player[pnr].action |= KEY_BUTTON_DROP;
942 /* clear delayed drop button on next event */
943 clear_drop_button[pnr] = TRUE;
946 element_dropped[pnr] = FALSE;
951 else if (tape.recording && tape.pausing)
953 /* prevent key release events from un-pausing a paused game */
954 if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
955 TapeTogglePause(TAPE_TOGGLE_MANUAL);
961 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
962 if (key == key_info[i].key_default)
963 joy |= key_info[i].action;
968 if (key_status == KEY_PRESSED)
969 key_joystick_mapping |= joy;
971 key_joystick_mapping &= ~joy;
976 if (game_status != GAME_MODE_PLAYING)
977 key_joystick_mapping = 0;
979 if (key_status == KEY_RELEASED)
982 if ((key == KSYM_Return || key == KSYM_KP_Enter) &&
983 (GetKeyModState() & KMOD_Alt) && video.fullscreen_available)
985 setup.fullscreen = !setup.fullscreen;
987 ToggleFullscreenIfNeeded();
989 if (game_status == GAME_MODE_SETUP)
990 RedrawSetupScreenAfterFullscreenToggle();
996 if (game_status == GAME_MODE_PLAYING && local_player->LevelSolved_GameEnd &&
997 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
999 if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
1000 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1008 if (game_status == GAME_MODE_MAIN &&
1009 (key == setup.shortcut.toggle_pause || key == KSYM_space))
1011 StartGameActions(options.network, setup.autorecord, level.random_seed);
1016 if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
1018 if (key == setup.shortcut.save_game)
1020 else if (key == setup.shortcut.load_game)
1022 else if (key == setup.shortcut.toggle_pause)
1023 TapeTogglePause(TAPE_TOGGLE_MANUAL);
1025 HandleTapeButtonKeys(key);
1026 HandleSoundButtonKeys(key);
1029 if (game_status == GAME_MODE_PLAYING && !network_playing)
1031 int centered_player_nr_next = -999;
1033 if (key == setup.shortcut.focus_player_all)
1034 centered_player_nr_next = -1;
1036 for (i = 0; i < MAX_PLAYERS; i++)
1037 if (key == setup.shortcut.focus_player[i])
1038 centered_player_nr_next = i;
1040 if (centered_player_nr_next != -999)
1042 game.centered_player_nr_next = centered_player_nr_next;
1043 game.set_centered_player = TRUE;
1047 tape.centered_player_nr_next = game.centered_player_nr_next;
1048 tape.set_centered_player = TRUE;
1053 HandleKeysSpecial(key);
1055 if (HandleGadgetsKeyInput(key))
1057 if (key != KSYM_Escape) /* always allow ESC key to be handled */
1058 key = KSYM_UNDEFINED;
1061 switch (game_status)
1063 case GAME_MODE_PSEUDO_TYPENAME:
1064 HandleTypeName(0, key);
1067 case GAME_MODE_TITLE:
1068 case GAME_MODE_MAIN:
1069 case GAME_MODE_LEVELS:
1070 case GAME_MODE_LEVELNR:
1071 case GAME_MODE_SETUP:
1072 case GAME_MODE_INFO:
1073 case GAME_MODE_SCORES:
1078 if (game_status == GAME_MODE_TITLE)
1079 HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1080 else if (game_status == GAME_MODE_MAIN)
1081 HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
1082 else if (game_status == GAME_MODE_LEVELS)
1083 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
1084 else if (game_status == GAME_MODE_LEVELNR)
1085 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
1086 else if (game_status == GAME_MODE_SETUP)
1087 HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1088 else if (game_status == GAME_MODE_INFO)
1089 HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1090 else if (game_status == GAME_MODE_SCORES)
1091 HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
1095 if (game_status != GAME_MODE_MAIN)
1096 FadeSkipNextFadeIn();
1098 if (game_status == GAME_MODE_TITLE)
1099 HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1100 else if (game_status == GAME_MODE_LEVELS)
1101 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
1102 else if (game_status == GAME_MODE_LEVELNR)
1103 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
1104 else if (game_status == GAME_MODE_SETUP)
1105 HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1106 else if (game_status == GAME_MODE_INFO)
1107 HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1108 else if (game_status == GAME_MODE_SCORES)
1109 HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
1113 if (game_status == GAME_MODE_LEVELS)
1114 HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1115 else if (game_status == GAME_MODE_LEVELNR)
1116 HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1117 else if (game_status == GAME_MODE_SETUP)
1118 HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1119 else if (game_status == GAME_MODE_INFO)
1120 HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1121 else if (game_status == GAME_MODE_SCORES)
1122 HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1125 case KSYM_Page_Down:
1126 if (game_status == GAME_MODE_LEVELS)
1127 HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1128 else if (game_status == GAME_MODE_LEVELNR)
1129 HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1130 else if (game_status == GAME_MODE_SETUP)
1131 HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1132 else if (game_status == GAME_MODE_INFO)
1133 HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1134 else if (game_status == GAME_MODE_SCORES)
1135 HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1140 GameFrameDelay = (GameFrameDelay == 500 ? GAME_FRAME_DELAY : 500);
1144 setup.sp_show_border_elements = !setup.sp_show_border_elements;
1145 printf("Supaplex border elements %s\n",
1146 setup.sp_show_border_elements ? "enabled" : "disabled");
1155 case GAME_MODE_EDITOR:
1156 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
1157 HandleLevelEditorKeyInput(key);
1160 case GAME_MODE_PLAYING:
1165 RequestQuitGame(setup.ask_on_escape);
1183 if (GameFrameDelay == 500)
1184 GameFrameDelay = GAME_FRAME_DELAY;
1186 GameFrameDelay = 500;
1189 GameFrameDelay = (key - KSYM_0) * 10;
1190 printf("Game speed == %d%% (%d ms delay between two frames)\n",
1191 GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
1197 options.debug = FALSE;
1198 printf("debug mode disabled\n");
1202 options.debug = TRUE;
1203 printf("debug mode enabled\n");
1208 if (!global.fps_slowdown)
1210 global.fps_slowdown = TRUE;
1211 global.fps_slowdown_factor = 2;
1212 printf("fps slowdown enabled -- display only every 2nd frame\n");
1214 else if (global.fps_slowdown_factor == 2)
1216 global.fps_slowdown_factor = 4;
1217 printf("fps slowdown enabled -- display only every 4th frame\n");
1221 global.fps_slowdown = FALSE;
1222 global.fps_slowdown_factor = 1;
1223 printf("fps slowdown disabled\n");
1228 ScrollStepSize = TILEX / 8;
1229 printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
1233 ScrollStepSize = TILEX / 4;
1234 printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
1238 ScrollStepSize = TILEX / 2;
1239 printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
1243 ScrollStepSize = TILEX;
1244 printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
1248 printf("::: currently using game engine version %d\n",
1249 game.engine_version);
1260 if (key == KSYM_Escape)
1262 game_status = GAME_MODE_MAIN;
1270 void HandleNoEvent()
1272 if (button_status && game_status != GAME_MODE_PLAYING)
1274 HandleButton(0, 0, -button_status, button_status);
1279 #if defined(NETWORK_AVALIABLE)
1280 if (options.network)
1287 static int HandleJoystickForAllPlayers()
1292 for (i = 0; i < MAX_PLAYERS; i++)
1294 byte joy_action = 0;
1297 if (!setup.input[i].use_joystick)
1301 joy_action = Joystick(i);
1302 result |= joy_action;
1304 if (!setup.input[i].use_joystick)
1307 stored_player[i].action = joy_action;
1313 void HandleJoystick()
1315 int joystick = HandleJoystickForAllPlayers();
1316 int keyboard = key_joystick_mapping;
1317 int joy = (joystick | keyboard);
1318 int left = joy & JOY_LEFT;
1319 int right = joy & JOY_RIGHT;
1320 int up = joy & JOY_UP;
1321 int down = joy & JOY_DOWN;
1322 int button = joy & JOY_BUTTON;
1323 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1324 int dx = (left ? -1 : right ? 1 : 0);
1325 int dy = (up ? -1 : down ? 1 : 0);
1327 switch (game_status)
1329 case GAME_MODE_TITLE:
1330 case GAME_MODE_MAIN:
1331 case GAME_MODE_LEVELS:
1332 case GAME_MODE_LEVELNR:
1333 case GAME_MODE_SETUP:
1334 case GAME_MODE_INFO:
1336 static unsigned int joystickmove_delay = 0;
1338 if (joystick && !button &&
1339 !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
1340 newbutton = dx = dy = 0;
1342 if (game_status == GAME_MODE_TITLE)
1343 HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1344 else if (game_status == GAME_MODE_MAIN)
1345 HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1346 else if (game_status == GAME_MODE_LEVELS)
1347 HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
1348 else if (game_status == GAME_MODE_LEVELNR)
1349 HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
1350 else if (game_status == GAME_MODE_SETUP)
1351 HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1352 else if (game_status == GAME_MODE_INFO)
1353 HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1357 case GAME_MODE_SCORES:
1358 HandleHallOfFame(0, 0, dx, dy, !newbutton);
1361 case GAME_MODE_EDITOR:
1362 HandleLevelEditorIdle();
1365 case GAME_MODE_PLAYING:
1366 if (tape.playing || keyboard)
1367 newbutton = ((joy & JOY_BUTTON) != 0);
1370 if (local_player->LevelSolved_GameEnd && newbutton)
1372 if (AllPlayersGone && newbutton)