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();
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_PRESSED && !motion_status && IN_GFX_SCREEN(mx, my))
415 DumpTile(LEVELX((mx - SX) / TILEX), LEVELY((my - SY) / TILEY));
424 static boolean is_string_suffix(char *string, char *suffix)
426 int string_len = strlen(string);
427 int suffix_len = strlen(suffix);
429 if (suffix_len > string_len)
432 return (strcmp(&string[string_len - suffix_len], suffix) == 0);
435 #define MAX_CHEAT_INPUT_LEN 32
437 static void HandleKeysSpecial(Key key)
439 static char cheat_input[2 * MAX_CHEAT_INPUT_LEN + 1] = "";
440 char letter = getCharFromKey(key);
441 int cheat_input_len = strlen(cheat_input);
447 if (cheat_input_len >= 2 * MAX_CHEAT_INPUT_LEN)
449 for (i = 0; i < MAX_CHEAT_INPUT_LEN + 1; i++)
450 cheat_input[i] = cheat_input[MAX_CHEAT_INPUT_LEN + i];
452 cheat_input_len = MAX_CHEAT_INPUT_LEN;
455 cheat_input[cheat_input_len++] = letter;
456 cheat_input[cheat_input_len] = '\0';
459 printf("::: '%s' [%d]\n", cheat_input, cheat_input_len);
462 if (game_status == GAME_MODE_MAIN)
464 if (is_string_suffix(cheat_input, ":insert-solution-tape") ||
465 is_string_suffix(cheat_input, ":ist"))
467 InsertSolutionTape();
469 else if (is_string_suffix(cheat_input, ":reload-graphics") ||
470 is_string_suffix(cheat_input, ":rg"))
472 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS);
475 else if (is_string_suffix(cheat_input, ":reload-sounds") ||
476 is_string_suffix(cheat_input, ":rs"))
478 ReloadCustomArtwork(1 << ARTWORK_TYPE_SOUNDS);
481 else if (is_string_suffix(cheat_input, ":reload-music") ||
482 is_string_suffix(cheat_input, ":rm"))
484 ReloadCustomArtwork(1 << ARTWORK_TYPE_MUSIC);
487 else if (is_string_suffix(cheat_input, ":reload-artwork") ||
488 is_string_suffix(cheat_input, ":ra"))
490 ReloadCustomArtwork(1 << ARTWORK_TYPE_GRAPHICS |
491 1 << ARTWORK_TYPE_SOUNDS |
492 1 << ARTWORK_TYPE_MUSIC);
495 else if (is_string_suffix(cheat_input, ":dump-level") ||
496 is_string_suffix(cheat_input, ":dl"))
500 else if (is_string_suffix(cheat_input, ":dump-tape") ||
501 is_string_suffix(cheat_input, ":dt"))
506 else if (game_status == GAME_MODE_PLAYING)
509 if (is_string_suffix(cheat_input, ".q"))
510 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
511 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
512 local_player->inventory_element[local_player->inventory_size++] =
516 else if (game_status == GAME_MODE_EDITOR)
518 if (is_string_suffix(cheat_input, ":dump-brush") ||
519 is_string_suffix(cheat_input, ":DB"))
526 void HandleKey(Key key, int key_status)
529 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
530 static struct SetupKeyboardInfo custom_key;
538 { &custom_key.left, DEFAULT_KEY_LEFT, JOY_LEFT },
539 { &custom_key.right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
540 { &custom_key.up, DEFAULT_KEY_UP, JOY_UP },
541 { &custom_key.down, DEFAULT_KEY_DOWN, JOY_DOWN },
542 { &custom_key.snap, DEFAULT_KEY_SNAP, JOY_BUTTON_1 },
543 { &custom_key.drop, DEFAULT_KEY_DROP, JOY_BUTTON_2 }
546 if (game_status == GAME_MODE_PLAYING)
548 /* only needed for single-step tape recording mode */
549 static boolean clear_button_2[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
550 static boolean element_dropped[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
553 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
558 if (setup.input[pnr].use_joystick)
561 custom_key = setup.input[pnr].key;
563 for (i = 0; i < 6; i++)
564 if (key == *key_info[i].key_custom)
565 key_action |= key_info[i].action;
567 if (tape.single_step && clear_button_2[pnr])
569 stored_player[pnr].action &= ~KEY_BUTTON_2;
570 clear_button_2[pnr] = FALSE;
573 if (key_status == KEY_PRESSED)
574 stored_player[pnr].action |= key_action;
576 stored_player[pnr].action &= ~key_action;
578 if (tape.single_step && tape.recording && tape.pausing)
580 if (key_status == KEY_PRESSED &&
581 (key_action & (KEY_MOTION | KEY_BUTTON_1)))
583 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
585 if (key_action & KEY_MOTION)
587 if (stored_player[pnr].action & KEY_BUTTON_2)
588 element_dropped[pnr] = TRUE;
591 else if (key_status == KEY_RELEASED &&
592 (key_action & KEY_BUTTON_2))
594 if (!element_dropped[pnr])
596 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
598 stored_player[pnr].action |= KEY_BUTTON_2;
599 clear_button_2[pnr] = TRUE;
602 element_dropped[pnr] = FALSE;
605 else if (tape.recording && tape.pausing && (key_action & KEY_ACTION))
606 TapeTogglePause(TAPE_TOGGLE_MANUAL);
613 for (i = 0; i < 6; i++)
614 if (key == key_info[i].key_default)
615 joy |= key_info[i].action;
620 if (key_status == KEY_PRESSED)
621 key_joystick_mapping |= joy;
623 key_joystick_mapping &= ~joy;
628 if (game_status != GAME_MODE_PLAYING)
629 key_joystick_mapping = 0;
631 if (key_status == KEY_RELEASED)
634 if (game_status == GAME_MODE_PLAYING && AllPlayersGone &&
635 (key == KSYM_Return || key == setup.shortcut.toggle_pause))
637 CloseDoor(DOOR_CLOSE_1);
638 game_status = GAME_MODE_MAIN;
644 if (game_status == GAME_MODE_MAIN && key == setup.shortcut.toggle_pause)
646 if (setup.autorecord)
647 TapeStartRecording();
649 #if defined(NETWORK_AVALIABLE)
651 SendToServer_StartPlaying();
655 game_status = GAME_MODE_PLAYING;
663 if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
665 if (key == setup.shortcut.save_game)
667 else if (key == setup.shortcut.load_game)
669 else if (key == setup.shortcut.toggle_pause)
670 TapeTogglePause(TAPE_TOGGLE_MANUAL);
673 HandleKeysSpecial(key);
675 if (HandleGadgetsKeyInput(key))
677 if (key != KSYM_Escape) /* always allow ESC key to be handled */
678 key = KSYM_UNDEFINED;
683 case GAME_MODE_PSEUDO_TYPENAME:
684 HandleTypeName(0, key);
688 case GAME_MODE_LEVELS:
689 case GAME_MODE_SETUP:
695 if (game_status == GAME_MODE_MAIN)
696 HandleMainMenu(0,0, 0,0, MB_MENU_CHOICE);
697 else if (game_status == GAME_MODE_LEVELS)
698 HandleChooseLevel(0,0, 0,0, MB_MENU_CHOICE);
699 else if (game_status == GAME_MODE_SETUP)
700 HandleSetupScreen(0,0, 0,0, MB_MENU_CHOICE);
701 else if (game_status == GAME_MODE_INFO)
702 HandleInfoScreen(0,0, 0,0, MB_MENU_CHOICE);
706 if (game_status == GAME_MODE_LEVELS)
707 HandleChooseLevel(0,0, 0,0, MB_MENU_LEAVE);
708 else if (game_status == GAME_MODE_SETUP)
709 HandleSetupScreen(0,0, 0,0, MB_MENU_LEAVE);
710 else if (game_status == GAME_MODE_INFO)
711 HandleInfoScreen(0,0, 0,0, MB_MENU_LEAVE);
715 if (game_status == GAME_MODE_LEVELS)
716 HandleChooseLevel(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
717 else if (game_status == GAME_MODE_SETUP)
718 HandleSetupScreen(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
719 else if (game_status == GAME_MODE_INFO)
720 HandleInfoScreen(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
724 if (game_status == GAME_MODE_LEVELS)
725 HandleChooseLevel(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
726 else if (game_status == GAME_MODE_SETUP)
727 HandleSetupScreen(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
728 else if (game_status == GAME_MODE_INFO)
729 HandleInfoScreen(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
737 case GAME_MODE_SCORES:
743 game_status = GAME_MODE_MAIN;
748 HandleHallOfFame(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
752 HandleHallOfFame(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
760 case GAME_MODE_EDITOR:
761 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
762 HandleLevelEditorKeyInput(key);
765 case GAME_MODE_PLAYING:
770 RequestQuitGame(setup.ask_on_escape);
786 if (GameFrameDelay == 500)
787 GameFrameDelay = GAME_FRAME_DELAY;
789 GameFrameDelay = 500;
792 GameFrameDelay = (key - KSYM_0) * 10;
793 printf("Game speed == %d%% (%d ms delay between two frames)\n",
794 GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
800 options.debug = FALSE;
801 printf("debug mode disabled\n");
805 options.debug = TRUE;
806 printf("debug mode enabled\n");
811 if (!global.fps_slowdown)
813 global.fps_slowdown = TRUE;
814 global.fps_slowdown_factor = 2;
815 printf("fps slowdown enabled -- display only every 2nd frame\n");
817 else if (global.fps_slowdown_factor == 2)
819 global.fps_slowdown_factor = 4;
820 printf("fps slowdown enabled -- display only every 4th frame\n");
824 global.fps_slowdown = FALSE;
825 global.fps_slowdown_factor = 1;
826 printf("fps slowdown disabled\n");
832 if (ScrollStepSize == TILEX/8)
833 ScrollStepSize = TILEX/4;
835 ScrollStepSize = TILEX/8;
836 printf("ScrollStepSize == %d\n", ScrollStepSize);
845 ScrollStepSize = TILEX/4;
850 ScrollStepSize = TILEX/8;
852 printf("MoveSpeed == %d\n", MoveSpeed);
857 ScrollStepSize = TILEX/8;
858 printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
862 ScrollStepSize = TILEX/4;
863 printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
867 ScrollStepSize = TILEX/2;
868 printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
872 ScrollStepSize = TILEX;
873 printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
877 printf("::: currently using game engine version %d\n",
878 game.engine_version);
887 for (i = 0; i < MAX_PLAYERS; i++)
889 printf("Player %d:\n", i);
890 printf(" jx == %d, jy == %d\n",
891 stored_player[i].jx, stored_player[i].jy);
892 printf(" last_jx == %d, last_jy == %d\n",
893 stored_player[i].last_jx, stored_player[i].last_jy);
909 if (key == KSYM_Escape)
911 game_status = GAME_MODE_MAIN;
921 if (button_status && game_status != GAME_MODE_PLAYING)
923 HandleButton(0, 0, -button_status);
927 #if defined(NETWORK_AVALIABLE)
935 static int HandleJoystickForAllPlayers()
940 for (i = 0; i < MAX_PLAYERS; i++)
945 if (!setup.input[i].use_joystick)
949 joy_action = Joystick(i);
950 result |= joy_action;
952 if (!setup.input[i].use_joystick)
955 stored_player[i].action = joy_action;
961 void HandleJoystick()
963 int joystick = HandleJoystickForAllPlayers();
964 int keyboard = key_joystick_mapping;
965 int joy = (joystick | keyboard);
966 int left = joy & JOY_LEFT;
967 int right = joy & JOY_RIGHT;
968 int up = joy & JOY_UP;
969 int down = joy & JOY_DOWN;
970 int button = joy & JOY_BUTTON;
971 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
972 int dx = (left ? -1 : right ? 1 : 0);
973 int dy = (up ? -1 : down ? 1 : 0);
978 case GAME_MODE_LEVELS:
979 case GAME_MODE_SETUP:
982 static unsigned long joystickmove_delay = 0;
984 if (joystick && !button &&
985 !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
986 newbutton = dx = dy = 0;
988 if (game_status == GAME_MODE_MAIN)
989 HandleMainMenu(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
990 else if (game_status == GAME_MODE_LEVELS)
991 HandleChooseLevel(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
992 else if (game_status == GAME_MODE_SETUP)
993 HandleSetupScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
994 else if (game_status == GAME_MODE_INFO)
995 HandleInfoScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
999 case GAME_MODE_SCORES:
1000 HandleHallOfFame(0,0, dx,dy, !newbutton);
1003 case GAME_MODE_EDITOR:
1004 HandleLevelEditorIdle();
1007 case GAME_MODE_PLAYING:
1008 if (tape.playing || keyboard)
1009 newbutton = ((joy & JOY_BUTTON) != 0);
1011 if (AllPlayersGone && newbutton)
1013 CloseDoor(DOOR_CLOSE_1);
1014 game_status = GAME_MODE_MAIN;