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]);
441 printf(" RunnerVisit[%d][%d] == %d\n", x,y, RunnerVisit[x][y]);
442 printf(" PlayerVisit[%d][%d] == %d\n", x,y, PlayerVisit[x][y]);
454 void HandleKey(Key key, int key_status)
457 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
458 static struct SetupKeyboardInfo custom_key;
466 { &custom_key.left, DEFAULT_KEY_LEFT, JOY_LEFT },
467 { &custom_key.right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
468 { &custom_key.up, DEFAULT_KEY_UP, JOY_UP },
469 { &custom_key.down, DEFAULT_KEY_DOWN, JOY_DOWN },
470 { &custom_key.snap, DEFAULT_KEY_SNAP, JOY_BUTTON_1 },
471 { &custom_key.bomb, DEFAULT_KEY_BOMB, JOY_BUTTON_2 }
474 if (game_status == GAME_MODE_PLAYING)
476 /* only needed for single-step tape recording mode */
477 static boolean clear_button_2[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
478 static boolean bomb_placed[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
481 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
486 if (setup.input[pnr].use_joystick)
489 custom_key = setup.input[pnr].key;
491 for (i = 0; i < 6; i++)
492 if (key == *key_info[i].key_custom)
493 key_action |= key_info[i].action;
495 if (tape.single_step && clear_button_2[pnr])
497 stored_player[pnr].action &= ~KEY_BUTTON_2;
498 clear_button_2[pnr] = FALSE;
501 if (key_status == KEY_PRESSED)
502 stored_player[pnr].action |= key_action;
504 stored_player[pnr].action &= ~key_action;
506 if (tape.single_step && tape.recording && tape.pausing)
508 if (key_status == KEY_PRESSED &&
509 (key_action & (KEY_MOTION | KEY_BUTTON_1)))
511 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
513 if (key_action & KEY_MOTION)
515 if (stored_player[pnr].action & KEY_BUTTON_2)
516 bomb_placed[pnr] = TRUE;
519 else if (key_status == KEY_RELEASED &&
520 (key_action & KEY_BUTTON_2))
522 if (!bomb_placed[pnr])
524 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
526 stored_player[pnr].action |= KEY_BUTTON_2;
527 clear_button_2[pnr] = TRUE;
530 bomb_placed[pnr] = FALSE;
533 else if (tape.recording && tape.pausing && (key_action & KEY_ACTION))
534 TapeTogglePause(TAPE_TOGGLE_MANUAL);
541 for (i = 0; i < 6; i++)
542 if (key == key_info[i].key_default)
543 joy |= key_info[i].action;
548 if (key_status == KEY_PRESSED)
549 key_joystick_mapping |= joy;
551 key_joystick_mapping &= ~joy;
556 if (game_status != GAME_MODE_PLAYING)
557 key_joystick_mapping = 0;
559 if (key_status == KEY_RELEASED)
562 if ((key == KSYM_Return || key == setup.shortcut.toggle_pause) &&
563 game_status == GAME_MODE_PLAYING && AllPlayersGone)
565 CloseDoor(DOOR_CLOSE_1);
566 game_status = GAME_MODE_MAIN;
571 /* special key shortcuts */
572 if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
574 if (key == setup.shortcut.save_game)
576 else if (key == setup.shortcut.load_game)
578 else if (key == setup.shortcut.toggle_pause)
579 TapeTogglePause(TAPE_TOGGLE_MANUAL);
582 if (HandleGadgetsKeyInput(key))
584 if (key != KSYM_Escape) /* always allow ESC key to be handled */
585 key = KSYM_UNDEFINED;
590 case GAME_MODE_PSEUDO_TYPENAME:
591 HandleTypeName(0, key);
595 case GAME_MODE_LEVELS:
596 case GAME_MODE_SETUP:
601 if (game_status == GAME_MODE_MAIN)
602 HandleMainMenu(0,0, 0,0, MB_MENU_CHOICE);
603 else if (game_status == GAME_MODE_LEVELS)
604 HandleChooseLevel(0,0, 0,0, MB_MENU_CHOICE);
605 else if (game_status == GAME_MODE_SETUP)
606 HandleSetupScreen(0,0, 0,0, MB_MENU_CHOICE);
607 else if (game_status == GAME_MODE_INFO)
608 HandleInfoScreen(0,0, 0,0, MB_MENU_CHOICE);
612 if (game_status == GAME_MODE_LEVELS)
613 HandleChooseLevel(0,0, 0,0, MB_MENU_LEAVE);
614 else if (game_status == GAME_MODE_SETUP)
615 HandleSetupScreen(0,0, 0,0, MB_MENU_LEAVE);
616 else if (game_status == GAME_MODE_INFO)
617 HandleInfoScreen(0,0, 0,0, MB_MENU_LEAVE);
621 if (game_status == GAME_MODE_LEVELS)
622 HandleChooseLevel(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
623 else if (game_status == GAME_MODE_SETUP)
624 HandleSetupScreen(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
625 else if (game_status == GAME_MODE_INFO)
626 HandleInfoScreen(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
630 if (game_status == GAME_MODE_LEVELS)
631 HandleChooseLevel(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
632 else if (game_status == GAME_MODE_SETUP)
633 HandleSetupScreen(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
634 else if (game_status == GAME_MODE_INFO)
635 HandleInfoScreen(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
649 case GAME_MODE_SCORES:
654 game_status = GAME_MODE_MAIN;
659 HandleHallOfFame(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
663 HandleHallOfFame(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
671 case GAME_MODE_EDITOR:
672 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
673 HandleLevelEditorKeyInput(key);
676 case GAME_MODE_PLAYING:
681 RequestQuitGame(setup.ask_on_escape);
697 if (GameFrameDelay == 500)
698 GameFrameDelay = GAME_FRAME_DELAY;
700 GameFrameDelay = 500;
703 GameFrameDelay = (key - KSYM_0) * 10;
704 printf("Game speed == %d%% (%d ms delay between two frames)\n",
705 GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
711 options.debug = FALSE;
712 printf("debug mode disabled\n");
716 options.debug = TRUE;
717 printf("debug mode enabled\n");
722 if (!global.fps_slowdown)
724 global.fps_slowdown = TRUE;
725 global.fps_slowdown_factor = 2;
726 printf("fps slowdown enabled -- display only every 2nd frame\n");
728 else if (global.fps_slowdown_factor == 2)
730 global.fps_slowdown_factor = 4;
731 printf("fps slowdown enabled -- display only every 4th frame\n");
735 global.fps_slowdown = FALSE;
736 global.fps_slowdown_factor = 1;
737 printf("fps slowdown disabled\n");
743 if (ScrollStepSize == TILEX/8)
744 ScrollStepSize = TILEX/4;
746 ScrollStepSize = TILEX/8;
747 printf("ScrollStepSize == %d\n", ScrollStepSize);
756 ScrollStepSize = TILEX/4;
761 ScrollStepSize = TILEX/8;
763 printf("MoveSpeed == %d\n", MoveSpeed);
768 ScrollStepSize = TILEX/8;
769 printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
773 ScrollStepSize = TILEX/4;
774 printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
778 ScrollStepSize = TILEX/2;
779 printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
783 ScrollStepSize = TILEX;
784 printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
792 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
793 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
794 local_player->inventory_element[local_player->inventory_size++] =
807 for (i = 0; i < MAX_PLAYERS; i++)
809 printf("Player %d:\n", i);
810 printf(" jx == %d, jy == %d\n",
811 stored_player[i].jx, stored_player[i].jy);
812 printf(" last_jx == %d, last_jy == %d\n",
813 stored_player[i].last_jx, stored_player[i].last_jy);
828 if (key == KSYM_Escape)
830 game_status = GAME_MODE_MAIN;
840 if (button_status && game_status != GAME_MODE_PLAYING)
842 HandleButton(0, 0, -button_status);
846 #if defined(PLATFORM_UNIX)
854 static int HandleJoystickForAllPlayers()
859 for (i = 0; i < MAX_PLAYERS; i++)
864 if (!setup.input[i].use_joystick)
868 joy_action = Joystick(i);
869 result |= joy_action;
871 if (!setup.input[i].use_joystick)
874 stored_player[i].action = joy_action;
880 void HandleJoystick()
882 int joystick = HandleJoystickForAllPlayers();
883 int keyboard = key_joystick_mapping;
884 int joy = (joystick | keyboard);
885 int left = joy & JOY_LEFT;
886 int right = joy & JOY_RIGHT;
887 int up = joy & JOY_UP;
888 int down = joy & JOY_DOWN;
889 int button = joy & JOY_BUTTON;
890 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
891 int dx = (left ? -1 : right ? 1 : 0);
892 int dy = (up ? -1 : down ? 1 : 0);
897 case GAME_MODE_LEVELS:
898 case GAME_MODE_SETUP:
901 static unsigned long joystickmove_delay = 0;
903 if (joystick && !button &&
904 !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
905 newbutton = dx = dy = 0;
907 if (game_status == GAME_MODE_MAIN)
908 HandleMainMenu(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
909 else if (game_status == GAME_MODE_LEVELS)
910 HandleChooseLevel(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
911 else if (game_status == GAME_MODE_SETUP)
912 HandleSetupScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
913 else if (game_status == GAME_MODE_INFO)
914 HandleInfoScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
918 case GAME_MODE_SCORES:
919 HandleHallOfFame(0,0, dx,dy, !newbutton);
922 case GAME_MODE_EDITOR:
923 HandleLevelEditorIdle();
926 case GAME_MODE_PLAYING:
927 if (tape.playing || keyboard)
928 newbutton = ((joy & JOY_BUTTON) != 0);
930 if (AllPlayersGone && newbutton)
932 CloseDoor(DOOR_CLOSE_1);
933 game_status = GAME_MODE_MAIN;