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 SDL_WINDOWEVENT:
166 HandleWindowEvent((WindowEvent *) &event);
169 case EVENT_FINGERPRESS:
170 case EVENT_FINGERRELEASE:
171 case EVENT_FINGERMOTION:
172 HandleFingerEvent((FingerEvent *) &event);
175 case EVENT_TEXTINPUT:
176 HandleTextEvent((TextEvent *) &event);
181 case EVENT_KEYRELEASE:
182 HandleKeyEvent((KeyEvent *) &event);
186 HandleOtherEvents(&event);
193 /* when playing, display a special mouse pointer inside the playfield */
194 if (game_status == GAME_MODE_PLAYING && !tape.pausing)
196 if (!playfield_cursor_set && cursor_inside_playfield &&
197 DelayReached(&playfield_cursor_delay, 1000))
199 SetMouseCursor(CURSOR_PLAYFIELD);
200 playfield_cursor_set = TRUE;
203 else if (playfield_cursor_set)
205 SetMouseCursor(CURSOR_DEFAULT);
206 playfield_cursor_set = FALSE;
212 /* don't use all CPU time when idle; the main loop while playing
213 has its own synchronization and is CPU friendly, too */
215 if (game_status == GAME_MODE_PLAYING)
222 if (!PendingEvent()) /* delay only if no pending events */
226 /* refresh window contents from drawing buffer, if needed */
229 if (game_status == GAME_MODE_QUIT)
234 void HandleOtherEvents(Event *event)
239 HandleExposeEvent((ExposeEvent *) event);
242 case EVENT_UNMAPNOTIFY:
244 /* This causes the game to stop not only when iconified, but also
245 when on another virtual desktop, which might be not desired. */
246 SleepWhileUnmapped();
252 HandleFocusEvent((FocusChangeEvent *) event);
255 case EVENT_CLIENTMESSAGE:
256 HandleClientMessageEvent((ClientMessageEvent *) event);
259 #if defined(TARGET_SDL)
260 case SDL_JOYAXISMOTION:
261 case SDL_JOYBUTTONDOWN:
262 case SDL_JOYBUTTONUP:
263 HandleJoystickEvent(event);
267 HandleWindowManagerEvent(event);
276 void ClearEventQueue()
278 while (PendingEvent())
286 case EVENT_BUTTONRELEASE:
287 button_status = MB_RELEASED;
290 case EVENT_KEYRELEASE:
294 key_joystick_mapping = 0;
299 HandleOtherEvents(&event);
305 void ClearPlayerAction()
309 /* simulate key release events for still pressed keys */
310 key_joystick_mapping = 0;
311 for (i = 0; i < MAX_PLAYERS; i++)
312 stored_player[i].action = 0;
315 void SleepWhileUnmapped()
317 boolean window_unmapped = TRUE;
319 KeyboardAutoRepeatOn();
321 while (window_unmapped)
329 case EVENT_BUTTONRELEASE:
330 button_status = MB_RELEASED;
333 case EVENT_KEYRELEASE:
334 key_joystick_mapping = 0;
337 case EVENT_MAPNOTIFY:
338 window_unmapped = FALSE;
341 case EVENT_UNMAPNOTIFY:
342 /* this is only to surely prevent the 'should not happen' case
343 * of recursively looping between 'SleepWhileUnmapped()' and
344 * 'HandleOtherEvents()' which usually calls this funtion.
349 HandleOtherEvents(&event);
354 if (game_status == GAME_MODE_PLAYING)
355 KeyboardAutoRepeatOffUnlessAutoplay();
358 void HandleExposeEvent(ExposeEvent *event)
360 #if !defined(TARGET_SDL)
361 RedrawPlayfield(FALSE, event->x, event->y, event->width, event->height);
366 void HandleButtonEvent(ButtonEvent *event)
369 Error(ERR_DEBUG, "BUTTON EVENT: button %d %s, x/y %d/%d\n",
371 event->type == EVENT_BUTTONPRESS ? "pressed" : "released",
375 motion_status = FALSE;
377 if (event->type == EVENT_BUTTONPRESS)
378 button_status = event->button;
380 button_status = MB_RELEASED;
382 HandleButton(event->x, event->y, button_status, event->button);
385 void HandleMotionEvent(MotionEvent *event)
387 if (!PointerInWindow(window))
388 return; /* window and pointer are on different screens */
390 if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR)
393 motion_status = TRUE;
396 Error(ERR_DEBUG, "MOTION EVENT: button %d moved, x/y %d/%d\n",
397 button_status, event->x, event->y);
400 HandleButton(event->x, event->y, button_status, button_status);
403 #if defined(TARGET_SDL2)
404 void HandleWindowEvent(WindowEvent *event)
407 int subtype = event->event;
410 (subtype == SDL_WINDOWEVENT_SHOWN ? "SDL_WINDOWEVENT_SHOWN" :
411 subtype == SDL_WINDOWEVENT_HIDDEN ? "SDL_WINDOWEVENT_HIDDEN" :
412 subtype == SDL_WINDOWEVENT_EXPOSED ? "SDL_WINDOWEVENT_EXPOSED" :
413 subtype == SDL_WINDOWEVENT_MOVED ? "SDL_WINDOWEVENT_MOVED" :
414 subtype == SDL_WINDOWEVENT_SIZE_CHANGED ? "SDL_WINDOWEVENT_SIZE_CHANGED" :
415 subtype == SDL_WINDOWEVENT_RESIZED ? "SDL_WINDOWEVENT_RESIZED" :
416 subtype == SDL_WINDOWEVENT_MINIMIZED ? "SDL_WINDOWEVENT_MINIMIZED" :
417 subtype == SDL_WINDOWEVENT_MAXIMIZED ? "SDL_WINDOWEVENT_MAXIMIZED" :
418 subtype == SDL_WINDOWEVENT_RESTORED ? "SDL_WINDOWEVENT_RESTORED" :
419 subtype == SDL_WINDOWEVENT_ENTER ? "SDL_WINDOWEVENT_ENTER" :
420 subtype == SDL_WINDOWEVENT_LEAVE ? "SDL_WINDOWEVENT_LEAVE" :
421 subtype == SDL_WINDOWEVENT_FOCUS_GAINED ? "SDL_WINDOWEVENT_FOCUS_GAINED" :
422 subtype == SDL_WINDOWEVENT_FOCUS_LOST ? "SDL_WINDOWEVENT_FOCUS_LOST" :
423 subtype == SDL_WINDOWEVENT_CLOSE ? "SDL_WINDOWEVENT_CLOSE" :
426 Error(ERR_DEBUG, "WINDOW EVENT: '%s', %ld, %ld",
427 event_name, event->data1, event->data2);
430 if (event->event == SDL_WINDOWEVENT_EXPOSED)
434 if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED)
436 // if game started in fullscreen mode, window will also get fullscreen size
437 if (!video.fullscreen_enabled && video.fullscreen_initial)
439 SDLSetWindowScaling(setup.window_scaling_percent);
441 // only do this correction once
442 video.fullscreen_initial = FALSE;
447 if (event->event == SDL_WINDOWEVENT_RESIZED && !video.fullscreen_enabled)
450 int new_window_width = event->data1;
451 int new_window_height = event->data2;
453 printf("::: RESIZED from %d, %d to %d, %d\n",
454 video.window_width, video.window_height,
455 new_window_width, new_window_height);
457 // if window size has changed after resizing, calculate new scaling factor
458 if (new_window_width != video.window_width ||
459 new_window_height != video.window_height)
461 int new_xpercent = (100 * new_window_width / video.width);
462 int new_ypercent = (100 * new_window_height / video.height);
464 setup.window_scaling_percent = video.window_scaling_percent =
465 MIN(MAX(MIN_WINDOW_SCALING_PERCENT, MIN(new_xpercent, new_ypercent)),
466 MAX_WINDOW_SCALING_PERCENT);
468 video.window_width = new_window_width;
469 video.window_height = new_window_height;
471 printf("::: setup.window_scaling_percent set to %d\n",
472 setup.window_scaling_percent);
474 if (game_status == GAME_MODE_SETUP)
475 RedrawSetupScreenAfterFullscreenToggle();
478 // prevent slightly wrong scaling factor due to rounding differences
479 float scaling_factor = (float)setup.window_scaling_percent / 100;
480 int old_xsize = (int)(scaling_factor * video.width);
481 int old_ysize = (int)(scaling_factor * video.height);
482 int new_xsize = event->data1;
483 int new_ysize = event->data2;
485 // window size is unchanged when going from fullscreen to window mode,
486 // but reverse calculation of scaling factor might result in a scaling
487 // factor that is slightly different due to rounding differences;
488 // therefore compare old/new window size and not old/new scaling factor
489 if (old_xsize != new_xsize ||
490 old_ysize != new_ysize)
492 int new_xpercent = (100 * new_xsize / video.width);
493 int new_ypercent = (100 * new_ysize / video.height);
495 setup.window_scaling_percent = MIN(new_xpercent, new_ypercent);
497 if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
498 setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
499 else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
500 setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
502 printf("::: setup.window_scaling_percent set to %d\n",
503 setup.window_scaling_percent);
509 void HandleFingerEvent(FingerEvent *event)
512 static int num_events = 0;
517 Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
518 event->type == EVENT_FINGERPRESS ? "pressed" :
519 event->type == EVENT_FINGERRELEASE ? "released" : "moved",
523 event->dx, event->dy,
528 int x = (int)(event->x * video.width);
529 int y = (int)(event->y * video.height);
530 int button = MB_LEFTBUTTON;
532 Error(ERR_DEBUG, "=> screen x/y %d/%d", x, y);
536 if (++num_events >= max_events)
542 if (event->type == EVENT_FINGERPRESS ||
543 event->type == EVENT_FINGERMOTION)
544 button_status = button;
546 button_status = MB_RELEASED;
548 int max_x = SX + SXSIZE;
549 int max_y = SY + SYSIZE;
553 if (game_status == GAME_MODE_PLAYING)
555 if (game_status == GAME_MODE_PLAYING &&
559 int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
562 Key key = (event->y < 1.0 / 3.0 ? setup.input[0].key.up :
563 event->y > 2.0 / 3.0 ? setup.input[0].key.down :
564 event->x < 1.0 / 3.0 ? setup.input[0].key.left :
565 event->x > 2.0 / 3.0 ? setup.input[0].key.right :
566 setup.input[0].key.drop);
568 Key key = (y < max_y / 3 ? setup.input[0].key.up :
569 y > 2 * max_y / 3 ? setup.input[0].key.down :
570 x < max_x / 3 ? setup.input[0].key.left :
571 x > 2 * max_x / 3 ? setup.input[0].key.right :
572 setup.input[0].key.drop);
575 Error(ERR_DEBUG, "=> key == %d, key_status == %d", key, key_status);
577 HandleKey(key, key_status);
582 Error(ERR_DEBUG, "::: button_status == %d, button == %d\n",
583 button_status, button);
585 HandleButton(x, y, button_status, button);
591 static boolean checkTextInputKeyModState()
593 // when playing, only handle raw key events and ignore text input
594 if (game_status == GAME_MODE_PLAYING)
597 return ((GetKeyModState() & KMOD_TextInput) != KMOD_None);
600 void HandleTextEvent(TextEvent *event)
602 char *text = event->text;
603 Key key = getKeyFromKeyName(text);
606 Error(ERR_DEBUG, "TEXT EVENT: text == '%s' [%d byte(s), '%c'/%d], resulting key == %d (%s)",
609 text[0], (int)(text[0]),
611 getKeyNameFromKey(key));
614 // if (game_status != GAME_MODE_PLAYING && GetKeyModState() != KMOD_None)
616 if (game_status != GAME_MODE_PLAYING &&
617 (GetKeyModState() & KMOD_TextInput) != KMOD_None)
619 if (checkTextInputKeyModState())
621 HandleKey(key, KEY_PRESSED);
622 HandleKey(key, KEY_RELEASED);
627 void HandleKeyEvent(KeyEvent *event)
629 int key_status = (event->type == EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
630 boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
631 Key key = GetEventKey(event, with_modifiers);
632 Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
635 Error(ERR_DEBUG, "KEY EVENT: key was %s, keysym.scancode == %d, keysym.sym == %d, keymod = %d, GetKeyModState() = 0x%04x, resulting key == %d (%s)",
636 event->type == EVENT_KEYPRESS ? "pressed" : "released",
637 event->keysym.scancode,
642 getKeyNameFromKey(key));
646 if (key == KSYM_Menu)
647 Error(ERR_DEBUG, "menu key pressed");
648 else if (key == KSYM_Back)
649 Error(ERR_DEBUG, "back key pressed");
652 #if defined(PLATFORM_ANDROID)
653 // always map the "back" button to the "escape" key on Android devices
654 if (key == KSYM_Back)
658 HandleKeyModState(keymod, key_status);
660 #if defined(TARGET_SDL2)
662 // if (game_status == GAME_MODE_PLAYING || GetKeyModState() == KMOD_None)
664 if (game_status == GAME_MODE_PLAYING ||
665 (GetKeyModState() & KMOD_TextInput) == KMOD_None)
667 if (!checkTextInputKeyModState())
668 HandleKey(key, key_status);
670 HandleKey(key, key_status);
674 void HandleFocusEvent(FocusChangeEvent *event)
676 static int old_joystick_status = -1;
678 if (event->type == EVENT_FOCUSOUT)
680 KeyboardAutoRepeatOn();
681 old_joystick_status = joystick.status;
682 joystick.status = JOYSTICK_NOT_AVAILABLE;
686 else if (event->type == EVENT_FOCUSIN)
688 /* When there are two Rocks'n'Diamonds windows which overlap and
689 the player moves the pointer from one game window to the other,
690 a 'FocusOut' event is generated for the window the pointer is
691 leaving and a 'FocusIn' event is generated for the window the
692 pointer is entering. In some cases, it can happen that the
693 'FocusIn' event is handled by the one game process before the
694 'FocusOut' event by the other game process. In this case the
695 X11 environment would end up with activated keyboard auto repeat,
696 because unfortunately this is a global setting and not (which
697 would be far better) set for each X11 window individually.
698 The effect would be keyboard auto repeat while playing the game
699 (game_status == GAME_MODE_PLAYING), which is not desired.
700 To avoid this special case, we just wait 1/10 second before
701 processing the 'FocusIn' event.
704 if (game_status == GAME_MODE_PLAYING)
707 KeyboardAutoRepeatOffUnlessAutoplay();
710 if (old_joystick_status != -1)
711 joystick.status = old_joystick_status;
715 void HandleClientMessageEvent(ClientMessageEvent *event)
717 if (CheckCloseWindowEvent(event))
721 void HandleWindowManagerEvent(Event *event)
723 #if defined(TARGET_SDL)
724 SDLHandleWindowManagerEvent(event);
728 void HandleButton(int mx, int my, int button, int button_nr)
730 static int old_mx = 0, old_my = 0;
744 if (HandleGadgets(mx, my, button))
746 /* do not handle this button event anymore */
747 mx = my = -32; /* force mouse event to be outside screen tiles */
750 /* do not use scroll wheel button events for anything other than gadgets */
751 if (IS_WHEEL_BUTTON(button_nr))
755 Error(ERR_DEBUG, "::: game_status == %d", game_status);
760 case GAME_MODE_TITLE:
761 HandleTitleScreen(mx, my, 0, 0, button);
765 HandleMainMenu(mx, my, 0, 0, button);
768 case GAME_MODE_PSEUDO_TYPENAME:
769 HandleTypeName(0, KSYM_Return);
772 case GAME_MODE_LEVELS:
773 HandleChooseLevelSet(mx, my, 0, 0, button);
776 case GAME_MODE_LEVELNR:
777 HandleChooseLevelNr(mx, my, 0, 0, button);
780 case GAME_MODE_SCORES:
781 HandleHallOfFame(0, 0, 0, 0, button);
784 case GAME_MODE_EDITOR:
785 HandleLevelEditorIdle();
789 HandleInfoScreen(mx, my, 0, 0, button);
792 case GAME_MODE_SETUP:
793 HandleSetupScreen(mx, my, 0, 0, button);
796 case GAME_MODE_PLAYING:
798 if (button == MB_PRESSED && !motion_status && IN_GFX_SCREEN(mx, my))
799 DumpTile(LEVELX((mx - SX) / TILEX), LEVELY((my - SY) / TILEY));
808 static boolean is_string_suffix(char *string, char *suffix)
810 int string_len = strlen(string);
811 int suffix_len = strlen(suffix);
813 if (suffix_len > string_len)
816 return (strEqual(&string[string_len - suffix_len], suffix));
819 #define MAX_CHEAT_INPUT_LEN 32
821 static void HandleKeysSpecial(Key key)
823 static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
824 char letter = getCharFromKey(key);
825 int cheat_input_len = strlen(cheat_input);
831 if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
833 for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
834 cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
836 cheat_input_len = MAX_CHEAT_INPUT_LEN;
839 cheat_input[cheat_input_len++] = letter;
840 cheat_input[cheat_input_len] = '\0';
843 Error(ERR_DEBUG, "SPECIAL KEY '%s' [%d]\n", cheat_input, cheat_input_len);
846 if (game_status == GAME_MODE_MAIN)
848 if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
849 is_string_suffix(cheat_input, ":ist"))
851 InsertSolutionTape();
853 else if (is_string_suffix(cheat_input, ":reload-graphics") ||
854 is_string_suffix(cheat_input, ":rg"))
856 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
859 else if (is_string_suffix(cheat_input, ":reload-sounds") ||
860 is_string_suffix(cheat_input, ":rs"))
862 ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
865 else if (is_string_suffix(cheat_input, ":reload-music") ||
866 is_string_suffix(cheat_input, ":rm"))
868 ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
871 else if (is_string_suffix(cheat_input, ":reload-artwork") ||
872 is_string_suffix(cheat_input, ":ra"))
874 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
875 1 << ARTWORK_TYPE_SOUNDS |
876 1 << ARTWORK_TYPE_MUSIC);
879 else if (is_string_suffix(cheat_input, ":dump-level") ||
880 is_string_suffix(cheat_input, ":dl"))
884 else if (is_string_suffix(cheat_input, ":dump-tape") ||
885 is_string_suffix(cheat_input, ":dt"))
889 else if (is_string_suffix(cheat_input, ":save-native-level") ||
890 is_string_suffix(cheat_input, ":snl"))
892 SaveNativeLevel(&level);
895 else if (game_status == GAME_MODE_PLAYING)
898 if (is_string_suffix(cheat_input, ".q"))
899 DEBUG_SetMaximumDynamite();
902 else if (game_status == GAME_MODE_EDITOR)
904 if (is_string_suffix(cheat_input, ":dump-brush") ||
905 is_string_suffix(cheat_input, ":DB"))
909 else if (is_string_suffix(cheat_input, ":DDB"))
916 void HandleKey(Key key, int key_status)
918 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
919 static struct SetupKeyboardInfo ski;
920 static struct SetupShortcutInfo ssi;
929 { &ski.left, &ssi.snap_left, DEFAULT_KEY_LEFT, JOY_LEFT },
930 { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
931 { &ski.up, &ssi.snap_up, DEFAULT_KEY_UP, JOY_UP },
932 { &ski.down, &ssi.snap_down, DEFAULT_KEY_DOWN, JOY_DOWN },
933 { &ski.snap, NULL, DEFAULT_KEY_SNAP, JOY_BUTTON_SNAP },
934 { &ski.drop, NULL, DEFAULT_KEY_DROP, JOY_BUTTON_DROP }
939 if (game_status == GAME_MODE_PLAYING)
941 /* only needed for single-step tape recording mode */
942 static boolean clear_snap_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
943 static boolean clear_drop_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
944 static boolean element_snapped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
945 static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
948 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
952 if (setup.input[pnr].use_joystick)
955 ski = setup.input[pnr].key;
957 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
958 if (key == *key_info[i].key_custom)
959 key_action |= key_info[i].action;
961 /* use combined snap+direction keys for the first player only */
964 ssi = setup.shortcut;
966 for (i = 0; i < NUM_DIRECTIONS; i++)
967 if (key == *key_info[i].key_snap)
968 key_action |= key_info[i].action | JOY_BUTTON_SNAP;
971 /* clear delayed snap and drop actions in single step mode (see below) */
972 if (tape.single_step)
974 if (clear_snap_button[pnr])
976 stored_player[pnr].action &= ~KEY_BUTTON_SNAP;
977 clear_snap_button[pnr] = FALSE;
980 if (clear_drop_button[pnr])
982 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
983 clear_drop_button[pnr] = FALSE;
987 if (key_status == KEY_PRESSED)
988 stored_player[pnr].action |= key_action;
990 stored_player[pnr].action &= ~key_action;
992 if (tape.single_step && tape.recording && tape.pausing)
994 if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
996 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
998 /* if snap key already pressed, don't snap when releasing (below) */
999 if (stored_player[pnr].action & KEY_BUTTON_SNAP)
1000 element_snapped[pnr] = TRUE;
1002 /* if drop key already pressed, don't drop when releasing (below) */
1003 if (stored_player[pnr].action & KEY_BUTTON_DROP)
1004 element_dropped[pnr] = TRUE;
1007 else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1009 if (level.game_engine_type == GAME_ENGINE_TYPE_EM ||
1010 level.game_engine_type == GAME_ENGINE_TYPE_SP)
1013 printf("::: drop key pressed\n");
1016 if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1017 getRedDiskReleaseFlag_SP() == 0)
1018 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1020 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1024 else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON)
1026 if (key_action & KEY_BUTTON_SNAP)
1028 /* if snap key was released without moving (see above), snap now */
1029 if (!element_snapped[pnr])
1031 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1033 stored_player[pnr].action |= KEY_BUTTON_SNAP;
1035 /* clear delayed snap button on next event */
1036 clear_snap_button[pnr] = TRUE;
1039 element_snapped[pnr] = FALSE;
1043 if (key_action & KEY_BUTTON_DROP &&
1044 level.game_engine_type == GAME_ENGINE_TYPE_RND)
1046 /* if drop key was released without moving (see above), drop now */
1047 if (!element_dropped[pnr])
1049 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1051 if (level.game_engine_type != GAME_ENGINE_TYPE_SP ||
1052 getRedDiskReleaseFlag_SP() != 0)
1053 stored_player[pnr].action |= KEY_BUTTON_DROP;
1055 /* clear delayed drop button on next event */
1056 clear_drop_button[pnr] = TRUE;
1059 element_dropped[pnr] = FALSE;
1064 else if (tape.recording && tape.pausing)
1066 /* prevent key release events from un-pausing a paused game */
1067 if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
1068 TapeTogglePause(TAPE_TOGGLE_MANUAL);
1074 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1075 if (key == key_info[i].key_default)
1076 joy |= key_info[i].action;
1081 if (key_status == KEY_PRESSED)
1082 key_joystick_mapping |= joy;
1084 key_joystick_mapping &= ~joy;
1089 if (game_status != GAME_MODE_PLAYING)
1090 key_joystick_mapping = 0;
1092 if (key_status == KEY_RELEASED)
1095 if ((key == KSYM_Return || key == KSYM_KP_Enter) &&
1096 (GetKeyModState() & KMOD_Alt) && video.fullscreen_available)
1098 setup.fullscreen = !setup.fullscreen;
1101 printf("::: %d\n", setup.window_scaling_percent);
1104 ToggleFullscreenIfNeeded();
1106 if (game_status == GAME_MODE_SETUP)
1107 RedrawSetupScreenAfterFullscreenToggle();
1112 if ((key == KSYM_minus || key == KSYM_plus || key == KSYM_0) &&
1113 (GetKeyModState() & KMOD_Control) && video.window_scaling_available &&
1114 !video.fullscreen_enabled)
1117 setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
1119 setup.window_scaling_percent +=
1120 (key == KSYM_minus ? -1 : +1) * STEP_WINDOW_SCALING_PERCENT;
1122 if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
1123 setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
1124 else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
1125 setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
1127 ToggleFullscreenIfNeeded();
1129 if (game_status == GAME_MODE_SETUP)
1130 RedrawSetupScreenAfterFullscreenToggle();
1136 if (game_status == GAME_MODE_PLAYING && local_player->LevelSolved_GameEnd &&
1137 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1139 if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
1140 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1148 if (game_status == GAME_MODE_MAIN &&
1149 (key == setup.shortcut.toggle_pause || key == KSYM_space))
1151 StartGameActions(options.network, setup.autorecord, level.random_seed);
1156 if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
1158 if (key == setup.shortcut.save_game)
1160 else if (key == setup.shortcut.load_game)
1162 else if (key == setup.shortcut.toggle_pause)
1163 TapeTogglePause(TAPE_TOGGLE_MANUAL);
1165 HandleTapeButtonKeys(key);
1166 HandleSoundButtonKeys(key);
1169 if (game_status == GAME_MODE_PLAYING && !network_playing)
1171 int centered_player_nr_next = -999;
1173 if (key == setup.shortcut.focus_player_all)
1174 centered_player_nr_next = -1;
1176 for (i = 0; i < MAX_PLAYERS; i++)
1177 if (key == setup.shortcut.focus_player[i])
1178 centered_player_nr_next = i;
1180 if (centered_player_nr_next != -999)
1182 game.centered_player_nr_next = centered_player_nr_next;
1183 game.set_centered_player = TRUE;
1187 tape.centered_player_nr_next = game.centered_player_nr_next;
1188 tape.set_centered_player = TRUE;
1193 HandleKeysSpecial(key);
1195 if (HandleGadgetsKeyInput(key))
1197 if (key != KSYM_Escape) /* always allow ESC key to be handled */
1198 key = KSYM_UNDEFINED;
1201 switch (game_status)
1203 case GAME_MODE_PSEUDO_TYPENAME:
1204 HandleTypeName(0, key);
1207 case GAME_MODE_TITLE:
1208 case GAME_MODE_MAIN:
1209 case GAME_MODE_LEVELS:
1210 case GAME_MODE_LEVELNR:
1211 case GAME_MODE_SETUP:
1212 case GAME_MODE_INFO:
1213 case GAME_MODE_SCORES:
1218 if (game_status == GAME_MODE_TITLE)
1219 HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1220 else if (game_status == GAME_MODE_MAIN)
1221 HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
1222 else if (game_status == GAME_MODE_LEVELS)
1223 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
1224 else if (game_status == GAME_MODE_LEVELNR)
1225 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
1226 else if (game_status == GAME_MODE_SETUP)
1227 HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1228 else if (game_status == GAME_MODE_INFO)
1229 HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1230 else if (game_status == GAME_MODE_SCORES)
1231 HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
1235 if (game_status != GAME_MODE_MAIN)
1236 FadeSkipNextFadeIn();
1238 if (game_status == GAME_MODE_TITLE)
1239 HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1240 else if (game_status == GAME_MODE_LEVELS)
1241 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
1242 else if (game_status == GAME_MODE_LEVELNR)
1243 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
1244 else if (game_status == GAME_MODE_SETUP)
1245 HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1246 else if (game_status == GAME_MODE_INFO)
1247 HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1248 else if (game_status == GAME_MODE_SCORES)
1249 HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
1253 if (game_status == GAME_MODE_LEVELS)
1254 HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1255 else if (game_status == GAME_MODE_LEVELNR)
1256 HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1257 else if (game_status == GAME_MODE_SETUP)
1258 HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1259 else if (game_status == GAME_MODE_INFO)
1260 HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1261 else if (game_status == GAME_MODE_SCORES)
1262 HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1265 case KSYM_Page_Down:
1266 if (game_status == GAME_MODE_LEVELS)
1267 HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1268 else if (game_status == GAME_MODE_LEVELNR)
1269 HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1270 else if (game_status == GAME_MODE_SETUP)
1271 HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1272 else if (game_status == GAME_MODE_INFO)
1273 HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1274 else if (game_status == GAME_MODE_SCORES)
1275 HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1280 GameFrameDelay = (GameFrameDelay == 500 ? GAME_FRAME_DELAY : 500);
1284 setup.sp_show_border_elements = !setup.sp_show_border_elements;
1285 printf("Supaplex border elements %s\n",
1286 setup.sp_show_border_elements ? "enabled" : "disabled");
1295 case GAME_MODE_EDITOR:
1296 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
1297 HandleLevelEditorKeyInput(key);
1300 case GAME_MODE_PLAYING:
1305 RequestQuitGame(setup.ask_on_escape);
1323 if (GameFrameDelay == 500)
1324 GameFrameDelay = GAME_FRAME_DELAY;
1326 GameFrameDelay = 500;
1329 GameFrameDelay = (key - KSYM_0) * 10;
1330 printf("Game speed == %d%% (%d ms delay between two frames)\n",
1331 GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
1337 options.debug = FALSE;
1338 printf("debug mode disabled\n");
1342 options.debug = TRUE;
1343 printf("debug mode enabled\n");
1348 if (!global.fps_slowdown)
1350 global.fps_slowdown = TRUE;
1351 global.fps_slowdown_factor = 2;
1352 printf("fps slowdown enabled -- display only every 2nd frame\n");
1354 else if (global.fps_slowdown_factor == 2)
1356 global.fps_slowdown_factor = 4;
1357 printf("fps slowdown enabled -- display only every 4th frame\n");
1361 global.fps_slowdown = FALSE;
1362 global.fps_slowdown_factor = 1;
1363 printf("fps slowdown disabled\n");
1368 ScrollStepSize = TILEX / 8;
1369 printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
1373 ScrollStepSize = TILEX / 4;
1374 printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
1378 ScrollStepSize = TILEX / 2;
1379 printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
1383 ScrollStepSize = TILEX;
1384 printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
1388 printf("::: currently using game engine version %d\n",
1389 game.engine_version);
1400 if (key == KSYM_Escape)
1402 game_status = GAME_MODE_MAIN;
1410 void HandleNoEvent()
1412 if (button_status && game_status != GAME_MODE_PLAYING)
1414 HandleButton(0, 0, -button_status, button_status);
1419 #if defined(NETWORK_AVALIABLE)
1420 if (options.network)
1427 static int HandleJoystickForAllPlayers()
1432 for (i = 0; i < MAX_PLAYERS; i++)
1434 byte joy_action = 0;
1437 if (!setup.input[i].use_joystick)
1441 joy_action = Joystick(i);
1442 result |= joy_action;
1444 if (!setup.input[i].use_joystick)
1447 stored_player[i].action = joy_action;
1453 void HandleJoystick()
1455 int joystick = HandleJoystickForAllPlayers();
1456 int keyboard = key_joystick_mapping;
1457 int joy = (joystick | keyboard);
1458 int left = joy & JOY_LEFT;
1459 int right = joy & JOY_RIGHT;
1460 int up = joy & JOY_UP;
1461 int down = joy & JOY_DOWN;
1462 int button = joy & JOY_BUTTON;
1463 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1464 int dx = (left ? -1 : right ? 1 : 0);
1465 int dy = (up ? -1 : down ? 1 : 0);
1467 switch (game_status)
1469 case GAME_MODE_TITLE:
1470 case GAME_MODE_MAIN:
1471 case GAME_MODE_LEVELS:
1472 case GAME_MODE_LEVELNR:
1473 case GAME_MODE_SETUP:
1474 case GAME_MODE_INFO:
1476 static unsigned int joystickmove_delay = 0;
1478 if (joystick && !button &&
1479 !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
1480 newbutton = dx = dy = 0;
1482 if (game_status == GAME_MODE_TITLE)
1483 HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1484 else if (game_status == GAME_MODE_MAIN)
1485 HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1486 else if (game_status == GAME_MODE_LEVELS)
1487 HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
1488 else if (game_status == GAME_MODE_LEVELNR)
1489 HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
1490 else if (game_status == GAME_MODE_SETUP)
1491 HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1492 else if (game_status == GAME_MODE_INFO)
1493 HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1497 case GAME_MODE_SCORES:
1498 HandleHallOfFame(0, 0, dx, dy, !newbutton);
1501 case GAME_MODE_EDITOR:
1502 HandleLevelEditorIdle();
1505 case GAME_MODE_PLAYING:
1506 if (tape.playing || keyboard)
1507 newbutton = ((joy & JOY_BUTTON) != 0);
1510 if (local_player->LevelSolved_GameEnd && newbutton)
1512 if (AllPlayersGone && newbutton)