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)
406 int subtype = event->event;
409 (subtype == SDL_WINDOWEVENT_SHOWN ? "SDL_WINDOWEVENT_SHOWN" :
410 subtype == SDL_WINDOWEVENT_HIDDEN ? "SDL_WINDOWEVENT_HIDDEN" :
411 subtype == SDL_WINDOWEVENT_EXPOSED ? "SDL_WINDOWEVENT_EXPOSED" :
412 subtype == SDL_WINDOWEVENT_MOVED ? "SDL_WINDOWEVENT_MOVED" :
413 subtype == SDL_WINDOWEVENT_SIZE_CHANGED ? "SDL_WINDOWEVENT_SIZE_CHANGED" :
414 subtype == SDL_WINDOWEVENT_RESIZED ? "SDL_WINDOWEVENT_RESIZED" :
415 subtype == SDL_WINDOWEVENT_MINIMIZED ? "SDL_WINDOWEVENT_MINIMIZED" :
416 subtype == SDL_WINDOWEVENT_MAXIMIZED ? "SDL_WINDOWEVENT_MAXIMIZED" :
417 subtype == SDL_WINDOWEVENT_RESTORED ? "SDL_WINDOWEVENT_RESTORED" :
418 subtype == SDL_WINDOWEVENT_ENTER ? "SDL_WINDOWEVENT_ENTER" :
419 subtype == SDL_WINDOWEVENT_LEAVE ? "SDL_WINDOWEVENT_LEAVE" :
420 subtype == SDL_WINDOWEVENT_FOCUS_GAINED ? "SDL_WINDOWEVENT_FOCUS_GAINED" :
421 subtype == SDL_WINDOWEVENT_FOCUS_LOST ? "SDL_WINDOWEVENT_FOCUS_LOST" :
422 subtype == SDL_WINDOWEVENT_CLOSE ? "SDL_WINDOWEVENT_CLOSE" :
425 Error(ERR_DEBUG, "WINDOW EVENT: '%s', %ld, %ld",
426 event_name, event->data1, event->data2);
428 if (event->event == SDL_WINDOWEVENT_EXPOSED)
432 if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED)
434 // if game started in fullscreen mode, window will also get fullscreen size
435 if (!video.fullscreen_enabled && video.fullscreen_initial)
437 SDLSetWindowScaling(setup.window_scaling_percent);
439 // only do this correction once
440 video.fullscreen_initial = FALSE;
445 if (event->event == SDL_WINDOWEVENT_RESIZED && !video.fullscreen_enabled)
448 int new_window_width = event->data1;
449 int new_window_height = event->data2;
451 printf("::: RESIZED from %d, %d to %d, %d\n",
452 video.window_width, video.window_height,
453 new_window_width, new_window_height);
455 // if window size has changed after resizing, calculate new scaling factor
456 if (new_window_width != video.window_width ||
457 new_window_height != video.window_height)
459 int new_xpercent = (100 * new_window_width / video.width);
460 int new_ypercent = (100 * new_window_height / video.height);
462 setup.window_scaling_percent = video.window_scaling_percent =
463 MIN(MAX(MIN_WINDOW_SCALING_PERCENT, MIN(new_xpercent, new_ypercent)),
464 MAX_WINDOW_SCALING_PERCENT);
466 video.window_width = new_window_width;
467 video.window_height = new_window_height;
469 printf("::: setup.window_scaling_percent set to %d\n",
470 setup.window_scaling_percent);
473 // prevent slightly wrong scaling factor due to rounding differences
474 float scaling_factor = (float)setup.window_scaling_percent / 100;
475 int old_xsize = (int)(scaling_factor * video.width);
476 int old_ysize = (int)(scaling_factor * video.height);
477 int new_xsize = event->data1;
478 int new_ysize = event->data2;
480 // window size is unchanged when going from fullscreen to window mode,
481 // but reverse calculation of scaling factor might result in a scaling
482 // factor that is slightly different due to rounding differences;
483 // therefore compare old/new window size and not old/new scaling factor
484 if (old_xsize != new_xsize ||
485 old_ysize != new_ysize)
487 int new_xpercent = (100 * new_xsize / video.width);
488 int new_ypercent = (100 * new_ysize / video.height);
490 setup.window_scaling_percent = MIN(new_xpercent, new_ypercent);
492 if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
493 setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
494 else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
495 setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
497 printf("::: setup.window_scaling_percent set to %d\n",
498 setup.window_scaling_percent);
504 void HandleFingerEvent(FingerEvent *event)
507 static int num_events = 0;
512 Error(ERR_DEBUG, "FINGER EVENT: finger was %s, touch ID %lld, finger ID %lld, x/y %f/%f, dx/dy %f/%f, pressure %f",
513 event->type == EVENT_FINGERPRESS ? "pressed" :
514 event->type == EVENT_FINGERRELEASE ? "released" : "moved",
518 event->dx, event->dy,
523 int x = (int)(event->x * video.width);
524 int y = (int)(event->y * video.height);
525 int button = MB_LEFTBUTTON;
527 Error(ERR_DEBUG, "=> screen x/y %d/%d", x, y);
531 if (++num_events >= max_events)
537 if (event->type == EVENT_FINGERPRESS ||
538 event->type == EVENT_FINGERMOTION)
539 button_status = button;
541 button_status = MB_RELEASED;
543 int max_x = SX + SXSIZE;
544 int max_y = SY + SYSIZE;
548 if (game_status == GAME_MODE_PLAYING)
550 if (game_status == GAME_MODE_PLAYING &&
554 int key_status = (event->type == EVENT_FINGERRELEASE ? KEY_RELEASED :
557 Key key = (event->y < 1.0 / 3.0 ? setup.input[0].key.up :
558 event->y > 2.0 / 3.0 ? setup.input[0].key.down :
559 event->x < 1.0 / 3.0 ? setup.input[0].key.left :
560 event->x > 2.0 / 3.0 ? setup.input[0].key.right :
561 setup.input[0].key.drop);
563 Key key = (y < max_y / 3 ? setup.input[0].key.up :
564 y > 2 * max_y / 3 ? setup.input[0].key.down :
565 x < max_x / 3 ? setup.input[0].key.left :
566 x > 2 * max_x / 3 ? setup.input[0].key.right :
567 setup.input[0].key.drop);
570 Error(ERR_DEBUG, "=> key == %d, key_status == %d", key, key_status);
572 HandleKey(key, key_status);
577 Error(ERR_DEBUG, "::: button_status == %d, button == %d\n",
578 button_status, button);
580 HandleButton(x, y, button_status, button);
586 static boolean checkTextInputKeyModState()
588 // when playing, only handle raw key events and ignore text input
589 if (game_status == GAME_MODE_PLAYING)
592 return ((GetKeyModState() & KMOD_TextInput) != KMOD_None);
595 void HandleTextEvent(TextEvent *event)
597 char *text = event->text;
598 Key key = getKeyFromKeyName(text);
601 Error(ERR_DEBUG, "TEXT EVENT: text == '%s' [%d byte(s), '%c'/%d], resulting key == %d (%s)",
604 text[0], (int)(text[0]),
606 getKeyNameFromKey(key));
609 // if (game_status != GAME_MODE_PLAYING && GetKeyModState() != KMOD_None)
611 if (game_status != GAME_MODE_PLAYING &&
612 (GetKeyModState() & KMOD_TextInput) != KMOD_None)
614 if (checkTextInputKeyModState())
616 HandleKey(key, KEY_PRESSED);
617 HandleKey(key, KEY_RELEASED);
622 void HandleKeyEvent(KeyEvent *event)
624 int key_status = (event->type == EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
625 boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
626 Key key = GetEventKey(event, with_modifiers);
627 Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
630 Error(ERR_DEBUG, "KEY EVENT: key was %s, keysym.scancode == %d, keysym.sym == %d, keymod = %d, GetKeyModState() = 0x%04x, resulting key == %d (%s)",
631 event->type == EVENT_KEYPRESS ? "pressed" : "released",
632 event->keysym.scancode,
637 getKeyNameFromKey(key));
641 if (key == KSYM_Menu)
642 Error(ERR_DEBUG, "menu key pressed");
643 else if (key == KSYM_Back)
644 Error(ERR_DEBUG, "back key pressed");
647 #if defined(PLATFORM_ANDROID)
648 // always map the "back" button to the "escape" key on Android devices
649 if (key == KSYM_Back)
653 HandleKeyModState(keymod, key_status);
655 #if defined(TARGET_SDL2)
657 // if (game_status == GAME_MODE_PLAYING || GetKeyModState() == KMOD_None)
659 if (game_status == GAME_MODE_PLAYING ||
660 (GetKeyModState() & KMOD_TextInput) == KMOD_None)
662 if (!checkTextInputKeyModState())
663 HandleKey(key, key_status);
665 HandleKey(key, key_status);
669 void HandleFocusEvent(FocusChangeEvent *event)
671 static int old_joystick_status = -1;
673 if (event->type == EVENT_FOCUSOUT)
675 KeyboardAutoRepeatOn();
676 old_joystick_status = joystick.status;
677 joystick.status = JOYSTICK_NOT_AVAILABLE;
681 else if (event->type == EVENT_FOCUSIN)
683 /* When there are two Rocks'n'Diamonds windows which overlap and
684 the player moves the pointer from one game window to the other,
685 a 'FocusOut' event is generated for the window the pointer is
686 leaving and a 'FocusIn' event is generated for the window the
687 pointer is entering. In some cases, it can happen that the
688 'FocusIn' event is handled by the one game process before the
689 'FocusOut' event by the other game process. In this case the
690 X11 environment would end up with activated keyboard auto repeat,
691 because unfortunately this is a global setting and not (which
692 would be far better) set for each X11 window individually.
693 The effect would be keyboard auto repeat while playing the game
694 (game_status == GAME_MODE_PLAYING), which is not desired.
695 To avoid this special case, we just wait 1/10 second before
696 processing the 'FocusIn' event.
699 if (game_status == GAME_MODE_PLAYING)
702 KeyboardAutoRepeatOffUnlessAutoplay();
705 if (old_joystick_status != -1)
706 joystick.status = old_joystick_status;
710 void HandleClientMessageEvent(ClientMessageEvent *event)
712 if (CheckCloseWindowEvent(event))
716 void HandleWindowManagerEvent(Event *event)
718 #if defined(TARGET_SDL)
719 SDLHandleWindowManagerEvent(event);
723 void HandleButton(int mx, int my, int button, int button_nr)
725 static int old_mx = 0, old_my = 0;
739 if (HandleGadgets(mx, my, button))
741 /* do not handle this button event anymore */
742 mx = my = -32; /* force mouse event to be outside screen tiles */
745 /* do not use scroll wheel button events for anything other than gadgets */
746 if (IS_WHEEL_BUTTON(button_nr))
750 Error(ERR_DEBUG, "::: game_status == %d", game_status);
755 case GAME_MODE_TITLE:
756 HandleTitleScreen(mx, my, 0, 0, button);
760 HandleMainMenu(mx, my, 0, 0, button);
763 case GAME_MODE_PSEUDO_TYPENAME:
764 HandleTypeName(0, KSYM_Return);
767 case GAME_MODE_LEVELS:
768 HandleChooseLevelSet(mx, my, 0, 0, button);
771 case GAME_MODE_LEVELNR:
772 HandleChooseLevelNr(mx, my, 0, 0, button);
775 case GAME_MODE_SCORES:
776 HandleHallOfFame(0, 0, 0, 0, button);
779 case GAME_MODE_EDITOR:
780 HandleLevelEditorIdle();
784 HandleInfoScreen(mx, my, 0, 0, button);
787 case GAME_MODE_SETUP:
788 HandleSetupScreen(mx, my, 0, 0, button);
791 case GAME_MODE_PLAYING:
793 if (button == MB_PRESSED && !motion_status && IN_GFX_SCREEN(mx, my))
794 DumpTile(LEVELX((mx - SX) / TILEX), LEVELY((my - SY) / TILEY));
803 static boolean is_string_suffix(char *string, char *suffix)
805 int string_len = strlen(string);
806 int suffix_len = strlen(suffix);
808 if (suffix_len > string_len)
811 return (strEqual(&string[string_len - suffix_len], suffix));
814 #define MAX_CHEAT_INPUT_LEN 32
816 static void HandleKeysSpecial(Key key)
818 static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
819 char letter = getCharFromKey(key);
820 int cheat_input_len = strlen(cheat_input);
826 if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
828 for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
829 cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
831 cheat_input_len = MAX_CHEAT_INPUT_LEN;
834 cheat_input[cheat_input_len++] = letter;
835 cheat_input[cheat_input_len] = '\0';
838 Error(ERR_DEBUG, "SPECIAL KEY '%s' [%d]\n", cheat_input, cheat_input_len);
841 if (game_status == GAME_MODE_MAIN)
843 if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
844 is_string_suffix(cheat_input, ":ist"))
846 InsertSolutionTape();
848 else if (is_string_suffix(cheat_input, ":reload-graphics") ||
849 is_string_suffix(cheat_input, ":rg"))
851 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
854 else if (is_string_suffix(cheat_input, ":reload-sounds") ||
855 is_string_suffix(cheat_input, ":rs"))
857 ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
860 else if (is_string_suffix(cheat_input, ":reload-music") ||
861 is_string_suffix(cheat_input, ":rm"))
863 ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
866 else if (is_string_suffix(cheat_input, ":reload-artwork") ||
867 is_string_suffix(cheat_input, ":ra"))
869 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
870 1 << ARTWORK_TYPE_SOUNDS |
871 1 << ARTWORK_TYPE_MUSIC);
874 else if (is_string_suffix(cheat_input, ":dump-level") ||
875 is_string_suffix(cheat_input, ":dl"))
879 else if (is_string_suffix(cheat_input, ":dump-tape") ||
880 is_string_suffix(cheat_input, ":dt"))
884 else if (is_string_suffix(cheat_input, ":save-native-level") ||
885 is_string_suffix(cheat_input, ":snl"))
887 SaveNativeLevel(&level);
890 else if (game_status == GAME_MODE_PLAYING)
893 if (is_string_suffix(cheat_input, ".q"))
894 DEBUG_SetMaximumDynamite();
897 else if (game_status == GAME_MODE_EDITOR)
899 if (is_string_suffix(cheat_input, ":dump-brush") ||
900 is_string_suffix(cheat_input, ":DB"))
904 else if (is_string_suffix(cheat_input, ":DDB"))
911 void HandleKey(Key key, int key_status)
913 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
914 static struct SetupKeyboardInfo ski;
915 static struct SetupShortcutInfo ssi;
924 { &ski.left, &ssi.snap_left, DEFAULT_KEY_LEFT, JOY_LEFT },
925 { &ski.right, &ssi.snap_right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
926 { &ski.up, &ssi.snap_up, DEFAULT_KEY_UP, JOY_UP },
927 { &ski.down, &ssi.snap_down, DEFAULT_KEY_DOWN, JOY_DOWN },
928 { &ski.snap, NULL, DEFAULT_KEY_SNAP, JOY_BUTTON_SNAP },
929 { &ski.drop, NULL, DEFAULT_KEY_DROP, JOY_BUTTON_DROP }
934 if (game_status == GAME_MODE_PLAYING)
936 /* only needed for single-step tape recording mode */
937 static boolean clear_snap_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
938 static boolean clear_drop_button[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
939 static boolean element_snapped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
940 static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
943 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
947 if (setup.input[pnr].use_joystick)
950 ski = setup.input[pnr].key;
952 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
953 if (key == *key_info[i].key_custom)
954 key_action |= key_info[i].action;
956 /* use combined snap+direction keys for the first player only */
959 ssi = setup.shortcut;
961 for (i = 0; i < NUM_DIRECTIONS; i++)
962 if (key == *key_info[i].key_snap)
963 key_action |= key_info[i].action | JOY_BUTTON_SNAP;
966 /* clear delayed snap and drop actions in single step mode (see below) */
967 if (tape.single_step)
969 if (clear_snap_button[pnr])
971 stored_player[pnr].action &= ~KEY_BUTTON_SNAP;
972 clear_snap_button[pnr] = FALSE;
975 if (clear_drop_button[pnr])
977 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
978 clear_drop_button[pnr] = FALSE;
982 if (key_status == KEY_PRESSED)
983 stored_player[pnr].action |= key_action;
985 stored_player[pnr].action &= ~key_action;
987 if (tape.single_step && tape.recording && tape.pausing)
989 if (key_status == KEY_PRESSED && key_action & KEY_MOTION)
991 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
993 /* if snap key already pressed, don't snap when releasing (below) */
994 if (stored_player[pnr].action & KEY_BUTTON_SNAP)
995 element_snapped[pnr] = TRUE;
997 /* if drop key already pressed, don't drop when releasing (below) */
998 if (stored_player[pnr].action & KEY_BUTTON_DROP)
999 element_dropped[pnr] = TRUE;
1002 else if (key_status == KEY_PRESSED && key_action & KEY_BUTTON_DROP)
1004 if (level.game_engine_type == GAME_ENGINE_TYPE_EM ||
1005 level.game_engine_type == GAME_ENGINE_TYPE_SP)
1008 printf("::: drop key pressed\n");
1011 if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
1012 getRedDiskReleaseFlag_SP() == 0)
1013 stored_player[pnr].action &= ~KEY_BUTTON_DROP;
1015 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1019 else if (key_status == KEY_RELEASED && key_action & KEY_BUTTON)
1021 if (key_action & KEY_BUTTON_SNAP)
1023 /* if snap key was released without moving (see above), snap now */
1024 if (!element_snapped[pnr])
1026 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1028 stored_player[pnr].action |= KEY_BUTTON_SNAP;
1030 /* clear delayed snap button on next event */
1031 clear_snap_button[pnr] = TRUE;
1034 element_snapped[pnr] = FALSE;
1038 if (key_action & KEY_BUTTON_DROP &&
1039 level.game_engine_type == GAME_ENGINE_TYPE_RND)
1041 /* if drop key was released without moving (see above), drop now */
1042 if (!element_dropped[pnr])
1044 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
1046 if (level.game_engine_type != GAME_ENGINE_TYPE_SP ||
1047 getRedDiskReleaseFlag_SP() != 0)
1048 stored_player[pnr].action |= KEY_BUTTON_DROP;
1050 /* clear delayed drop button on next event */
1051 clear_drop_button[pnr] = TRUE;
1054 element_dropped[pnr] = FALSE;
1059 else if (tape.recording && tape.pausing)
1061 /* prevent key release events from un-pausing a paused game */
1062 if (key_status == KEY_PRESSED && key_action & KEY_ACTION)
1063 TapeTogglePause(TAPE_TOGGLE_MANUAL);
1069 for (i = 0; i < NUM_PLAYER_ACTIONS; i++)
1070 if (key == key_info[i].key_default)
1071 joy |= key_info[i].action;
1076 if (key_status == KEY_PRESSED)
1077 key_joystick_mapping |= joy;
1079 key_joystick_mapping &= ~joy;
1084 if (game_status != GAME_MODE_PLAYING)
1085 key_joystick_mapping = 0;
1087 if (key_status == KEY_RELEASED)
1090 if ((key == KSYM_Return || key == KSYM_KP_Enter) &&
1091 (GetKeyModState() & KMOD_Alt) && video.fullscreen_available)
1093 setup.fullscreen = !setup.fullscreen;
1095 printf("::: %d\n", setup.window_scaling_percent);
1097 ToggleFullscreenIfNeeded();
1099 if (game_status == GAME_MODE_SETUP)
1100 RedrawSetupScreenAfterFullscreenToggle();
1105 if ((key == KSYM_minus || key == KSYM_plus || key == KSYM_0) &&
1106 (GetKeyModState() & KMOD_Control) && video.window_scaling_available &&
1107 !video.fullscreen_enabled)
1110 setup.window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
1112 setup.window_scaling_percent +=
1113 (key == KSYM_minus ? -1 : +1) * STEP_WINDOW_SCALING_PERCENT;
1115 if (setup.window_scaling_percent < MIN_WINDOW_SCALING_PERCENT)
1116 setup.window_scaling_percent = MIN_WINDOW_SCALING_PERCENT;
1117 else if (setup.window_scaling_percent > MAX_WINDOW_SCALING_PERCENT)
1118 setup.window_scaling_percent = MAX_WINDOW_SCALING_PERCENT;
1120 ToggleFullscreenIfNeeded();
1122 if (game_status == GAME_MODE_SETUP)
1123 RedrawSetupScreenAfterFullscreenToggle();
1129 if (game_status == GAME_MODE_PLAYING && local_player->LevelSolved_GameEnd &&
1130 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1132 if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
1133 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
1141 if (game_status == GAME_MODE_MAIN &&
1142 (key == setup.shortcut.toggle_pause || key == KSYM_space))
1144 StartGameActions(options.network, setup.autorecord, level.random_seed);
1149 if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
1151 if (key == setup.shortcut.save_game)
1153 else if (key == setup.shortcut.load_game)
1155 else if (key == setup.shortcut.toggle_pause)
1156 TapeTogglePause(TAPE_TOGGLE_MANUAL);
1158 HandleTapeButtonKeys(key);
1159 HandleSoundButtonKeys(key);
1162 if (game_status == GAME_MODE_PLAYING && !network_playing)
1164 int centered_player_nr_next = -999;
1166 if (key == setup.shortcut.focus_player_all)
1167 centered_player_nr_next = -1;
1169 for (i = 0; i < MAX_PLAYERS; i++)
1170 if (key == setup.shortcut.focus_player[i])
1171 centered_player_nr_next = i;
1173 if (centered_player_nr_next != -999)
1175 game.centered_player_nr_next = centered_player_nr_next;
1176 game.set_centered_player = TRUE;
1180 tape.centered_player_nr_next = game.centered_player_nr_next;
1181 tape.set_centered_player = TRUE;
1186 HandleKeysSpecial(key);
1188 if (HandleGadgetsKeyInput(key))
1190 if (key != KSYM_Escape) /* always allow ESC key to be handled */
1191 key = KSYM_UNDEFINED;
1194 switch (game_status)
1196 case GAME_MODE_PSEUDO_TYPENAME:
1197 HandleTypeName(0, key);
1200 case GAME_MODE_TITLE:
1201 case GAME_MODE_MAIN:
1202 case GAME_MODE_LEVELS:
1203 case GAME_MODE_LEVELNR:
1204 case GAME_MODE_SETUP:
1205 case GAME_MODE_INFO:
1206 case GAME_MODE_SCORES:
1211 if (game_status == GAME_MODE_TITLE)
1212 HandleTitleScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1213 else if (game_status == GAME_MODE_MAIN)
1214 HandleMainMenu(0, 0, 0, 0, MB_MENU_CHOICE);
1215 else if (game_status == GAME_MODE_LEVELS)
1216 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_CHOICE);
1217 else if (game_status == GAME_MODE_LEVELNR)
1218 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_CHOICE);
1219 else if (game_status == GAME_MODE_SETUP)
1220 HandleSetupScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1221 else if (game_status == GAME_MODE_INFO)
1222 HandleInfoScreen(0, 0, 0, 0, MB_MENU_CHOICE);
1223 else if (game_status == GAME_MODE_SCORES)
1224 HandleHallOfFame(0, 0, 0, 0, MB_MENU_CHOICE);
1228 if (game_status != GAME_MODE_MAIN)
1229 FadeSkipNextFadeIn();
1231 if (game_status == GAME_MODE_TITLE)
1232 HandleTitleScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1233 else if (game_status == GAME_MODE_LEVELS)
1234 HandleChooseLevelSet(0, 0, 0, 0, MB_MENU_LEAVE);
1235 else if (game_status == GAME_MODE_LEVELNR)
1236 HandleChooseLevelNr(0, 0, 0, 0, MB_MENU_LEAVE);
1237 else if (game_status == GAME_MODE_SETUP)
1238 HandleSetupScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1239 else if (game_status == GAME_MODE_INFO)
1240 HandleInfoScreen(0, 0, 0, 0, MB_MENU_LEAVE);
1241 else if (game_status == GAME_MODE_SCORES)
1242 HandleHallOfFame(0, 0, 0, 0, MB_MENU_LEAVE);
1246 if (game_status == GAME_MODE_LEVELS)
1247 HandleChooseLevelSet(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1248 else if (game_status == GAME_MODE_LEVELNR)
1249 HandleChooseLevelNr(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1250 else if (game_status == GAME_MODE_SETUP)
1251 HandleSetupScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1252 else if (game_status == GAME_MODE_INFO)
1253 HandleInfoScreen(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1254 else if (game_status == GAME_MODE_SCORES)
1255 HandleHallOfFame(0, 0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
1258 case KSYM_Page_Down:
1259 if (game_status == GAME_MODE_LEVELS)
1260 HandleChooseLevelSet(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1261 else if (game_status == GAME_MODE_LEVELNR)
1262 HandleChooseLevelNr(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1263 else if (game_status == GAME_MODE_SETUP)
1264 HandleSetupScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1265 else if (game_status == GAME_MODE_INFO)
1266 HandleInfoScreen(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1267 else if (game_status == GAME_MODE_SCORES)
1268 HandleHallOfFame(0, 0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
1273 GameFrameDelay = (GameFrameDelay == 500 ? GAME_FRAME_DELAY : 500);
1277 setup.sp_show_border_elements = !setup.sp_show_border_elements;
1278 printf("Supaplex border elements %s\n",
1279 setup.sp_show_border_elements ? "enabled" : "disabled");
1288 case GAME_MODE_EDITOR:
1289 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
1290 HandleLevelEditorKeyInput(key);
1293 case GAME_MODE_PLAYING:
1298 RequestQuitGame(setup.ask_on_escape);
1316 if (GameFrameDelay == 500)
1317 GameFrameDelay = GAME_FRAME_DELAY;
1319 GameFrameDelay = 500;
1322 GameFrameDelay = (key - KSYM_0) * 10;
1323 printf("Game speed == %d%% (%d ms delay between two frames)\n",
1324 GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
1330 options.debug = FALSE;
1331 printf("debug mode disabled\n");
1335 options.debug = TRUE;
1336 printf("debug mode enabled\n");
1341 if (!global.fps_slowdown)
1343 global.fps_slowdown = TRUE;
1344 global.fps_slowdown_factor = 2;
1345 printf("fps slowdown enabled -- display only every 2nd frame\n");
1347 else if (global.fps_slowdown_factor == 2)
1349 global.fps_slowdown_factor = 4;
1350 printf("fps slowdown enabled -- display only every 4th frame\n");
1354 global.fps_slowdown = FALSE;
1355 global.fps_slowdown_factor = 1;
1356 printf("fps slowdown disabled\n");
1361 ScrollStepSize = TILEX / 8;
1362 printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
1366 ScrollStepSize = TILEX / 4;
1367 printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
1371 ScrollStepSize = TILEX / 2;
1372 printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
1376 ScrollStepSize = TILEX;
1377 printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
1381 printf("::: currently using game engine version %d\n",
1382 game.engine_version);
1393 if (key == KSYM_Escape)
1395 game_status = GAME_MODE_MAIN;
1403 void HandleNoEvent()
1405 if (button_status && game_status != GAME_MODE_PLAYING)
1407 HandleButton(0, 0, -button_status, button_status);
1412 #if defined(NETWORK_AVALIABLE)
1413 if (options.network)
1420 static int HandleJoystickForAllPlayers()
1425 for (i = 0; i < MAX_PLAYERS; i++)
1427 byte joy_action = 0;
1430 if (!setup.input[i].use_joystick)
1434 joy_action = Joystick(i);
1435 result |= joy_action;
1437 if (!setup.input[i].use_joystick)
1440 stored_player[i].action = joy_action;
1446 void HandleJoystick()
1448 int joystick = HandleJoystickForAllPlayers();
1449 int keyboard = key_joystick_mapping;
1450 int joy = (joystick | keyboard);
1451 int left = joy & JOY_LEFT;
1452 int right = joy & JOY_RIGHT;
1453 int up = joy & JOY_UP;
1454 int down = joy & JOY_DOWN;
1455 int button = joy & JOY_BUTTON;
1456 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
1457 int dx = (left ? -1 : right ? 1 : 0);
1458 int dy = (up ? -1 : down ? 1 : 0);
1460 switch (game_status)
1462 case GAME_MODE_TITLE:
1463 case GAME_MODE_MAIN:
1464 case GAME_MODE_LEVELS:
1465 case GAME_MODE_LEVELNR:
1466 case GAME_MODE_SETUP:
1467 case GAME_MODE_INFO:
1469 static unsigned int joystickmove_delay = 0;
1471 if (joystick && !button &&
1472 !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
1473 newbutton = dx = dy = 0;
1475 if (game_status == GAME_MODE_TITLE)
1476 HandleTitleScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1477 else if (game_status == GAME_MODE_MAIN)
1478 HandleMainMenu(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1479 else if (game_status == GAME_MODE_LEVELS)
1480 HandleChooseLevelSet(0,0,dx,dy,newbutton?MB_MENU_CHOICE : MB_MENU_MARK);
1481 else if (game_status == GAME_MODE_LEVELNR)
1482 HandleChooseLevelNr(0,0,dx,dy,newbutton? MB_MENU_CHOICE : MB_MENU_MARK);
1483 else if (game_status == GAME_MODE_SETUP)
1484 HandleSetupScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1485 else if (game_status == GAME_MODE_INFO)
1486 HandleInfoScreen(0,0,dx,dy, newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1490 case GAME_MODE_SCORES:
1491 HandleHallOfFame(0, 0, dx, dy, !newbutton);
1494 case GAME_MODE_EDITOR:
1495 HandleLevelEditorIdle();
1498 case GAME_MODE_PLAYING:
1499 if (tape.playing || keyboard)
1500 newbutton = ((joy & JOY_BUTTON) != 0);
1503 if (local_player->LevelSolved_GameEnd && newbutton)
1505 if (AllPlayersGone && newbutton)