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;
218 /* don't use all CPU time when idle; the main loop while playing
219 has its own synchronization and is CPU friendly, too */
221 if (game_status == GAME_MODE_PLAYING)
228 if (!PendingEvent()) /* delay only if no pending events */
232 /* refresh window contents from drawing buffer, if needed */
235 if (game_status == GAME_MODE_QUIT)
240 void HandleOtherEvents(Event *event)
245 HandleExposeEvent((ExposeEvent *) event);
248 case EVENT_UNMAPNOTIFY:
250 /* This causes the game to stop not only when iconified, but also
251 when on another virtual desktop, which might be not desired. */
252 SleepWhileUnmapped();
258 HandleFocusEvent((FocusChangeEvent *) event);
261 case EVENT_CLIENTMESSAGE:
262 HandleClientMessageEvent((ClientMessageEvent *) event);
265 #if defined(TARGET_SDL)
266 case SDL_JOYAXISMOTION:
267 case SDL_JOYBUTTONDOWN:
268 case SDL_JOYBUTTONUP:
269 HandleJoystickEvent(event);
273 HandleWindowManagerEvent(event);
282 void ClearEventQueue()
284 while (PendingEvent())
292 case EVENT_BUTTONRELEASE:
293 button_status = MB_RELEASED;
296 case EVENT_KEYRELEASE:
300 key_joystick_mapping = 0;
305 HandleOtherEvents(&event);
311 void ClearPlayerAction()
315 /* simulate key release events for still pressed keys */
316 key_joystick_mapping = 0;
317 for (i = 0; i < MAX_PLAYERS; i++)
318 stored_player[i].action = 0;
321 void SleepWhileUnmapped()
323 boolean window_unmapped = TRUE;
325 KeyboardAutoRepeatOn();
327 while (window_unmapped)
335 case EVENT_BUTTONRELEASE:
336 button_status = MB_RELEASED;
339 case EVENT_KEYRELEASE:
340 key_joystick_mapping = 0;
343 case EVENT_MAPNOTIFY:
344 window_unmapped = FALSE;
347 case EVENT_UNMAPNOTIFY:
348 /* this is only to surely prevent the 'should not happen' case
349 * of recursively looping between 'SleepWhileUnmapped()' and
350 * 'HandleOtherEvents()' which usually calls this funtion.
355 HandleOtherEvents(&event);
360 if (game_status == GAME_MODE_PLAYING)
361 KeyboardAutoRepeatOffUnlessAutoplay();
364 void HandleExposeEvent(ExposeEvent *event)
366 #if !defined(TARGET_SDL)
367 RedrawPlayfield(FALSE, event->x, event->y, event->width, event->height);
372 void HandleButtonEvent(ButtonEvent *event)
375 Error(ERR_DEBUG, "BUTTON EVENT: button %d %s, x/y %d/%d\n",
377 event->type == EVENT_BUTTONPRESS ? "pressed" : "released",
381 motion_status = FALSE;
383 if (event->type == EVENT_BUTTONPRESS)
384 button_status = event->button;
386 button_status = MB_RELEASED;
388 HandleButton(event->x, event->y, button_status, event->button);
391 void HandleMotionEvent(MotionEvent *event)
393 if (!PointerInWindow(window))
394 return; /* window and pointer are on different screens */
396 if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR)
399 motion_status = TRUE;
402 Error(ERR_DEBUG, "MOTION EVENT: button %d moved, x/y %d/%d\n",
403 button_status, event->x, event->y);
406 HandleButton(event->x, event->y, button_status, button_status);
409 #if defined(TARGET_SDL2)
410 void HandleWindowEvent(WindowEvent *event)
413 int subtype = event->event;
416 (subtype == SDL_WINDOWEVENT_SHOWN ? "SDL_WINDOWEVENT_SHOWN" :
417 subtype == SDL_WINDOWEVENT_HIDDEN ? "SDL_WINDOWEVENT_HIDDEN" :
418 subtype == SDL_WINDOWEVENT_EXPOSED ? "SDL_WINDOWEVENT_EXPOSED" :
419 subtype == SDL_WINDOWEVENT_MOVED ? "SDL_WINDOWEVENT_MOVED" :
420 subtype == SDL_WINDOWEVENT_SIZE_CHANGED ? "SDL_WINDOWEVENT_SIZE_CHANGED" :
421 subtype == SDL_WINDOWEVENT_RESIZED ? "SDL_WINDOWEVENT_RESIZED" :
422 subtype == SDL_WINDOWEVENT_MINIMIZED ? "SDL_WINDOWEVENT_MINIMIZED" :
423 subtype == SDL_WINDOWEVENT_MAXIMIZED ? "SDL_WINDOWEVENT_MAXIMIZED" :
424 subtype == SDL_WINDOWEVENT_RESTORED ? "SDL_WINDOWEVENT_RESTORED" :
425 subtype == SDL_WINDOWEVENT_ENTER ? "SDL_WINDOWEVENT_ENTER" :
426 subtype == SDL_WINDOWEVENT_LEAVE ? "SDL_WINDOWEVENT_LEAVE" :
427 subtype == SDL_WINDOWEVENT_FOCUS_GAINED ? "SDL_WINDOWEVENT_FOCUS_GAINED" :
428 subtype == SDL_WINDOWEVENT_FOCUS_LOST ? "SDL_WINDOWEVENT_FOCUS_LOST" :
429 subtype == SDL_WINDOWEVENT_CLOSE ? "SDL_WINDOWEVENT_CLOSE" :
432 Error(ERR_DEBUG, "WINDOW EVENT: '%s', %ld, %ld",
433 event_name, event->data1, event->data2);
436 if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED ||
437 event->event == SDL_WINDOWEVENT_RESIZED ||
438 event->event == SDL_WINDOWEVENT_EXPOSED)
442 if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED)
444 // if game started in fullscreen mode, window will also get fullscreen size
445 if (!video.fullscreen_enabled && video.fullscreen_initial)
447 SDLSetWindowScaling(setup.window_scaling_percent);
449 // only do this correction once
450 video.fullscreen_initial = FALSE;
455 if (event->event == SDL_WINDOWEVENT_RESIZED && !video.fullscreen_enabled)
458 int new_window_width = event->data1;
459 int new_window_height = event->data2;
461 // if window size has changed after resizing, calculate new scaling factor
462 if (new_window_width != video.window_width ||
463 new_window_height != video.window_height)
465 int new_xpercent = (100 * new_window_width / video.width);
466 int new_ypercent = (100 * new_window_height / video.height);
468 printf("::: RESIZED from %d, %d to %d, %d\n",
469 video.window_width, video.window_height,
470 new_window_width, new_window_height);
472 setup.window_scaling_percent = video.window_scaling_percent =
473 MIN(MAX(MIN_WINDOW_SCALING_PERCENT, MIN(new_xpercent, new_ypercent)),
474 MAX_WINDOW_SCALING_PERCENT);
476 video.window_width = new_window_width;
477 video.window_height = new_window_height;
479 printf("::: setup.window_scaling_percent set to %d\n",
480 setup.window_scaling_percent);
482 if (game_status == GAME_MODE_SETUP)
483 RedrawSetupScreenAfterFullscreenToggle();
486 // prevent slightly wrong scaling factor due to rounding differences
487 float scaling_factor = (float)setup.window_scaling_percent / 100;
488 int old_xsize = (int)(scaling_factor * video.width);
489 int old_ysize = (int)(scaling_factor * video.height);
490 int new_xsize = event->data1;
491 int new_ysize = event->data2;
493 // window size is unchanged when going from fullscreen to window mode,
494 // but reverse calculation of scaling factor might result in a scaling
495 // factor that is slightly different due to rounding differences;
496 // therefore compare old/new window size and not old/new scaling factor
497 if (old_xsize != new_xsize ||
498 old_ysize != new_ysize)
500 int new_xpercent = (100 * new_xsize / video.width);
501 int new_ypercent = (100 * new_ysize / video.height);
503 setup.window_scaling_percent = MIN(new_xpercent, new_ypercent);
505 if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
506 setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
507 else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
508 setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
510 printf("::: setup.window_scaling_percent set to %d\n",
511 setup.window_scaling_percent);
517 void HandleFingerEvent(FingerEvent *event)
520 static int num_events = 0;
526 Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
527 event->type == EVENT_FINGERPRESS ? "pressed" :
528 event->type == EVENT_FINGERRELEASE ? "released" : "moved",
532 event->dx, event->dy,
538 int x = (int)(event->x * video.width);
539 int y = (int)(event->y * video.height);
540 int button = MB_LEFTBUTTON;
542 Error(ERR_DEBUG, "=> screen x/y %d/%d", x, y);
546 if (++num_events >= max_events)
552 if (event->type == EVENT_FINGERPRESS ||
553 event->type == EVENT_FINGERMOTION)
554 button_status = button;
556 button_status = MB_RELEASED;
558 int max_x = SX + SXSIZE;
559 int max_y = SY + SYSIZE;
563 if (game_status == GAME_MODE_PLAYING)
565 if (game_status == GAME_MODE_PLAYING &&
569 int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
572 Key key = (event->y < 1.0 / 3.0 ? setup.input[0].key.up :
573 event->y > 2.0 / 3.0 ? setup.input[0].key.down :
574 event->x < 1.0 / 3.0 ? setup.input[0].key.left :
575 event->x > 2.0 / 3.0 ? setup.input[0].key.right :
576 setup.input[0].key.drop);
578 Key key = (y < max_y / 3 ? setup.input[0].key.up :
579 y > 2 * max_y / 3 ? setup.input[0].key.down :
580 x < max_x / 3 ? setup.input[0].key.left :
581 x > 2 * max_x / 3 ? setup.input[0].key.right :
582 setup.input[0].key.drop);
585 Error(ERR_DEBUG, "=> key == %d, key_status == %d", key, key_status);
587 HandleKey(key, key_status);
592 Error(ERR_DEBUG, "::: button_status == %d, button == %d\n",
593 button_status, button);
595 HandleButton(x, y, button_status, button);
601 static boolean checkTextInputKeyModState()
603 // when playing, only handle raw key events and ignore text input
604 if (game_status == GAME_MODE_PLAYING)
607 return ((GetKeyModState() & KMOD_TextInput) != KMOD_None);
610 void HandleTextEvent(TextEvent *event)
612 char *text = event->text;
613 Key key = getKeyFromKeyName(text);
616 Error(ERR_DEBUG, "TEXT EVENT: text == '%s' [%d byte(s), '%c'/%d], resulting key == %d (%s) [%04x]",
619 text[0], (int)(text[0]),
621 getKeyNameFromKey(key),
625 // if (game_status != GAME_MODE_PLAYING && GetKeyModState() != KMOD_None)
627 if (game_status != GAME_MODE_PLAYING &&
628 (GetKeyModState() & KMOD_TextInput) != KMOD_None)
630 if (checkTextInputKeyModState())
632 HandleKey(key, KEY_PRESSED);
633 HandleKey(key, KEY_RELEASED);
638 void HandleKeyEvent(KeyEvent *event)
640 int key_status = (event->type == EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
641 boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
642 Key key = GetEventKey(event, with_modifiers);
643 Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
646 Error(ERR_DEBUG, "KEY EVENT: key was %s, keysym.scancode == %d, keysym.sym == %d, keymod = %d, GetKeyModState() = 0x%04x, resulting key == %d (%s)",
647 event->type == EVENT_KEYPRESS ? "pressed" : "released",
648 event->keysym.scancode,
653 getKeyNameFromKey(key));
657 if (key == KSYM_Menu)
658 Error(ERR_DEBUG, "menu key pressed");
659 else if (key == KSYM_Back)
660 Error(ERR_DEBUG, "back key pressed");
663 #if defined(PLATFORM_ANDROID)
664 // always map the "back" button to the "escape" key on Android devices
665 if (key == KSYM_Back)
669 HandleKeyModState(keymod, key_status);
671 #if defined(TARGET_SDL2)
673 // if (game_status == GAME_MODE_PLAYING || GetKeyModState() == KMOD_None)
675 if (game_status == GAME_MODE_PLAYING ||
676 (GetKeyModState() & KMOD_TextInput) == KMOD_None)
678 if (!checkTextInputKeyModState())
679 HandleKey(key, key_status);
681 HandleKey(key, key_status);
685 void HandleFocusEvent(FocusChangeEvent *event)
687 static int old_joystick_status = -1;
689 if (event->type == EVENT_FOCUSOUT)
691 KeyboardAutoRepeatOn();
692 old_joystick_status = joystick.status;
693 joystick.status = JOYSTICK_NOT_AVAILABLE;
697 else if (event->type == EVENT_FOCUSIN)
699 /* When there are two Rocks'n'Diamonds windows which overlap and
700 the player moves the pointer from one game window to the other,
701 a 'FocusOut' event is generated for the window the pointer is
702 leaving and a 'FocusIn' event is generated for the window the
703 pointer is entering. In some cases, it can happen that the
704 'FocusIn' event is handled by the one game process before the
705 'FocusOut' event by the other game process. In this case the
706 X11 environment would end up with activated keyboard auto repeat,
707 because unfortunately this is a global setting and not (which
708 would be far better) set for each X11 window individually.
709 The effect would be keyboard auto repeat while playing the game
710 (game_status == GAME_MODE_PLAYING), which is not desired.
711 To avoid this special case, we just wait 1/10 second before
712 processing the 'FocusIn' event.
715 if (game_status == GAME_MODE_PLAYING)
718 KeyboardAutoRepeatOffUnlessAutoplay();
721 if (old_joystick_status != -1)
722 joystick.status = old_joystick_status;
726 void HandleClientMessageEvent(ClientMessageEvent *event)
728 if (CheckCloseWindowEvent(event))
732 void HandleWindowManagerEvent(Event *event)
734 #if defined(TARGET_SDL)
735 SDLHandleWindowManagerEvent(event);
739 void HandleButton(int mx, int my, int button, int button_nr)
741 static int old_mx = 0, old_my = 0;
755 if (HandleGadgets(mx, my, button))
757 /* do not handle this button event anymore */
758 mx = my = -32; /* force mouse event to be outside screen tiles */
761 /* do not use scroll wheel button events for anything other than gadgets */
762 if (IS_WHEEL_BUTTON(button_nr))
766 Error(ERR_DEBUG, "::: game_status == %d", game_status);
771 case GAME_MODE_TITLE:
772 HandleTitleScreen(mx, my, 0, 0, button);
776 HandleMainMenu(mx, my, 0, 0, button);
779 case GAME_MODE_PSEUDO_TYPENAME:
780 HandleTypeName(0, KSYM_Return);
783 case GAME_MODE_LEVELS:
784 HandleChooseLevelSet(mx, my, 0, 0, button);
787 case GAME_MODE_LEVELNR:
788 HandleChooseLevelNr(mx, my, 0, 0, button);
791 case GAME_MODE_SCORES:
792 HandleHallOfFame(0, 0, 0, 0, button);
795 case GAME_MODE_EDITOR:
796 HandleLevelEditorIdle();
800 HandleInfoScreen(mx, my, 0, 0, button);
803 case GAME_MODE_SETUP:
804 HandleSetupScreen(mx, my, 0, 0, button);
807 case GAME_MODE_PLAYING:
809 if (button == MB_PRESSED && !motion_status && IN_GFX_SCREEN(mx, my))
810 DumpTile(LEVELX((mx - SX) / TILEX), LEVELY((my - SY) / TILEY));
819 static boolean is_string_suffix(char *string, char *suffix)
821 int string_len = strlen(string);
822 int suffix_len = strlen(suffix);
824 if (suffix_len > string_len)
827 return (strEqual(&string[string_len - suffix_len], suffix));
830 #define MAX_CHEAT_INPUT_LEN 32
832 static void HandleKeysSpecial(Key key)
834 static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
835 char letter = getCharFromKey(key);
836 int cheat_input_len = strlen(cheat_input);
842 if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
844 for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
845 cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
847 cheat_input_len = MAX_CHEAT_INPUT_LEN;
850 cheat_input[cheat_input_len++] = letter;
851 cheat_input[cheat_input_len] = '\0';
854 Error(ERR_DEBUG, "SPECIAL KEY '%s' [%d]\n", cheat_input, cheat_input_len);
857 if (game_status == GAME_MODE_MAIN)
859 if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
860 is_string_suffix(cheat_input, ":ist"))
862 InsertSolutionTape();
864 else if (is_string_suffix(cheat_input, ":reload-graphics") ||
865 is_string_suffix(cheat_input, ":rg"))
867 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
870 else if (is_string_suffix(cheat_input, ":reload-sounds") ||
871 is_string_suffix(cheat_input, ":rs"))
873 ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
876 else if (is_string_suffix(cheat_input, ":reload-music") ||
877 is_string_suffix(cheat_input, ":rm"))
879 ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
882 else if (is_string_suffix(cheat_input, ":reload-artwork") ||
883 is_string_suffix(cheat_input, ":ra"))
885 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
886 1 << ARTWORK_TYPE_SOUNDS |
887 1 << ARTWORK_TYPE_MUSIC);
890 else if (is_string_suffix(cheat_input, ":dump-level") ||
891 is_string_suffix(cheat_input, ":dl"))
895 else if (is_string_suffix(cheat_input, ":dump-tape") ||
896 is_string_suffix(cheat_input, ":dt"))
900 else if (is_string_suffix(cheat_input, ":save-native-level") ||
901 is_string_suffix(cheat_input, ":snl"))
903 SaveNativeLevel(&level);
906 else if (game_status == GAME_MODE_PLAYING)
909 if (is_string_suffix(cheat_input, ".q"))
910 DEBUG_SetMaximumDynamite();
913 else if (game_status == GAME_MODE_EDITOR)
915 if (is_string_suffix(cheat_input, ":dump-brush") ||
916 is_string_suffix(cheat_input, ":DB"))
920 else if (is_string_suffix(cheat_input, ":DDB"))
927 void HandleKey(Key key, int key_status)
929 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
930 static struct SetupKeyboardInfo ski;
931 static struct SetupShortcutInfo ssi;
940 { &ski.left, &ssi.snap_left, DEFAULT_KEY_LEFT, JOY_LEFT },
941 { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
942 { &ski.up, &ssi.snap_up, DEFAULT_KEY_UP, JOY_UP },
943 { &ski.down, &ssi.snap_down, DEFAULT_KEY_DOWN, JOY_DOWN },
944 { &ski.snap, NULL, DEFAULT_KEY_SNAP, JOY_BUTTON_SNAP },
945 { &ski.drop, NULL, DEFAULT_KEY_DROP, JOY_BUTTON_DROP }
950 if (game_status == GAME_MODE_PLAYING)
952 /* only needed for single-step tape recording mode */
953 static boolean clear_snap_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
954 static boolean clear_drop_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
955 static boolean element_snapped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
956 static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
959 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
963 if (setup.input[pnr].use_joystick)
966 ski = setup.input[pnr].key;
968 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
969 if (key == *key_info[i].key_custom)
970 key_action |= key_info[i].action;
972 /* use combined snap+direction keys for the first player only */
975 ssi = setup.shortcut;
977 for (i = 0; i < NUM_DIRECTIONS; i++)
978 if (key == *key_info[i].key_snap)
979 key_action |= key_info[i].action | JOY_BUTTON_SNAP;
982 /* clear delayed snap and drop actions in single step mode (see below) */
983 if (tape.single_step)
985 if (clear_snap_button[pnr])
987 stored_player[pnr].action &= ~KEY_BUTTON_SNAP;
988 clear_snap_button[pnr] = FALSE;
991 if (clear_drop_button[pnr])
993 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
994 clear_drop_button[pnr] = FALSE;
998 if (key_status == KEY_PRESSED)
999 stored_player[pnr].action |= key_action;
1001 stored_player[pnr].action &= ~key_action;
1003 if (tape.single_step && tape.recording && tape.pausing)
1005 if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
1007 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1009 /* if snap key already pressed, don't snap when releasing (below) */
1010 if (stored_player[pnr].action & KEY_BUTTON_SNAP)
1011 element_snapped[pnr] = TRUE;
1013 /* if drop key already pressed, don't drop when releasing (below) */
1014 if (stored_player[pnr].action & KEY_BUTTON_DROP)
1015 element_dropped[pnr] = TRUE;
1018 else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1020 if (level.game_engine_type == GAME_ENGINE_TYPE_EM ||
1021 level.game_engine_type == GAME_ENGINE_TYPE_SP)
1024 printf("::: drop key pressed\n");
1027 if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1028 getRedDiskReleaseFlag_SP() == 0)
1029 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1031 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1035 else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON)
1037 if (key_action & KEY_BUTTON_SNAP)
1039 /* if snap key was released without moving (see above), snap now */
1040 if (!element_snapped[pnr])
1042 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1044 stored_player[pnr].action |= KEY_BUTTON_SNAP;
1046 /* clear delayed snap button on next event */
1047 clear_snap_button[pnr] = TRUE;
1050 element_snapped[pnr] = FALSE;
1054 if (key_action & KEY_BUTTON_DROP &&
1055 level.game_engine_type == GAME_ENGINE_TYPE_RND)
1057 /* if drop key was released without moving (see above), drop now */
1058 if (!element_dropped[pnr])
1060 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1062 if (level.game_engine_type != GAME_ENGINE_TYPE_SP ||
1063 getRedDiskReleaseFlag_SP() != 0)
1064 stored_player[pnr].action |= KEY_BUTTON_DROP;
1066 /* clear delayed drop button on next event */
1067 clear_drop_button[pnr] = TRUE;
1070 element_dropped[pnr] = FALSE;
1075 else if (tape.recording && tape.pausing)
1077 /* prevent key release events from un-pausing a paused game */
1078 if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
1079 TapeTogglePause(TAPE_TOGGLE_MANUAL);
1085 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1086 if (key == key_info[i].key_default)
1087 joy |= key_info[i].action;
1092 if (key_status == KEY_PRESSED)
1093 key_joystick_mapping |= joy;
1095 key_joystick_mapping &= ~joy;
1100 if (game_status != GAME_MODE_PLAYING)
1101 key_joystick_mapping = 0;
1103 if (key_status == KEY_RELEASED)
1106 if ((key == KSYM_Return || key == KSYM_KP_Enter) &&
1107 (GetKeyModState() & KMOD_Alt) && video.fullscreen_available)
1109 setup.fullscreen = !setup.fullscreen;
1112 printf("::: %d\n", setup.window_scaling_percent);
1115 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1117 if (game_status == GAME_MODE_SETUP)
1118 RedrawSetupScreenAfterFullscreenToggle();
1123 if ((key == KSYM_minus || key == KSYM_plus || key == KSYM_0) &&
1124 (GetKeyModState() & KMOD_Alt) && video.window_scaling_available &&
1125 !video.fullscreen_enabled)
1128 setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
1130 setup.window_scaling_percent +=
1131 (key == KSYM_minus ? -1 : +1) * STEP_WINDOW_SCALING_PERCENT;
1133 if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
1134 setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
1135 else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
1136 setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
1138 ToggleFullscreenOrChangeWindowScalingIfNeeded();
1140 if (game_status == GAME_MODE_SETUP)
1141 RedrawSetupScreenAfterFullscreenToggle();
1147 if (game_status == GAME_MODE_PLAYING && local_player->LevelSolved_GameEnd &&
1148 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1150 if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
1151 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1159 if (game_status == GAME_MODE_MAIN &&
1160 (key == setup.shortcut.toggle_pause || key == KSYM_space))
1162 StartGameActions(options.network, setup.autorecord, level.random_seed);
1167 if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
1169 if (key == setup.shortcut.save_game)
1171 else if (key == setup.shortcut.load_game)
1173 else if (key == setup.shortcut.toggle_pause)
1174 TapeTogglePause(TAPE_TOGGLE_MANUAL);
1176 HandleTapeButtonKeys(key);
1177 HandleSoundButtonKeys(key);
1180 if (game_status == GAME_MODE_PLAYING && !network_playing)
1182 int centered_player_nr_next = -999;
1184 if (key == setup.shortcut.focus_player_all)
1185 centered_player_nr_next = -1;
1187 for (i = 0; i < MAX_PLAYERS; i++)
1188 if (key == setup.shortcut.focus_player[i])
1189 centered_player_nr_next = i;
1191 if (centered_player_nr_next != -999)
1193 game.centered_player_nr_next = centered_player_nr_next;
1194 game.set_centered_player = TRUE;
1198 tape.centered_player_nr_next = game.centered_player_nr_next;
1199 tape.set_centered_player = TRUE;
1204 HandleKeysSpecial(key);
1206 if (HandleGadgetsKeyInput(key))
1208 if (key != KSYM_Escape) /* always allow ESC key to be handled */
1209 key = KSYM_UNDEFINED;
1212 switch (game_status)
1214 case GAME_MODE_PSEUDO_TYPENAME:
1215 HandleTypeName(0, key);
1218 case GAME_MODE_TITLE:
1219 case GAME_MODE_MAIN:
1220 case GAME_MODE_LEVELS:
1221 case GAME_MODE_LEVELNR:
1222 case GAME_MODE_SETUP:
1223 case GAME_MODE_INFO:
1224 case GAME_MODE_SCORES:
1229 if (game_status == GAME_MODE_TITLE)
1230 HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1231 else if (game_status == GAME_MODE_MAIN)
1232 HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
1233 else if (game_status == GAME_MODE_LEVELS)
1234 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
1235 else if (game_status == GAME_MODE_LEVELNR)
1236 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
1237 else if (game_status == GAME_MODE_SETUP)
1238 HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1239 else if (game_status == GAME_MODE_INFO)
1240 HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1241 else if (game_status == GAME_MODE_SCORES)
1242 HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
1246 if (game_status != GAME_MODE_MAIN)
1247 FadeSkipNextFadeIn();
1249 if (game_status == GAME_MODE_TITLE)
1250 HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1251 else if (game_status == GAME_MODE_LEVELS)
1252 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
1253 else if (game_status == GAME_MODE_LEVELNR)
1254 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
1255 else if (game_status == GAME_MODE_SETUP)
1256 HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1257 else if (game_status == GAME_MODE_INFO)
1258 HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1259 else if (game_status == GAME_MODE_SCORES)
1260 HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
1264 if (game_status == GAME_MODE_LEVELS)
1265 HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1266 else if (game_status == GAME_MODE_LEVELNR)
1267 HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1268 else if (game_status == GAME_MODE_SETUP)
1269 HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1270 else if (game_status == GAME_MODE_INFO)
1271 HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1272 else if (game_status == GAME_MODE_SCORES)
1273 HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1276 case KSYM_Page_Down:
1277 if (game_status == GAME_MODE_LEVELS)
1278 HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1279 else if (game_status == GAME_MODE_LEVELNR)
1280 HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1281 else if (game_status == GAME_MODE_SETUP)
1282 HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1283 else if (game_status == GAME_MODE_INFO)
1284 HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1285 else if (game_status == GAME_MODE_SCORES)
1286 HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1291 GameFrameDelay = (GameFrameDelay == 500 ? GAME_FRAME_DELAY : 500);
1295 setup.sp_show_border_elements = !setup.sp_show_border_elements;
1296 printf("Supaplex border elements %s\n",
1297 setup.sp_show_border_elements ? "enabled" : "disabled");
1306 case GAME_MODE_EDITOR:
1307 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
1308 HandleLevelEditorKeyInput(key);
1311 case GAME_MODE_PLAYING:
1316 RequestQuitGame(setup.ask_on_escape);
1334 if (GameFrameDelay == 500)
1335 GameFrameDelay = GAME_FRAME_DELAY;
1337 GameFrameDelay = 500;
1340 GameFrameDelay = (key - KSYM_0) * 10;
1341 printf("Game speed == %d%% (%d ms delay between two frames)\n",
1342 GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
1348 options.debug = FALSE;
1349 printf("debug mode disabled\n");
1353 options.debug = TRUE;
1354 printf("debug mode enabled\n");
1359 if (!global.fps_slowdown)
1361 global.fps_slowdown = TRUE;
1362 global.fps_slowdown_factor = 2;
1363 printf("fps slowdown enabled -- display only every 2nd frame\n");
1365 else if (global.fps_slowdown_factor == 2)
1367 global.fps_slowdown_factor = 4;
1368 printf("fps slowdown enabled -- display only every 4th frame\n");
1372 global.fps_slowdown = FALSE;
1373 global.fps_slowdown_factor = 1;
1374 printf("fps slowdown disabled\n");
1379 ScrollStepSize = TILEX / 8;
1380 printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
1384 ScrollStepSize = TILEX / 4;
1385 printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
1389 ScrollStepSize = TILEX / 2;
1390 printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
1394 ScrollStepSize = TILEX;
1395 printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
1399 printf("::: currently using game engine version %d\n",
1400 game.engine_version);
1411 if (key == KSYM_Escape)
1413 game_status = GAME_MODE_MAIN;
1421 void HandleNoEvent()
1423 if (button_status && game_status != GAME_MODE_PLAYING)
1425 HandleButton(0, 0, -button_status, button_status);
1436 #if defined(NETWORK_AVALIABLE)
1437 if (options.network)
1441 switch (game_status)
1443 case GAME_MODE_MAIN:
1444 DrawPreviewLevelAnimation();
1448 case GAME_MODE_LEVELS:
1449 case GAME_MODE_LEVELNR:
1450 case GAME_MODE_SETUP:
1451 case GAME_MODE_INFO:
1452 case GAME_MODE_SCORES:
1456 case GAME_MODE_EDITOR:
1457 HandleLevelEditorIdle();
1465 static int HandleJoystickForAllPlayers()
1470 for (i = 0; i < MAX_PLAYERS; i++)
1472 byte joy_action = 0;
1475 if (!setup.input[i].use_joystick)
1479 joy_action = Joystick(i);
1480 result |= joy_action;
1482 if (!setup.input[i].use_joystick)
1485 stored_player[i].action = joy_action;
1491 void HandleJoystick()
1493 int joystick = HandleJoystickForAllPlayers();
1494 int keyboard = key_joystick_mapping;
1495 int joy = (joystick | keyboard);
1496 int left = joy & JOY_LEFT;
1497 int right = joy & JOY_RIGHT;
1498 int up = joy & JOY_UP;
1499 int down = joy & JOY_DOWN;
1500 int button = joy & JOY_BUTTON;
1501 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1502 int dx = (left ? -1 : right ? 1 : 0);
1503 int dy = (up ? -1 : down ? 1 : 0);
1505 switch (game_status)
1507 case GAME_MODE_TITLE:
1508 case GAME_MODE_MAIN:
1509 case GAME_MODE_LEVELS:
1510 case GAME_MODE_LEVELNR:
1511 case GAME_MODE_SETUP:
1512 case GAME_MODE_INFO:
1514 static unsigned int joystickmove_delay = 0;
1516 if (joystick && !button &&
1517 !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
1518 newbutton = dx = dy = 0;
1520 if (game_status == GAME_MODE_TITLE)
1521 HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1522 else if (game_status == GAME_MODE_MAIN)
1523 HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1524 else if (game_status == GAME_MODE_LEVELS)
1525 HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
1526 else if (game_status == GAME_MODE_LEVELNR)
1527 HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
1528 else if (game_status == GAME_MODE_SETUP)
1529 HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1530 else if (game_status == GAME_MODE_INFO)
1531 HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1535 case GAME_MODE_SCORES:
1536 HandleHallOfFame(0, 0, dx, dy, !newbutton);
1540 case GAME_MODE_EDITOR:
1541 HandleLevelEditorIdle();
1545 case GAME_MODE_PLAYING:
1546 if (tape.playing || keyboard)
1547 newbutton = ((joy & JOY_BUTTON) != 0);
1550 if (newbutton && local_player->LevelSolved_GameEnd)
1552 if (newbutton && AllPlayersGone)