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 while (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 && !tape.pausing)
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();
351 if (old_joystick_status != -1)
352 joystick.status = old_joystick_status;
356 void HandleClientMessageEvent(ClientMessageEvent *event)
358 if (CheckCloseWindowEvent(event))
362 void HandleButton(int mx, int my, int button)
364 static int old_mx = 0, old_my = 0;
378 if (HandleGadgets(mx, my, button))
380 /* do not handle this button event anymore */
381 mx = my = -32; /* force mouse event to be outside screen tiles */
387 HandleMainMenu(mx,my, 0,0, button);
390 case GAME_MODE_PSEUDO_TYPENAME:
391 HandleTypeName(0, KSYM_Return);
394 case GAME_MODE_LEVELS:
395 HandleChooseLevel(mx,my, 0,0, button);
398 case GAME_MODE_SCORES:
399 HandleHallOfFame(0,0, 0,0, button);
402 case GAME_MODE_EDITOR:
406 HandleInfoScreen(mx,my, 0,0, button);
409 case GAME_MODE_SETUP:
410 HandleSetupScreen(mx,my, 0,0, button);
413 case GAME_MODE_PLAYING:
415 if (button == MB_PRESSED && !motion_status && IN_GFX_SCREEN(mx, my))
416 DumpTile(LEVELX((mx - SX) / TILEX), LEVELY((my - SY) / TILEY));
425 static boolean is_string_suffix(char *string, char *suffix)
427 int string_len = strlen(string);
428 int suffix_len = strlen(suffix);
430 if (suffix_len > string_len)
433 return (strcmp(&string[string_len - suffix_len], suffix) == 0);
436 #define MAX_CHEAT_INPUT_LEN 32
438 static void HandleKeysSpecial(Key key)
440 static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
441 char letter = getCharFromKey(key);
442 int cheat_input_len = strlen(cheat_input);
448 if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
450 for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
451 cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
453 cheat_input_len = MAX_CHEAT_INPUT_LEN;
456 cheat_input[cheat_input_len++] = letter;
457 cheat_input[cheat_input_len] = '\0';
460 printf("::: '%s' [%d]\n", cheat_input, cheat_input_len);
463 if (game_status == GAME_MODE_MAIN)
465 if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
466 is_string_suffix(cheat_input, ":ist"))
468 InsertSolutionTape();
470 else if (is_string_suffix(cheat_input, ":reload-graphics") ||
471 is_string_suffix(cheat_input, ":rg"))
473 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
476 else if (is_string_suffix(cheat_input, ":reload-sounds") ||
477 is_string_suffix(cheat_input, ":rs"))
479 ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
482 else if (is_string_suffix(cheat_input, ":reload-music") ||
483 is_string_suffix(cheat_input, ":rm"))
485 ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
488 else if (is_string_suffix(cheat_input, ":reload-artwork") ||
489 is_string_suffix(cheat_input, ":ra"))
491 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
492 1 << ARTWORK_TYPE_SOUNDS |
493 1 << ARTWORK_TYPE_MUSIC);
496 else if (is_string_suffix(cheat_input, ":dump-level") ||
497 is_string_suffix(cheat_input, ":dl"))
501 else if (is_string_suffix(cheat_input, ":dump-tape") ||
502 is_string_suffix(cheat_input, ":dt"))
507 else if (game_status == GAME_MODE_PLAYING)
510 if (is_string_suffix(cheat_input, ".q"))
511 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
512 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
513 local_player->inventory_element[local_player->inventory_size++] =
517 else if (game_status == GAME_MODE_EDITOR)
519 if (is_string_suffix(cheat_input, ":dump-brush") ||
520 is_string_suffix(cheat_input, ":DB"))
527 void HandleKey(Key key, int key_status)
530 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
531 static struct SetupKeyboardInfo custom_key;
539 { &custom_key.left, DEFAULT_KEY_LEFT, JOY_LEFT },
540 { &custom_key.right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
541 { &custom_key.up, DEFAULT_KEY_UP, JOY_UP },
542 { &custom_key.down, DEFAULT_KEY_DOWN, JOY_DOWN },
543 { &custom_key.snap, DEFAULT_KEY_SNAP, JOY_BUTTON_1 },
544 { &custom_key.drop, DEFAULT_KEY_DROP, JOY_BUTTON_2 }
547 if (game_status == GAME_MODE_PLAYING)
549 /* only needed for single-step tape recording mode */
550 static boolean clear_button_2[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
551 static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
554 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
559 if (setup.input[pnr].use_joystick)
562 custom_key = setup.input[pnr].key;
564 for (i = 0; i < 6; i++)
565 if (key == *key_info[i].key_custom)
566 key_action |= key_info[i].action;
568 if (tape.single_step && clear_button_2[pnr])
570 stored_player[pnr].action &= ~KEY_BUTTON_2;
571 clear_button_2[pnr] = FALSE;
574 if (key_status == KEY_PRESSED)
575 stored_player[pnr].action |= key_action;
577 stored_player[pnr].action &= ~key_action;
579 if (tape.single_step && tape.recording && tape.pausing)
581 if (key_status == KEY_PRESSED &&
582 (key_action & (KEY_MOTION | KEY_BUTTON_1)))
584 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
586 if (key_action & KEY_MOTION)
588 if (stored_player[pnr].action & KEY_BUTTON_2)
589 element_dropped[pnr] = TRUE;
592 else if (key_status == KEY_RELEASED &&
593 (key_action & KEY_BUTTON_2))
595 if (!element_dropped[pnr])
597 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
599 stored_player[pnr].action |= KEY_BUTTON_2;
600 clear_button_2[pnr] = TRUE;
603 element_dropped[pnr] = FALSE;
606 else if (tape.recording && tape.pausing && (key_action & KEY_ACTION))
607 TapeTogglePause(TAPE_TOGGLE_MANUAL);
614 for (i = 0; i < 6; i++)
615 if (key == key_info[i].key_default)
616 joy |= key_info[i].action;
621 if (key_status == KEY_PRESSED)
622 key_joystick_mapping |= joy;
624 key_joystick_mapping &= ~joy;
629 if (game_status != GAME_MODE_PLAYING)
630 key_joystick_mapping = 0;
632 if (key_status == KEY_RELEASED)
635 if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
636 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
638 CloseDoor(DOOR_CLOSE_1);
639 game_status = GAME_MODE_MAIN;
645 if (game_status == GAME_MODE_MAIN && key == setup.shortcut.toggle_pause)
647 if (setup.autorecord)
648 TapeStartRecording();
650 #if defined(NETWORK_AVALIABLE)
652 SendToServer_StartPlaying();
656 game_status = GAME_MODE_PLAYING;
664 if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
666 if (key == setup.shortcut.save_game)
668 else if (key == setup.shortcut.load_game)
670 else if (key == setup.shortcut.toggle_pause)
671 TapeTogglePause(TAPE_TOGGLE_MANUAL);
674 HandleKeysSpecial(key);
676 if (HandleGadgetsKeyInput(key))
678 if (key != KSYM_Escape) /* always allow ESC key to be handled */
679 key = KSYM_UNDEFINED;
684 case GAME_MODE_PSEUDO_TYPENAME:
685 HandleTypeName(0, key);
689 case GAME_MODE_LEVELS:
690 case GAME_MODE_SETUP:
696 if (game_status == GAME_MODE_MAIN)
697 HandleMainMenu(0,0, 0,0, MB_MENU_CHOICE);
698 else if (game_status == GAME_MODE_LEVELS)
699 HandleChooseLevel(0,0, 0,0, MB_MENU_CHOICE);
700 else if (game_status == GAME_MODE_SETUP)
701 HandleSetupScreen(0,0, 0,0, MB_MENU_CHOICE);
702 else if (game_status == GAME_MODE_INFO)
703 HandleInfoScreen(0,0, 0,0, MB_MENU_CHOICE);
707 if (game_status == GAME_MODE_LEVELS)
708 HandleChooseLevel(0,0, 0,0, MB_MENU_LEAVE);
709 else if (game_status == GAME_MODE_SETUP)
710 HandleSetupScreen(0,0, 0,0, MB_MENU_LEAVE);
711 else if (game_status == GAME_MODE_INFO)
712 HandleInfoScreen(0,0, 0,0, MB_MENU_LEAVE);
716 if (game_status == GAME_MODE_LEVELS)
717 HandleChooseLevel(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
718 else if (game_status == GAME_MODE_SETUP)
719 HandleSetupScreen(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
720 else if (game_status == GAME_MODE_INFO)
721 HandleInfoScreen(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
725 if (game_status == GAME_MODE_LEVELS)
726 HandleChooseLevel(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
727 else if (game_status == GAME_MODE_SETUP)
728 HandleSetupScreen(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
729 else if (game_status == GAME_MODE_INFO)
730 HandleInfoScreen(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
738 case GAME_MODE_SCORES:
744 game_status = GAME_MODE_MAIN;
749 HandleHallOfFame(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
753 HandleHallOfFame(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
761 case GAME_MODE_EDITOR:
762 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
763 HandleLevelEditorKeyInput(key);
766 case GAME_MODE_PLAYING:
771 RequestQuitGame(setup.ask_on_escape);
787 if (GameFrameDelay == 500)
788 GameFrameDelay = GAME_FRAME_DELAY;
790 GameFrameDelay = 500;
793 GameFrameDelay = (key - KSYM_0) * 10;
794 printf("Game speed == %d%% (%d ms delay between two frames)\n",
795 GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
801 options.debug = FALSE;
802 printf("debug mode disabled\n");
806 options.debug = TRUE;
807 printf("debug mode enabled\n");
812 if (!global.fps_slowdown)
814 global.fps_slowdown = TRUE;
815 global.fps_slowdown_factor = 2;
816 printf("fps slowdown enabled -- display only every 2nd frame\n");
818 else if (global.fps_slowdown_factor == 2)
820 global.fps_slowdown_factor = 4;
821 printf("fps slowdown enabled -- display only every 4th frame\n");
825 global.fps_slowdown = FALSE;
826 global.fps_slowdown_factor = 1;
827 printf("fps slowdown disabled\n");
833 if (ScrollStepSize == TILEX/8)
834 ScrollStepSize = TILEX/4;
836 ScrollStepSize = TILEX/8;
837 printf("ScrollStepSize == %d\n", ScrollStepSize);
846 ScrollStepSize = TILEX/4;
851 ScrollStepSize = TILEX/8;
853 printf("MoveSpeed == %d\n", MoveSpeed);
858 ScrollStepSize = TILEX/8;
859 printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
863 ScrollStepSize = TILEX/4;
864 printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
868 ScrollStepSize = TILEX/2;
869 printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
873 ScrollStepSize = TILEX;
874 printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
878 printf("::: currently using game engine version %d\n",
879 game.engine_version);
888 for (i = 0; i < MAX_PLAYERS; i++)
890 printf("Player %d:\n", i);
891 printf(" jx == %d, jy == %d\n",
892 stored_player[i].jx, stored_player[i].jy);
893 printf(" last_jx == %d, last_jy == %d\n",
894 stored_player[i].last_jx, stored_player[i].last_jy);
910 if (key == KSYM_Escape)
912 game_status = GAME_MODE_MAIN;
922 if (button_status && game_status != GAME_MODE_PLAYING)
924 HandleButton(0, 0, -button_status);
928 #if defined(NETWORK_AVALIABLE)
936 static int HandleJoystickForAllPlayers()
941 for (i = 0; i < MAX_PLAYERS; i++)
946 if (!setup.input[i].use_joystick)
950 joy_action = Joystick(i);
951 result |= joy_action;
953 if (!setup.input[i].use_joystick)
956 stored_player[i].action = joy_action;
962 void HandleJoystick()
964 int joystick = HandleJoystickForAllPlayers();
965 int keyboard = key_joystick_mapping;
966 int joy = (joystick | keyboard);
967 int left = joy & JOY_LEFT;
968 int right = joy & JOY_RIGHT;
969 int up = joy & JOY_UP;
970 int down = joy & JOY_DOWN;
971 int button = joy & JOY_BUTTON;
972 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
973 int dx = (left ? -1 : right ? 1 : 0);
974 int dy = (up ? -1 : down ? 1 : 0);
979 case GAME_MODE_LEVELS:
980 case GAME_MODE_SETUP:
983 static unsigned long joystickmove_delay = 0;
985 if (joystick && !button &&
986 !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
987 newbutton = dx = dy = 0;
989 if (game_status == GAME_MODE_MAIN)
990 HandleMainMenu(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
991 else if (game_status == GAME_MODE_LEVELS)
992 HandleChooseLevel(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
993 else if (game_status == GAME_MODE_SETUP)
994 HandleSetupScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
995 else if (game_status == GAME_MODE_INFO)
996 HandleInfoScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
1000 case GAME_MODE_SCORES:
1001 HandleHallOfFame(0,0, dx,dy, !newbutton);
1004 case GAME_MODE_EDITOR:
1005 HandleLevelEditorIdle();
1008 case GAME_MODE_PLAYING:
1009 if (tape.playing || keyboard)
1010 newbutton = ((joy & JOY_BUTTON) != 0);
1012 if (AllPlayersGone && newbutton)
1014 CloseDoor(DOOR_CLOSE_1);
1015 game_status = GAME_MODE_MAIN;