1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
27 static boolean cursor_inside_playfield = FALSE;
28 static boolean playfield_cursor_set = FALSE;
29 static unsigned long playfield_cursor_delay = 0;
32 /* event filter especially needed for SDL event filtering due to
33 delay problems with lots of mouse motion events when mouse button
34 not pressed (X11 can handle this with 'PointerMotionHintMask') */
36 int FilterMouseMotionEvents(const Event *event)
40 /* non-motion events are directly passed to event handler functions */
41 if (event->type != EVENT_MOTIONNOTIFY)
44 motion = (MotionEvent *)event;
45 cursor_inside_playfield = (motion->x >= SX && motion->x < SX + SXSIZE &&
46 motion->y >= SY && motion->y < SY + SYSIZE);
48 if (game_status == GAME_MODE_PLAYING && playfield_cursor_set)
50 SetMouseCursor(CURSOR_DEFAULT);
51 playfield_cursor_set = FALSE;
52 DelayReached(&playfield_cursor_delay, 0);
55 /* skip mouse motion events without pressed button outside level editor */
56 if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR &&
57 game_status != GAME_MODE_PLAYING)
63 /* this is only really needed for non-SDL targets to filter unwanted events;
64 when using SDL with properly installed event filter, this function can be
65 replaced with a simple "NextEvent()" call, but it doesn't hurt either */
67 static boolean NextValidEvent(Event *event)
69 while (PendingEvent())
73 if (FilterMouseMotionEvents(event))
84 if (PendingEvent()) /* got event */
88 if (NextValidEvent(&event))
92 case EVENT_BUTTONPRESS:
93 case EVENT_BUTTONRELEASE:
94 HandleButtonEvent((ButtonEvent *) &event);
97 case EVENT_MOTIONNOTIFY:
98 HandleMotionEvent((MotionEvent *) &event);
102 case EVENT_KEYRELEASE:
103 HandleKeyEvent((KeyEvent *) &event);
107 HandleOtherEvents(&event);
114 /* when playing, display a special mouse pointer inside the playfield */
115 if (game_status == GAME_MODE_PLAYING)
117 if (!playfield_cursor_set && cursor_inside_playfield &&
118 DelayReached(&playfield_cursor_delay, 1000))
120 SetMouseCursor(CURSOR_PLAYFIELD);
121 playfield_cursor_set = TRUE;
124 else if (playfield_cursor_set)
126 SetMouseCursor(CURSOR_DEFAULT);
127 playfield_cursor_set = FALSE;
133 /* don't use all CPU time when idle; the main loop while playing
134 has its own synchronization and is CPU friendly, too */
136 if (game_status == GAME_MODE_PLAYING)
141 if (!PendingEvent()) /* delay only if no pending events */
145 /* refresh window contents from drawing buffer, if needed */
148 if (game_status == GAME_MODE_QUIT)
153 void HandleOtherEvents(Event *event)
158 HandleExposeEvent((ExposeEvent *) event);
161 case EVENT_UNMAPNOTIFY:
163 /* This causes the game to stop not only when iconified, but also
164 when on another virtual desktop, which might be not desired. */
165 SleepWhileUnmapped();
171 HandleFocusEvent((FocusChangeEvent *) event);
174 case EVENT_CLIENTMESSAGE:
175 HandleClientMessageEvent((ClientMessageEvent *) event);
178 #if defined(TARGET_SDL)
179 case SDL_JOYAXISMOTION:
180 case SDL_JOYBUTTONDOWN:
181 case SDL_JOYBUTTONUP:
182 HandleJoystickEvent(event);
191 void ClearEventQueue()
193 while (PendingEvent())
201 case EVENT_BUTTONRELEASE:
202 button_status = MB_RELEASED;
205 case EVENT_KEYRELEASE:
206 key_joystick_mapping = 0;
210 HandleOtherEvents(&event);
216 void ClearPlayerAction()
220 /* simulate key release events for still pressed keys */
221 key_joystick_mapping = 0;
222 for (i = 0; i < MAX_PLAYERS; i++)
223 stored_player[i].action = 0;
226 void SleepWhileUnmapped()
228 boolean window_unmapped = TRUE;
230 KeyboardAutoRepeatOn();
232 while (window_unmapped)
240 case EVENT_BUTTONRELEASE:
241 button_status = MB_RELEASED;
244 case EVENT_KEYRELEASE:
245 key_joystick_mapping = 0;
248 case EVENT_MAPNOTIFY:
249 window_unmapped = FALSE;
252 case EVENT_UNMAPNOTIFY:
253 /* this is only to surely prevent the 'should not happen' case
254 * of recursively looping between 'SleepWhileUnmapped()' and
255 * 'HandleOtherEvents()' which usually calls this funtion.
260 HandleOtherEvents(&event);
265 if (game_status == GAME_MODE_PLAYING)
266 KeyboardAutoRepeatOffUnlessAutoplay();
269 void HandleExposeEvent(ExposeEvent *event)
272 RedrawPlayfield(FALSE, event->x, event->y, event->width, event->height);
277 void HandleButtonEvent(ButtonEvent *event)
279 motion_status = FALSE;
281 if (event->type == EVENT_BUTTONPRESS)
282 button_status = event->button;
284 button_status = MB_RELEASED;
286 HandleButton(event->x, event->y, button_status);
289 void HandleMotionEvent(MotionEvent *event)
291 if (!PointerInWindow(window))
292 return; /* window and pointer are on different screens */
295 if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR)
299 motion_status = TRUE;
301 HandleButton(event->x, event->y, button_status);
304 void HandleKeyEvent(KeyEvent *event)
306 int key_status = (event->type==EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
307 boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
308 Key key = GetEventKey(event, with_modifiers);
309 Key keymod = (with_modifiers ? GetEventKey(event, FALSE) : key);
311 HandleKeyModState(keymod, key_status);
312 HandleKey(key, key_status);
315 void HandleFocusEvent(FocusChangeEvent *event)
317 static int old_joystick_status = -1;
319 if (event->type == EVENT_FOCUSOUT)
321 KeyboardAutoRepeatOn();
322 old_joystick_status = joystick.status;
323 joystick.status = JOYSTICK_NOT_AVAILABLE;
327 else if (event->type == EVENT_FOCUSIN)
329 /* When there are two Rocks'n'Diamonds windows which overlap and
330 the player moves the pointer from one game window to the other,
331 a 'FocusOut' event is generated for the window the pointer is
332 leaving and a 'FocusIn' event is generated for the window the
333 pointer is entering. In some cases, it can happen that the
334 'FocusIn' event is handled by the one game process before the
335 'FocusOut' event by the other game process. In this case the
336 X11 environment would end up with activated keyboard auto repeat,
337 because unfortunately this is a global setting and not (which
338 would be far better) set for each X11 window individually.
339 The effect would be keyboard auto repeat while playing the game
340 (game_status == GAME_MODE_PLAYING), which is not desired.
341 To avoid this special case, we just wait 1/10 second before
342 processing the 'FocusIn' event.
345 if (game_status == GAME_MODE_PLAYING)
348 KeyboardAutoRepeatOffUnlessAutoplay();
350 if (old_joystick_status != -1)
351 joystick.status = old_joystick_status;
355 void HandleClientMessageEvent(ClientMessageEvent *event)
357 if (CheckCloseWindowEvent(event))
361 void HandleButton(int mx, int my, int button)
363 static int old_mx = 0, old_my = 0;
377 if (HandleGadgets(mx, my, button))
379 /* do not handle this button event anymore */
380 mx = my = -32; /* force mouse event to be outside screen tiles */
386 HandleMainMenu(mx,my, 0,0, button);
389 case GAME_MODE_PSEUDO_TYPENAME:
390 HandleTypeName(0, KSYM_Return);
393 case GAME_MODE_LEVELS:
394 HandleChooseLevel(mx,my, 0,0, button);
397 case GAME_MODE_SCORES:
398 HandleHallOfFame(0,0, 0,0, button);
401 case GAME_MODE_EDITOR:
405 HandleInfoScreen(mx,my, 0,0, button);
408 case GAME_MODE_SETUP:
409 HandleSetupScreen(mx,my, 0,0, button);
412 case GAME_MODE_PLAYING:
414 if (button == MB_RELEASED)
416 if (IN_GFX_SCREEN(mx, my))
418 int sx = (mx - SX) / TILEX;
419 int sy = (my - SY) / TILEY;
423 printf("INFO: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
425 if (!IN_LEV_FIELD(x, y))
428 printf(" Feld[%d][%d] == %d ('%s')\n", x,y, Feld[x][y],
429 element_info[Feld[x][y]].token_name);
430 printf(" Back[%d][%d] == %d\n", x,y, Back[x][y]);
431 printf(" Store[%d][%d] == %d\n", x,y, Store[x][y]);
432 printf(" Store2[%d][%d] == %d\n", x,y, Store2[x][y]);
433 printf(" StorePlayer[%d][%d] == %d\n", x,y, StorePlayer[x][y]);
434 printf(" MovPos[%d][%d] == %d\n", x,y, MovPos[x][y]);
435 printf(" MovDir[%d][%d] == %d\n", x,y, MovDir[x][y]);
436 printf(" MovDelay[%d][%d] == %d\n", x,y, MovDelay[x][y]);
437 printf(" ChangeDelay[%d][%d] == %d\n", x,y, ChangeDelay[x][y]);
438 printf(" GfxElement[%d][%d] == %d\n", x,y, GfxElement[x][y]);
439 printf(" GfxAction[%d][%d] == %d\n", x,y, GfxAction[x][y]);
440 printf(" GfxFrame[%d][%d] == %d\n", x,y, GfxFrame[x][y]);
452 static boolean is_string_suffix(char *string, char *suffix)
454 int string_len = strlen(string);
455 int suffix_len = strlen(suffix);
457 if (suffix_len > string_len)
460 return (strcmp(&string[string_len - suffix_len], suffix) == 0);
463 #define MAX_CHEAT_INPUT_LEN 32
465 static void HandleKeysCheating(Key key)
467 static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
468 char letter = getCharFromKey(key);
469 int cheat_input_len = strlen(cheat_input);
475 if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
477 for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
478 cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
480 cheat_input_len = MAX_CHEAT_INPUT_LEN;
483 cheat_input[cheat_input_len++] = letter;
484 cheat_input[cheat_input_len] = '\0';
487 printf("::: '%s' [%d]\n", cheat_input, cheat_input_len);
491 if (is_string_suffix(cheat_input, ":insert-solution-tape"))
492 InsertSolutionTape();
494 if (is_string_suffix(cheat_input, ":ist"))
495 InsertSolutionTape();
499 else if (is_string_suffix(cheat_input, ":dump-tape"))
501 else if (is_string_suffix(cheat_input, ".q"))
502 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
503 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
504 local_player->inventory_element[local_player->inventory_size++] =
509 void HandleKey(Key key, int key_status)
512 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
513 static struct SetupKeyboardInfo custom_key;
521 { &custom_key.left, DEFAULT_KEY_LEFT, JOY_LEFT },
522 { &custom_key.right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
523 { &custom_key.up, DEFAULT_KEY_UP, JOY_UP },
524 { &custom_key.down, DEFAULT_KEY_DOWN, JOY_DOWN },
525 { &custom_key.snap, DEFAULT_KEY_SNAP, JOY_BUTTON_1 },
526 { &custom_key.drop, DEFAULT_KEY_DROP, JOY_BUTTON_2 }
529 if (game_status == GAME_MODE_PLAYING)
531 /* only needed for single-step tape recording mode */
532 static boolean clear_button_2[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
533 static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
536 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
541 if (setup.input[pnr].use_joystick)
544 custom_key = setup.input[pnr].key;
546 for (i = 0; i < 6; i++)
547 if (key == *key_info[i].key_custom)
548 key_action |= key_info[i].action;
550 if (tape.single_step && clear_button_2[pnr])
552 stored_player[pnr].action &= ~KEY_BUTTON_2;
553 clear_button_2[pnr] = FALSE;
556 if (key_status == KEY_PRESSED)
557 stored_player[pnr].action |= key_action;
559 stored_player[pnr].action &= ~key_action;
561 if (tape.single_step && tape.recording && tape.pausing)
563 if (key_status == KEY_PRESSED &&
564 (key_action & (KEY_MOTION | KEY_BUTTON_1)))
566 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
568 if (key_action & KEY_MOTION)
570 if (stored_player[pnr].action & KEY_BUTTON_2)
571 element_dropped[pnr] = TRUE;
574 else if (key_status == KEY_RELEASED &&
575 (key_action & KEY_BUTTON_2))
577 if (!element_dropped[pnr])
579 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
581 stored_player[pnr].action |= KEY_BUTTON_2;
582 clear_button_2[pnr] = TRUE;
585 element_dropped[pnr] = FALSE;
588 else if (tape.recording && tape.pausing && (key_action & KEY_ACTION))
589 TapeTogglePause(TAPE_TOGGLE_MANUAL);
596 for (i = 0; i < 6; i++)
597 if (key == key_info[i].key_default)
598 joy |= key_info[i].action;
603 if (key_status == KEY_PRESSED)
604 key_joystick_mapping |= joy;
606 key_joystick_mapping &= ~joy;
611 if (game_status != GAME_MODE_PLAYING)
612 key_joystick_mapping = 0;
614 if (key_status == KEY_RELEASED)
617 if ((key == KSYM_Return || key == setup.shortcut.toggle_pause) &&
618 game_status == GAME_MODE_PLAYING && AllPlayersGone)
620 CloseDoor(DOOR_CLOSE_1);
621 game_status = GAME_MODE_MAIN;
626 /* special key shortcuts */
627 if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
629 if (key == setup.shortcut.save_game)
631 else if (key == setup.shortcut.load_game)
633 else if (key == setup.shortcut.toggle_pause)
634 TapeTogglePause(TAPE_TOGGLE_MANUAL);
636 HandleKeysCheating(key);
639 if (HandleGadgetsKeyInput(key))
641 if (key != KSYM_Escape) /* always allow ESC key to be handled */
642 key = KSYM_UNDEFINED;
647 case GAME_MODE_PSEUDO_TYPENAME:
648 HandleTypeName(0, key);
652 case GAME_MODE_LEVELS:
653 case GAME_MODE_SETUP:
658 if (game_status == GAME_MODE_MAIN)
659 HandleMainMenu(0,0, 0,0, MB_MENU_CHOICE);
660 else if (game_status == GAME_MODE_LEVELS)
661 HandleChooseLevel(0,0, 0,0, MB_MENU_CHOICE);
662 else if (game_status == GAME_MODE_SETUP)
663 HandleSetupScreen(0,0, 0,0, MB_MENU_CHOICE);
664 else if (game_status == GAME_MODE_INFO)
665 HandleInfoScreen(0,0, 0,0, MB_MENU_CHOICE);
669 if (game_status == GAME_MODE_LEVELS)
670 HandleChooseLevel(0,0, 0,0, MB_MENU_LEAVE);
671 else if (game_status == GAME_MODE_SETUP)
672 HandleSetupScreen(0,0, 0,0, MB_MENU_LEAVE);
673 else if (game_status == GAME_MODE_INFO)
674 HandleInfoScreen(0,0, 0,0, MB_MENU_LEAVE);
678 if (game_status == GAME_MODE_LEVELS)
679 HandleChooseLevel(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
680 else if (game_status == GAME_MODE_SETUP)
681 HandleSetupScreen(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
682 else if (game_status == GAME_MODE_INFO)
683 HandleInfoScreen(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
687 if (game_status == GAME_MODE_LEVELS)
688 HandleChooseLevel(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
689 else if (game_status == GAME_MODE_SETUP)
690 HandleSetupScreen(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
691 else if (game_status == GAME_MODE_INFO)
692 HandleInfoScreen(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
700 case GAME_MODE_SCORES:
705 game_status = GAME_MODE_MAIN;
710 HandleHallOfFame(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
714 HandleHallOfFame(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
722 case GAME_MODE_EDITOR:
723 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
724 HandleLevelEditorKeyInput(key);
727 case GAME_MODE_PLAYING:
732 RequestQuitGame(setup.ask_on_escape);
748 if (GameFrameDelay == 500)
749 GameFrameDelay = GAME_FRAME_DELAY;
751 GameFrameDelay = 500;
754 GameFrameDelay = (key - KSYM_0) * 10;
755 printf("Game speed == %d%% (%d ms delay between two frames)\n",
756 GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
762 options.debug = FALSE;
763 printf("debug mode disabled\n");
767 options.debug = TRUE;
768 printf("debug mode enabled\n");
773 if (!global.fps_slowdown)
775 global.fps_slowdown = TRUE;
776 global.fps_slowdown_factor = 2;
777 printf("fps slowdown enabled -- display only every 2nd frame\n");
779 else if (global.fps_slowdown_factor == 2)
781 global.fps_slowdown_factor = 4;
782 printf("fps slowdown enabled -- display only every 4th frame\n");
786 global.fps_slowdown = FALSE;
787 global.fps_slowdown_factor = 1;
788 printf("fps slowdown disabled\n");
794 if (ScrollStepSize == TILEX/8)
795 ScrollStepSize = TILEX/4;
797 ScrollStepSize = TILEX/8;
798 printf("ScrollStepSize == %d\n", ScrollStepSize);
807 ScrollStepSize = TILEX/4;
812 ScrollStepSize = TILEX/8;
814 printf("MoveSpeed == %d\n", MoveSpeed);
819 ScrollStepSize = TILEX/8;
820 printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
824 ScrollStepSize = TILEX/4;
825 printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
829 ScrollStepSize = TILEX/2;
830 printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
834 ScrollStepSize = TILEX;
835 printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
844 for (i = 0; i < MAX_PLAYERS; i++)
846 printf("Player %d:\n", i);
847 printf(" jx == %d, jy == %d\n",
848 stored_player[i].jx, stored_player[i].jy);
849 printf(" last_jx == %d, last_jy == %d\n",
850 stored_player[i].last_jx, stored_player[i].last_jy);
866 if (key == KSYM_Escape)
868 game_status = GAME_MODE_MAIN;
878 if (button_status && game_status != GAME_MODE_PLAYING)
880 HandleButton(0, 0, -button_status);
884 #if defined(PLATFORM_UNIX)
892 static int HandleJoystickForAllPlayers()
897 for (i = 0; i < MAX_PLAYERS; i++)
902 if (!setup.input[i].use_joystick)
906 joy_action = Joystick(i);
907 result |= joy_action;
909 if (!setup.input[i].use_joystick)
912 stored_player[i].action = joy_action;
918 void HandleJoystick()
920 int joystick = HandleJoystickForAllPlayers();
921 int keyboard = key_joystick_mapping;
922 int joy = (joystick | keyboard);
923 int left = joy & JOY_LEFT;
924 int right = joy & JOY_RIGHT;
925 int up = joy & JOY_UP;
926 int down = joy & JOY_DOWN;
927 int button = joy & JOY_BUTTON;
928 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
929 int dx = (left ? -1 : right ? 1 : 0);
930 int dy = (up ? -1 : down ? 1 : 0);
935 case GAME_MODE_LEVELS:
936 case GAME_MODE_SETUP:
939 static unsigned long joystickmove_delay = 0;
941 if (joystick && !button &&
942 !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
943 newbutton = dx = dy = 0;
945 if (game_status == GAME_MODE_MAIN)
946 HandleMainMenu(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
947 else if (game_status == GAME_MODE_LEVELS)
948 HandleChooseLevel(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
949 else if (game_status == GAME_MODE_SETUP)
950 HandleSetupScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
951 else if (game_status == GAME_MODE_INFO)
952 HandleInfoScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
956 case GAME_MODE_SCORES:
957 HandleHallOfFame(0,0, dx,dy, !newbutton);
960 case GAME_MODE_EDITOR:
961 HandleLevelEditorIdle();
964 case GAME_MODE_PLAYING:
965 if (tape.playing || keyboard)
966 newbutton = ((joy & JOY_BUTTON) != 0);
968 if (AllPlayersGone && newbutton)
970 CloseDoor(DOOR_CLOSE_1);
971 game_status = GAME_MODE_MAIN;