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 // !!! this may result in "HandleNoEvent()" never being called
194 // !!! (especially due to continuously processed tocuh events)
195 // !!! and therefore toon animations being stopped while events
196 // !!! are being processed (even if they are all thrown away)
200 /* when playing, display a special mouse pointer inside the playfield */
201 if (game_status == GAME_MODE_PLAYING && !tape.pausing)
203 if (!playfield_cursor_set && cursor_inside_playfield &&
204 DelayReached(&playfield_cursor_delay, 1000))
206 SetMouseCursor(CURSOR_PLAYFIELD);
207 playfield_cursor_set = TRUE;
210 else if (playfield_cursor_set)
212 SetMouseCursor(CURSOR_DEFAULT);
213 playfield_cursor_set = FALSE;
219 /* don't use all CPU time when idle; the main loop while playing
220 has its own synchronization and is CPU friendly, too */
222 if (game_status == GAME_MODE_PLAYING)
229 if (!PendingEvent()) /* delay only if no pending events */
233 /* refresh window contents from drawing buffer, if needed */
236 if (game_status == GAME_MODE_QUIT)
241 void HandleOtherEvents(Event *event)
246 HandleExposeEvent((ExposeEvent *) event);
249 case EVENT_UNMAPNOTIFY:
251 /* This causes the game to stop not only when iconified, but also
252 when on another virtual desktop, which might be not desired. */
253 SleepWhileUnmapped();
259 HandleFocusEvent((FocusChangeEvent *) event);
262 case EVENT_CLIENTMESSAGE:
263 HandleClientMessageEvent((ClientMessageEvent *) event);
266 #if defined(TARGET_SDL)
267 case SDL_JOYAXISMOTION:
268 case SDL_JOYBUTTONDOWN:
269 case SDL_JOYBUTTONUP:
270 HandleJoystickEvent(event);
274 HandleWindowManagerEvent(event);
283 void ClearEventQueue()
285 while (PendingEvent())
293 case EVENT_BUTTONRELEASE:
294 button_status = MB_RELEASED;
297 case EVENT_KEYRELEASE:
301 key_joystick_mapping = 0;
306 HandleOtherEvents(&event);
312 void ClearPlayerAction()
316 /* simulate key release events for still pressed keys */
317 key_joystick_mapping = 0;
318 for (i = 0; i < MAX_PLAYERS; i++)
319 stored_player[i].action = 0;
322 void SleepWhileUnmapped()
324 boolean window_unmapped = TRUE;
326 KeyboardAutoRepeatOn();
328 while (window_unmapped)
336 case EVENT_BUTTONRELEASE:
337 button_status = MB_RELEASED;
340 case EVENT_KEYRELEASE:
341 key_joystick_mapping = 0;
344 case EVENT_MAPNOTIFY:
345 window_unmapped = FALSE;
348 case EVENT_UNMAPNOTIFY:
349 /* this is only to surely prevent the 'should not happen' case
350 * of recursively looping between 'SleepWhileUnmapped()' and
351 * 'HandleOtherEvents()' which usually calls this funtion.
356 HandleOtherEvents(&event);
361 if (game_status == GAME_MODE_PLAYING)
362 KeyboardAutoRepeatOffUnlessAutoplay();
365 void HandleExposeEvent(ExposeEvent *event)
367 #if !defined(TARGET_SDL)
368 RedrawPlayfield(FALSE, event->x, event->y, event->width, event->height);
373 void HandleButtonEvent(ButtonEvent *event)
376 Error(ERR_DEBUG, "BUTTON EVENT: button %d %s, x/y %d/%d\n",
378 event->type == EVENT_BUTTONPRESS ? "pressed" : "released",
382 motion_status = FALSE;
384 if (event->type == EVENT_BUTTONPRESS)
385 button_status = event->button;
387 button_status = MB_RELEASED;
389 HandleButton(event->x, event->y, button_status, event->button);
392 void HandleMotionEvent(MotionEvent *event)
394 if (!PointerInWindow(window))
395 return; /* window and pointer are on different screens */
397 if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR)
400 motion_status = TRUE;
403 Error(ERR_DEBUG, "MOTION EVENT: button %d moved, x/y %d/%d\n",
404 button_status, event->x, event->y);
407 HandleButton(event->x, event->y, button_status, button_status);
410 #if defined(TARGET_SDL2)
411 void HandleWindowEvent(WindowEvent *event)
414 int subtype = event->event;
417 (subtype == SDL_WINDOWEVENT_SHOWN ? "SDL_WINDOWEVENT_SHOWN" :
418 subtype == SDL_WINDOWEVENT_HIDDEN ? "SDL_WINDOWEVENT_HIDDEN" :
419 subtype == SDL_WINDOWEVENT_EXPOSED ? "SDL_WINDOWEVENT_EXPOSED" :
420 subtype == SDL_WINDOWEVENT_MOVED ? "SDL_WINDOWEVENT_MOVED" :
421 subtype == SDL_WINDOWEVENT_SIZE_CHANGED ? "SDL_WINDOWEVENT_SIZE_CHANGED" :
422 subtype == SDL_WINDOWEVENT_RESIZED ? "SDL_WINDOWEVENT_RESIZED" :
423 subtype == SDL_WINDOWEVENT_MINIMIZED ? "SDL_WINDOWEVENT_MINIMIZED" :
424 subtype == SDL_WINDOWEVENT_MAXIMIZED ? "SDL_WINDOWEVENT_MAXIMIZED" :
425 subtype == SDL_WINDOWEVENT_RESTORED ? "SDL_WINDOWEVENT_RESTORED" :
426 subtype == SDL_WINDOWEVENT_ENTER ? "SDL_WINDOWEVENT_ENTER" :
427 subtype == SDL_WINDOWEVENT_LEAVE ? "SDL_WINDOWEVENT_LEAVE" :
428 subtype == SDL_WINDOWEVENT_FOCUS_GAINED ? "SDL_WINDOWEVENT_FOCUS_GAINED" :
429 subtype == SDL_WINDOWEVENT_FOCUS_LOST ? "SDL_WINDOWEVENT_FOCUS_LOST" :
430 subtype == SDL_WINDOWEVENT_CLOSE ? "SDL_WINDOWEVENT_CLOSE" :
433 Error(ERR_DEBUG, "WINDOW EVENT: '%s', %ld, %ld",
434 event_name, event->data1, event->data2);
437 if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED ||
438 event->event == SDL_WINDOWEVENT_RESIZED ||
439 event->event == SDL_WINDOWEVENT_EXPOSED)
443 if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED)
445 // if game started in fullscreen mode, window will also get fullscreen size
446 if (!video.fullscreen_enabled && video.fullscreen_initial)
448 SDLSetWindowScaling(setup.window_scaling_percent);
450 // only do this correction once
451 video.fullscreen_initial = FALSE;
456 if (event->event == SDL_WINDOWEVENT_RESIZED && !video.fullscreen_enabled)
459 int new_window_width = event->data1;
460 int new_window_height = event->data2;
462 printf("::: RESIZED from %d, %d to %d, %d\n",
463 video.window_width, video.window_height,
464 new_window_width, new_window_height);
466 // if window size has changed after resizing, calculate new scaling factor
467 if (new_window_width != video.window_width ||
468 new_window_height != video.window_height)
470 int new_xpercent = (100 * new_window_width / video.width);
471 int new_ypercent = (100 * new_window_height / video.height);
473 setup.window_scaling_percent = video.window_scaling_percent =
474 MIN(MAX(MIN_WINDOW_SCALING_PERCENT, MIN(new_xpercent, new_ypercent)),
475 MAX_WINDOW_SCALING_PERCENT);
477 video.window_width = new_window_width;
478 video.window_height = new_window_height;
480 printf("::: setup.window_scaling_percent set to %d\n",
481 setup.window_scaling_percent);
483 if (game_status == GAME_MODE_SETUP)
484 RedrawSetupScreenAfterFullscreenToggle();
487 // prevent slightly wrong scaling factor due to rounding differences
488 float scaling_factor = (float)setup.window_scaling_percent / 100;
489 int old_xsize = (int)(scaling_factor * video.width);
490 int old_ysize = (int)(scaling_factor * video.height);
491 int new_xsize = event->data1;
492 int new_ysize = event->data2;
494 // window size is unchanged when going from fullscreen to window mode,
495 // but reverse calculation of scaling factor might result in a scaling
496 // factor that is slightly different due to rounding differences;
497 // therefore compare old/new window size and not old/new scaling factor
498 if (old_xsize != new_xsize ||
499 old_ysize != new_ysize)
501 int new_xpercent = (100 * new_xsize / video.width);
502 int new_ypercent = (100 * new_ysize / video.height);
504 setup.window_scaling_percent = MIN(new_xpercent, new_ypercent);
506 if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
507 setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
508 else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
509 setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
511 printf("::: setup.window_scaling_percent set to %d\n",
512 setup.window_scaling_percent);
518 void HandleFingerEvent(FingerEvent *event)
521 static int num_events = 0;
527 Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
528 event->type == EVENT_FINGERPRESS ? "pressed" :
529 event->type == EVENT_FINGERRELEASE ? "released" : "moved",
533 event->dx, event->dy,
539 int x = (int)(event->x * video.width);
540 int y = (int)(event->y * video.height);
541 int button = MB_LEFTBUTTON;
543 Error(ERR_DEBUG, "=> screen x/y %d/%d", x, y);
547 if (++num_events >= max_events)
553 if (event->type == EVENT_FINGERPRESS ||
554 event->type == EVENT_FINGERMOTION)
555 button_status = button;
557 button_status = MB_RELEASED;
559 int max_x = SX + SXSIZE;
560 int max_y = SY + SYSIZE;
564 if (game_status == GAME_MODE_PLAYING)
566 if (game_status == GAME_MODE_PLAYING &&
570 int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
573 Key key = (event->y < 1.0 / 3.0 ? setup.input[0].key.up :
574 event->y > 2.0 / 3.0 ? setup.input[0].key.down :
575 event->x < 1.0 / 3.0 ? setup.input[0].key.left :
576 event->x > 2.0 / 3.0 ? setup.input[0].key.right :
577 setup.input[0].key.drop);
579 Key key = (y < max_y / 3 ? setup.input[0].key.up :
580 y > 2 * max_y / 3 ? setup.input[0].key.down :
581 x < max_x / 3 ? setup.input[0].key.left :
582 x > 2 * max_x / 3 ? setup.input[0].key.right :
583 setup.input[0].key.drop);
586 Error(ERR_DEBUG, "=> key == %d, key_status == %d", key, key_status);
588 HandleKey(key, key_status);
593 Error(ERR_DEBUG, "::: button_status == %d, button == %d\n",
594 button_status, button);
596 HandleButton(x, y, button_status, button);
602 static boolean checkTextInputKeyModState()
604 // when playing, only handle raw key events and ignore text input
605 if (game_status == GAME_MODE_PLAYING)
608 return ((GetKeyModState() & KMOD_TextInput) != KMOD_None);
611 void HandleTextEvent(TextEvent *event)
613 char *text = event->text;
614 Key key = getKeyFromKeyName(text);
617 Error(ERR_DEBUG, "TEXT EVENT: text == '%s' [%d byte(s), '%c'/%d], resulting key == %d (%s) [%04x]",
620 text[0], (int)(text[0]),
622 getKeyNameFromKey(key),
626 // if (game_status != GAME_MODE_PLAYING && GetKeyModState() != KMOD_None)
628 if (game_status != GAME_MODE_PLAYING &&
629 (GetKeyModState() & KMOD_TextInput) != KMOD_None)
631 if (checkTextInputKeyModState())
633 HandleKey(key, KEY_PRESSED);
634 HandleKey(key, KEY_RELEASED);
639 void HandleKeyEvent(KeyEvent *event)
641 int key_status = (event->type == EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
642 boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
643 Key key = GetEventKey(event, with_modifiers);
644 Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
647 Error(ERR_DEBUG, "KEY EVENT: key was %s, keysym.scancode == %d, keysym.sym == %d, keymod = %d, GetKeyModState() = 0x%04x, resulting key == %d (%s)",
648 event->type == EVENT_KEYPRESS ? "pressed" : "released",
649 event->keysym.scancode,
654 getKeyNameFromKey(key));
658 if (key == KSYM_Menu)
659 Error(ERR_DEBUG, "menu key pressed");
660 else if (key == KSYM_Back)
661 Error(ERR_DEBUG, "back key pressed");
664 #if defined(PLATFORM_ANDROID)
665 // always map the "back" button to the "escape" key on Android devices
666 if (key == KSYM_Back)
670 HandleKeyModState(keymod, key_status);
672 #if defined(TARGET_SDL2)
674 // if (game_status == GAME_MODE_PLAYING || GetKeyModState() == KMOD_None)
676 if (game_status == GAME_MODE_PLAYING ||
677 (GetKeyModState() & KMOD_TextInput) == KMOD_None)
679 if (!checkTextInputKeyModState())
680 HandleKey(key, key_status);
682 HandleKey(key, key_status);
686 void HandleFocusEvent(FocusChangeEvent *event)
688 static int old_joystick_status = -1;
690 if (event->type == EVENT_FOCUSOUT)
692 KeyboardAutoRepeatOn();
693 old_joystick_status = joystick.status;
694 joystick.status = JOYSTICK_NOT_AVAILABLE;
698 else if (event->type == EVENT_FOCUSIN)
700 /* When there are two Rocks'n'Diamonds windows which overlap and
701 the player moves the pointer from one game window to the other,
702 a 'FocusOut' event is generated for the window the pointer is
703 leaving and a 'FocusIn' event is generated for the window the
704 pointer is entering. In some cases, it can happen that the
705 'FocusIn' event is handled by the one game process before the
706 'FocusOut' event by the other game process. In this case the
707 X11 environment would end up with activated keyboard auto repeat,
708 because unfortunately this is a global setting and not (which
709 would be far better) set for each X11 window individually.
710 The effect would be keyboard auto repeat while playing the game
711 (game_status == GAME_MODE_PLAYING), which is not desired.
712 To avoid this special case, we just wait 1/10 second before
713 processing the 'FocusIn' event.
716 if (game_status == GAME_MODE_PLAYING)
719 KeyboardAutoRepeatOffUnlessAutoplay();
722 if (old_joystick_status != -1)
723 joystick.status = old_joystick_status;
727 void HandleClientMessageEvent(ClientMessageEvent *event)
729 if (CheckCloseWindowEvent(event))
733 void HandleWindowManagerEvent(Event *event)
735 #if defined(TARGET_SDL)
736 SDLHandleWindowManagerEvent(event);
740 void HandleButton(int mx, int my, int button, int button_nr)
742 static int old_mx = 0, old_my = 0;
756 if (HandleGadgets(mx, my, button))
758 /* do not handle this button event anymore */
759 mx = my = -32; /* force mouse event to be outside screen tiles */
762 /* do not use scroll wheel button events for anything other than gadgets */
763 if (IS_WHEEL_BUTTON(button_nr))
767 Error(ERR_DEBUG, "::: game_status == %d", game_status);
772 case GAME_MODE_TITLE:
773 HandleTitleScreen(mx, my, 0, 0, button);
777 HandleMainMenu(mx, my, 0, 0, button);
780 case GAME_MODE_PSEUDO_TYPENAME:
781 HandleTypeName(0, KSYM_Return);
784 case GAME_MODE_LEVELS:
785 HandleChooseLevelSet(mx, my, 0, 0, button);
788 case GAME_MODE_LEVELNR:
789 HandleChooseLevelNr(mx, my, 0, 0, button);
792 case GAME_MODE_SCORES:
793 HandleHallOfFame(0, 0, 0, 0, button);
796 case GAME_MODE_EDITOR:
797 HandleLevelEditorIdle();
801 HandleInfoScreen(mx, my, 0, 0, button);
804 case GAME_MODE_SETUP:
805 HandleSetupScreen(mx, my, 0, 0, button);
808 case GAME_MODE_PLAYING:
810 if (button == MB_PRESSED && !motion_status && IN_GFX_SCREEN(mx, my))
811 DumpTile(LEVELX((mx - SX) / TILEX), LEVELY((my - SY) / TILEY));
820 static boolean is_string_suffix(char *string, char *suffix)
822 int string_len = strlen(string);
823 int suffix_len = strlen(suffix);
825 if (suffix_len > string_len)
828 return (strEqual(&string[string_len - suffix_len], suffix));
831 #define MAX_CHEAT_INPUT_LEN 32
833 static void HandleKeysSpecial(Key key)
835 static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
836 char letter = getCharFromKey(key);
837 int cheat_input_len = strlen(cheat_input);
843 if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
845 for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
846 cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
848 cheat_input_len = MAX_CHEAT_INPUT_LEN;
851 cheat_input[cheat_input_len++] = letter;
852 cheat_input[cheat_input_len] = '\0';
855 Error(ERR_DEBUG, "SPECIAL KEY '%s' [%d]\n", cheat_input, cheat_input_len);
858 if (game_status == GAME_MODE_MAIN)
860 if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
861 is_string_suffix(cheat_input, ":ist"))
863 InsertSolutionTape();
865 else if (is_string_suffix(cheat_input, ":reload-graphics") ||
866 is_string_suffix(cheat_input, ":rg"))
868 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
871 else if (is_string_suffix(cheat_input, ":reload-sounds") ||
872 is_string_suffix(cheat_input, ":rs"))
874 ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
877 else if (is_string_suffix(cheat_input, ":reload-music") ||
878 is_string_suffix(cheat_input, ":rm"))
880 ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
883 else if (is_string_suffix(cheat_input, ":reload-artwork") ||
884 is_string_suffix(cheat_input, ":ra"))
886 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
887 1 << ARTWORK_TYPE_SOUNDS |
888 1 << ARTWORK_TYPE_MUSIC);
891 else if (is_string_suffix(cheat_input, ":dump-level") ||
892 is_string_suffix(cheat_input, ":dl"))
896 else if (is_string_suffix(cheat_input, ":dump-tape") ||
897 is_string_suffix(cheat_input, ":dt"))
901 else if (is_string_suffix(cheat_input, ":save-native-level") ||
902 is_string_suffix(cheat_input, ":snl"))
904 SaveNativeLevel(&level);
907 else if (game_status == GAME_MODE_PLAYING)
910 if (is_string_suffix(cheat_input, ".q"))
911 DEBUG_SetMaximumDynamite();
914 else if (game_status == GAME_MODE_EDITOR)
916 if (is_string_suffix(cheat_input, ":dump-brush") ||
917 is_string_suffix(cheat_input, ":DB"))
921 else if (is_string_suffix(cheat_input, ":DDB"))
928 void HandleKey(Key key, int key_status)
930 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
931 static struct SetupKeyboardInfo ski;
932 static struct SetupShortcutInfo ssi;
941 { &ski.left, &ssi.snap_left, DEFAULT_KEY_LEFT, JOY_LEFT },
942 { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
943 { &ski.up, &ssi.snap_up, DEFAULT_KEY_UP, JOY_UP },
944 { &ski.down, &ssi.snap_down, DEFAULT_KEY_DOWN, JOY_DOWN },
945 { &ski.snap, NULL, DEFAULT_KEY_SNAP, JOY_BUTTON_SNAP },
946 { &ski.drop, NULL, DEFAULT_KEY_DROP, JOY_BUTTON_DROP }
951 if (game_status == GAME_MODE_PLAYING)
953 /* only needed for single-step tape recording mode */
954 static boolean clear_snap_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
955 static boolean clear_drop_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
956 static boolean element_snapped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
957 static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
960 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
964 if (setup.input[pnr].use_joystick)
967 ski = setup.input[pnr].key;
969 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
970 if (key == *key_info[i].key_custom)
971 key_action |= key_info[i].action;
973 /* use combined snap+direction keys for the first player only */
976 ssi = setup.shortcut;
978 for (i = 0; i < NUM_DIRECTIONS; i++)
979 if (key == *key_info[i].key_snap)
980 key_action |= key_info[i].action | JOY_BUTTON_SNAP;
983 /* clear delayed snap and drop actions in single step mode (see below) */
984 if (tape.single_step)
986 if (clear_snap_button[pnr])
988 stored_player[pnr].action &= ~KEY_BUTTON_SNAP;
989 clear_snap_button[pnr] = FALSE;
992 if (clear_drop_button[pnr])
994 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
995 clear_drop_button[pnr] = FALSE;
999 if (key_status == KEY_PRESSED)
1000 stored_player[pnr].action |= key_action;
1002 stored_player[pnr].action &= ~key_action;
1004 if (tape.single_step && tape.recording && tape.pausing)
1006 if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
1008 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1010 /* if snap key already pressed, don't snap when releasing (below) */
1011 if (stored_player[pnr].action & KEY_BUTTON_SNAP)
1012 element_snapped[pnr] = TRUE;
1014 /* if drop key already pressed, don't drop when releasing (below) */
1015 if (stored_player[pnr].action & KEY_BUTTON_DROP)
1016 element_dropped[pnr] = TRUE;
1019 else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1021 if (level.game_engine_type == GAME_ENGINE_TYPE_EM ||
1022 level.game_engine_type == GAME_ENGINE_TYPE_SP)
1025 printf("::: drop key pressed\n");
1028 if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1029 getRedDiskReleaseFlag_SP() == 0)
1030 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1032 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1036 else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON)
1038 if (key_action & KEY_BUTTON_SNAP)
1040 /* if snap key was released without moving (see above), snap now */
1041 if (!element_snapped[pnr])
1043 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1045 stored_player[pnr].action |= KEY_BUTTON_SNAP;
1047 /* clear delayed snap button on next event */
1048 clear_snap_button[pnr] = TRUE;
1051 element_snapped[pnr] = FALSE;
1055 if (key_action & KEY_BUTTON_DROP &&
1056 level.game_engine_type == GAME_ENGINE_TYPE_RND)
1058 /* if drop key was released without moving (see above), drop now */
1059 if (!element_dropped[pnr])
1061 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1063 if (level.game_engine_type != GAME_ENGINE_TYPE_SP ||
1064 getRedDiskReleaseFlag_SP() != 0)
1065 stored_player[pnr].action |= KEY_BUTTON_DROP;
1067 /* clear delayed drop button on next event */
1068 clear_drop_button[pnr] = TRUE;
1071 element_dropped[pnr] = FALSE;
1076 else if (tape.recording && tape.pausing)
1078 /* prevent key release events from un-pausing a paused game */
1079 if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
1080 TapeTogglePause(TAPE_TOGGLE_MANUAL);
1086 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1087 if (key == key_info[i].key_default)
1088 joy |= key_info[i].action;
1093 if (key_status == KEY_PRESSED)
1094 key_joystick_mapping |= joy;
1096 key_joystick_mapping &= ~joy;
1101 if (game_status != GAME_MODE_PLAYING)
1102 key_joystick_mapping = 0;
1104 if (key_status == KEY_RELEASED)
1107 if ((key == KSYM_Return || key == KSYM_KP_Enter) &&
1108 (GetKeyModState() & KMOD_Alt) && video.fullscreen_available)
1110 setup.fullscreen = !setup.fullscreen;
1113 printf("::: %d\n", setup.window_scaling_percent);
1116 ToggleFullscreenIfNeeded();
1118 if (game_status == GAME_MODE_SETUP)
1119 RedrawSetupScreenAfterFullscreenToggle();
1124 if ((key == KSYM_minus || key == KSYM_plus || key == KSYM_0) &&
1125 (GetKeyModState() & KMOD_Control) && video.window_scaling_available &&
1126 !video.fullscreen_enabled)
1129 setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
1131 setup.window_scaling_percent +=
1132 (key == KSYM_minus ? -1 : +1) * STEP_WINDOW_SCALING_PERCENT;
1134 if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
1135 setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
1136 else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
1137 setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
1139 ToggleFullscreenIfNeeded();
1141 if (game_status == GAME_MODE_SETUP)
1142 RedrawSetupScreenAfterFullscreenToggle();
1148 if (game_status == GAME_MODE_PLAYING && local_player->LevelSolved_GameEnd &&
1149 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1151 if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
1152 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1160 if (game_status == GAME_MODE_MAIN &&
1161 (key == setup.shortcut.toggle_pause || key == KSYM_space))
1163 StartGameActions(options.network, setup.autorecord, level.random_seed);
1168 if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
1170 if (key == setup.shortcut.save_game)
1172 else if (key == setup.shortcut.load_game)
1174 else if (key == setup.shortcut.toggle_pause)
1175 TapeTogglePause(TAPE_TOGGLE_MANUAL);
1177 HandleTapeButtonKeys(key);
1178 HandleSoundButtonKeys(key);
1181 if (game_status == GAME_MODE_PLAYING && !network_playing)
1183 int centered_player_nr_next = -999;
1185 if (key == setup.shortcut.focus_player_all)
1186 centered_player_nr_next = -1;
1188 for (i = 0; i < MAX_PLAYERS; i++)
1189 if (key == setup.shortcut.focus_player[i])
1190 centered_player_nr_next = i;
1192 if (centered_player_nr_next != -999)
1194 game.centered_player_nr_next = centered_player_nr_next;
1195 game.set_centered_player = TRUE;
1199 tape.centered_player_nr_next = game.centered_player_nr_next;
1200 tape.set_centered_player = TRUE;
1205 HandleKeysSpecial(key);
1207 if (HandleGadgetsKeyInput(key))
1209 if (key != KSYM_Escape) /* always allow ESC key to be handled */
1210 key = KSYM_UNDEFINED;
1213 switch (game_status)
1215 case GAME_MODE_PSEUDO_TYPENAME:
1216 HandleTypeName(0, key);
1219 case GAME_MODE_TITLE:
1220 case GAME_MODE_MAIN:
1221 case GAME_MODE_LEVELS:
1222 case GAME_MODE_LEVELNR:
1223 case GAME_MODE_SETUP:
1224 case GAME_MODE_INFO:
1225 case GAME_MODE_SCORES:
1230 if (game_status == GAME_MODE_TITLE)
1231 HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1232 else if (game_status == GAME_MODE_MAIN)
1233 HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
1234 else if (game_status == GAME_MODE_LEVELS)
1235 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
1236 else if (game_status == GAME_MODE_LEVELNR)
1237 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
1238 else if (game_status == GAME_MODE_SETUP)
1239 HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1240 else if (game_status == GAME_MODE_INFO)
1241 HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1242 else if (game_status == GAME_MODE_SCORES)
1243 HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
1247 if (game_status != GAME_MODE_MAIN)
1248 FadeSkipNextFadeIn();
1250 if (game_status == GAME_MODE_TITLE)
1251 HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1252 else if (game_status == GAME_MODE_LEVELS)
1253 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
1254 else if (game_status == GAME_MODE_LEVELNR)
1255 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
1256 else if (game_status == GAME_MODE_SETUP)
1257 HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1258 else if (game_status == GAME_MODE_INFO)
1259 HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1260 else if (game_status == GAME_MODE_SCORES)
1261 HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
1265 if (game_status == GAME_MODE_LEVELS)
1266 HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1267 else if (game_status == GAME_MODE_LEVELNR)
1268 HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1269 else if (game_status == GAME_MODE_SETUP)
1270 HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1271 else if (game_status == GAME_MODE_INFO)
1272 HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1273 else if (game_status == GAME_MODE_SCORES)
1274 HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1277 case KSYM_Page_Down:
1278 if (game_status == GAME_MODE_LEVELS)
1279 HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1280 else if (game_status == GAME_MODE_LEVELNR)
1281 HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1282 else if (game_status == GAME_MODE_SETUP)
1283 HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1284 else if (game_status == GAME_MODE_INFO)
1285 HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1286 else if (game_status == GAME_MODE_SCORES)
1287 HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1292 GameFrameDelay = (GameFrameDelay == 500 ? GAME_FRAME_DELAY : 500);
1296 setup.sp_show_border_elements = !setup.sp_show_border_elements;
1297 printf("Supaplex border elements %s\n",
1298 setup.sp_show_border_elements ? "enabled" : "disabled");
1307 case GAME_MODE_EDITOR:
1308 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
1309 HandleLevelEditorKeyInput(key);
1312 case GAME_MODE_PLAYING:
1317 RequestQuitGame(setup.ask_on_escape);
1335 if (GameFrameDelay == 500)
1336 GameFrameDelay = GAME_FRAME_DELAY;
1338 GameFrameDelay = 500;
1341 GameFrameDelay = (key - KSYM_0) * 10;
1342 printf("Game speed == %d%% (%d ms delay between two frames)\n",
1343 GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
1349 options.debug = FALSE;
1350 printf("debug mode disabled\n");
1354 options.debug = TRUE;
1355 printf("debug mode enabled\n");
1360 if (!global.fps_slowdown)
1362 global.fps_slowdown = TRUE;
1363 global.fps_slowdown_factor = 2;
1364 printf("fps slowdown enabled -- display only every 2nd frame\n");
1366 else if (global.fps_slowdown_factor == 2)
1368 global.fps_slowdown_factor = 4;
1369 printf("fps slowdown enabled -- display only every 4th frame\n");
1373 global.fps_slowdown = FALSE;
1374 global.fps_slowdown_factor = 1;
1375 printf("fps slowdown disabled\n");
1380 ScrollStepSize = TILEX / 8;
1381 printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
1385 ScrollStepSize = TILEX / 4;
1386 printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
1390 ScrollStepSize = TILEX / 2;
1391 printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
1395 ScrollStepSize = TILEX;
1396 printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
1400 printf("::: currently using game engine version %d\n",
1401 game.engine_version);
1412 if (key == KSYM_Escape)
1414 game_status = GAME_MODE_MAIN;
1422 void HandleNoEvent()
1424 if (button_status && game_status != GAME_MODE_PLAYING)
1426 HandleButton(0, 0, -button_status, button_status);
1431 #if defined(NETWORK_AVALIABLE)
1432 if (options.network)
1439 static int HandleJoystickForAllPlayers()
1444 for (i = 0; i < MAX_PLAYERS; i++)
1446 byte joy_action = 0;
1449 if (!setup.input[i].use_joystick)
1453 joy_action = Joystick(i);
1454 result |= joy_action;
1456 if (!setup.input[i].use_joystick)
1459 stored_player[i].action = joy_action;
1465 void HandleJoystick()
1467 int joystick = HandleJoystickForAllPlayers();
1468 int keyboard = key_joystick_mapping;
1469 int joy = (joystick | keyboard);
1470 int left = joy & JOY_LEFT;
1471 int right = joy & JOY_RIGHT;
1472 int up = joy & JOY_UP;
1473 int down = joy & JOY_DOWN;
1474 int button = joy & JOY_BUTTON;
1475 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1476 int dx = (left ? -1 : right ? 1 : 0);
1477 int dy = (up ? -1 : down ? 1 : 0);
1479 switch (game_status)
1481 case GAME_MODE_TITLE:
1482 case GAME_MODE_MAIN:
1483 case GAME_MODE_LEVELS:
1484 case GAME_MODE_LEVELNR:
1485 case GAME_MODE_SETUP:
1486 case GAME_MODE_INFO:
1488 static unsigned int joystickmove_delay = 0;
1490 if (joystick && !button &&
1491 !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
1492 newbutton = dx = dy = 0;
1494 if (game_status == GAME_MODE_TITLE)
1495 HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1496 else if (game_status == GAME_MODE_MAIN)
1497 HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1498 else if (game_status == GAME_MODE_LEVELS)
1499 HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
1500 else if (game_status == GAME_MODE_LEVELNR)
1501 HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
1502 else if (game_status == GAME_MODE_SETUP)
1503 HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1504 else if (game_status == GAME_MODE_INFO)
1505 HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1509 case GAME_MODE_SCORES:
1510 HandleHallOfFame(0, 0, dx, dy, !newbutton);
1513 case GAME_MODE_EDITOR:
1514 HandleLevelEditorIdle();
1517 case GAME_MODE_PLAYING:
1518 if (tape.playing || keyboard)
1519 newbutton = ((joy & JOY_BUTTON) != 0);
1522 if (local_player->LevelSolved_GameEnd && newbutton)
1524 if (AllPlayersGone && newbutton)