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 HandleGadgets(mx, my, button);
382 HandleMainMenu(mx,my, 0,0, button);
385 case GAME_MODE_PSEUDO_TYPENAME:
386 HandleTypeName(0, KSYM_Return);
389 case GAME_MODE_LEVELS:
390 HandleChooseLevel(mx,my, 0,0, button);
393 case GAME_MODE_SCORES:
394 HandleHallOfFame(0,0, 0,0, button);
397 case GAME_MODE_EDITOR:
401 HandleHelpScreen(button);
404 case GAME_MODE_SETUP:
405 HandleSetupScreen(mx,my, 0,0, button);
408 case GAME_MODE_PLAYING:
410 if (button == MB_RELEASED)
412 int sx = (mx - SX) / TILEX;
413 int sy = (my - SY) / TILEY;
415 if (IN_VIS_FIELD(sx,sy))
420 printf("INFO: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
422 if (!IN_LEV_FIELD(x, y))
425 printf(" Feld[%d][%d] == %d ('%s')\n", x,y, Feld[x][y],
426 element_info[Feld[x][y]].token_name);
427 printf(" Back[%d][%d] == %d\n", x,y, Back[x][y]);
428 printf(" Store[%d][%d] == %d\n", x,y, Store[x][y]);
429 printf(" Store2[%d][%d] == %d\n", x,y, Store2[x][y]);
430 printf(" StorePlayer[%d][%d] == %d\n", x,y, StorePlayer[x][y]);
431 printf(" MovPos[%d][%d] == %d\n", x,y, MovPos[x][y]);
432 printf(" MovDir[%d][%d] == %d\n", x,y, MovDir[x][y]);
433 printf(" MovDelay[%d][%d] == %d\n", x,y, MovDelay[x][y]);
434 printf(" ChangeDelay[%d][%d] == %d\n", x,y, ChangeDelay[x][y]);
435 printf(" GfxElement[%d][%d] == %d\n", x,y, GfxElement[x][y]);
436 printf(" GfxAction[%d][%d] == %d\n", x,y, GfxAction[x][y]);
437 printf(" GfxFrame[%d][%d] == %d\n", x,y, GfxFrame[x][y]);
449 void HandleKey(Key key, int key_status)
452 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
453 static struct SetupKeyboardInfo custom_key;
461 { &custom_key.left, DEFAULT_KEY_LEFT, JOY_LEFT },
462 { &custom_key.right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
463 { &custom_key.up, DEFAULT_KEY_UP, JOY_UP },
464 { &custom_key.down, DEFAULT_KEY_DOWN, JOY_DOWN },
465 { &custom_key.snap, DEFAULT_KEY_SNAP, JOY_BUTTON_1 },
466 { &custom_key.bomb, DEFAULT_KEY_BOMB, JOY_BUTTON_2 }
469 if (game_status == GAME_MODE_PLAYING)
471 /* only needed for single-step tape recording mode */
472 static boolean clear_button_2[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
473 static boolean bomb_placed[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
476 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
481 if (setup.input[pnr].use_joystick)
484 custom_key = setup.input[pnr].key;
487 if (key == *key_info[i].key_custom)
488 key_action |= key_info[i].action;
490 if (tape.single_step && clear_button_2[pnr])
492 stored_player[pnr].action &= ~KEY_BUTTON_2;
493 clear_button_2[pnr] = FALSE;
496 if (key_status == KEY_PRESSED)
497 stored_player[pnr].action |= key_action;
499 stored_player[pnr].action &= ~key_action;
501 if (tape.single_step && tape.recording && tape.pausing)
503 if (key_status == KEY_PRESSED &&
504 (key_action & (KEY_MOTION | KEY_BUTTON_1)))
506 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
508 if (key_action & KEY_MOTION)
510 if (stored_player[pnr].action & KEY_BUTTON_2)
511 bomb_placed[pnr] = TRUE;
514 else if (key_status == KEY_RELEASED &&
515 (key_action & KEY_BUTTON_2))
517 if (!bomb_placed[pnr])
519 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
521 stored_player[pnr].action |= KEY_BUTTON_2;
522 clear_button_2[pnr] = TRUE;
525 bomb_placed[pnr] = FALSE;
528 else if (tape.recording && tape.pausing && (key_action & KEY_ACTION))
529 TapeTogglePause(TAPE_TOGGLE_MANUAL);
537 if (key == key_info[i].key_default)
538 joy |= key_info[i].action;
543 if (key_status == KEY_PRESSED)
544 key_joystick_mapping |= joy;
546 key_joystick_mapping &= ~joy;
551 if (game_status != GAME_MODE_PLAYING)
552 key_joystick_mapping = 0;
554 if (key_status == KEY_RELEASED)
557 if ((key == KSYM_Return || key == setup.shortcut.toggle_pause) &&
558 game_status == GAME_MODE_PLAYING && AllPlayersGone)
560 CloseDoor(DOOR_CLOSE_1);
561 game_status = GAME_MODE_MAIN;
566 /* allow quick escape to the main menu with the Escape key */
567 if (key == KSYM_Escape &&
568 game_status != GAME_MODE_MAIN &&
569 game_status != GAME_MODE_PLAYING &&
570 game_status != GAME_MODE_EDITOR &&
571 game_status != GAME_MODE_LEVELS &&
572 game_status != GAME_MODE_SETUP)
574 game_status = GAME_MODE_MAIN;
579 /* special key shortcuts */
580 if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
582 if (key == setup.shortcut.save_game)
584 else if (key == setup.shortcut.load_game)
586 else if (key == setup.shortcut.toggle_pause)
587 TapeTogglePause(TAPE_TOGGLE_MANUAL);
593 if (game_status == GAME_MODE_PLAYING && (tape.playing || tape.pausing))
600 HandleGadgetsKeyInput(key);
604 case GAME_MODE_PSEUDO_TYPENAME:
605 HandleTypeName(0, key);
609 case GAME_MODE_LEVELS:
610 case GAME_MODE_SETUP:
614 if (game_status == GAME_MODE_MAIN)
615 HandleMainMenu(0,0, 0,0, MB_MENU_CHOICE);
616 else if (game_status == GAME_MODE_LEVELS)
617 HandleChooseLevel(0,0, 0,0, MB_MENU_CHOICE);
618 else if (game_status == GAME_MODE_SETUP)
619 HandleSetupScreen(0,0, 0,0, MB_MENU_CHOICE);
623 if (game_status == GAME_MODE_LEVELS)
624 HandleChooseLevel(0,0, 0,0, MB_MENU_LEAVE);
625 else if (game_status == GAME_MODE_SETUP)
626 HandleSetupScreen(0,0, 0,0, MB_MENU_LEAVE);
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);
637 if (game_status == GAME_MODE_LEVELS)
638 HandleChooseLevel(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
639 else if (game_status == GAME_MODE_SETUP)
640 HandleSetupScreen(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
655 HandleHelpScreen(MB_RELEASED);
658 case GAME_MODE_SCORES:
662 game_status = GAME_MODE_MAIN;
668 HandleHallOfFame(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
672 HandleHallOfFame(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
680 case GAME_MODE_EDITOR:
681 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
682 HandleLevelEditorKeyInput(key);
685 case GAME_MODE_PLAYING:
690 RequestQuitGame(setup.ask_on_escape);
706 if (GameFrameDelay == 500)
707 GameFrameDelay = GAME_FRAME_DELAY;
709 GameFrameDelay = 500;
712 GameFrameDelay = (key - KSYM_0) * 10;
713 printf("Game speed == %d%% (%d ms delay between two frames)\n",
714 GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
720 options.debug = FALSE;
721 printf("debug mode disabled\n");
725 options.debug = TRUE;
726 printf("debug mode enabled\n");
731 if (!global.fps_slowdown)
733 global.fps_slowdown = TRUE;
734 global.fps_slowdown_factor = 2;
735 printf("fps slowdown enabled -- display only every 2nd frame\n");
737 else if (global.fps_slowdown_factor == 2)
739 global.fps_slowdown_factor = 4;
740 printf("fps slowdown enabled -- display only every 4th frame\n");
744 global.fps_slowdown = FALSE;
745 global.fps_slowdown_factor = 1;
746 printf("fps slowdown disabled\n");
752 if (ScrollStepSize == TILEX/8)
753 ScrollStepSize = TILEX/4;
755 ScrollStepSize = TILEX/8;
756 printf("ScrollStepSize == %d\n", ScrollStepSize);
765 ScrollStepSize = TILEX/4;
770 ScrollStepSize = TILEX/8;
772 printf("MoveSpeed == %d\n", MoveSpeed);
777 ScrollStepSize = TILEX/8;
778 printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
782 ScrollStepSize = TILEX/4;
783 printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
787 ScrollStepSize = TILEX/2;
788 printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
792 ScrollStepSize = TILEX;
793 printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
801 for (i=0; i < MAX_INVENTORY_SIZE; i++)
802 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
803 local_player->inventory_element[local_player->inventory_size++] =
816 for(i=0; i<MAX_PLAYERS; i++)
818 printf("Player %d:\n", i);
819 printf(" jx == %d, jy == %d\n",
820 stored_player[i].jx, stored_player[i].jy);
821 printf(" last_jx == %d, last_jy == %d\n",
822 stored_player[i].last_jx, stored_player[i].last_jy);
843 if (button_status && game_status != GAME_MODE_PLAYING)
845 HandleButton(0, 0, -button_status);
849 #if defined(PLATFORM_UNIX)
857 static int HandleJoystickForAllPlayers()
862 for (i=0; i<MAX_PLAYERS; i++)
867 if (!setup.input[i].use_joystick)
871 joy_action = Joystick(i);
872 result |= joy_action;
874 if (!setup.input[i].use_joystick)
877 stored_player[i].action = joy_action;
883 void HandleJoystick()
885 int joystick = HandleJoystickForAllPlayers();
886 int keyboard = key_joystick_mapping;
887 int joy = (joystick | keyboard);
888 int left = joy & JOY_LEFT;
889 int right = joy & JOY_RIGHT;
890 int up = joy & JOY_UP;
891 int down = joy & JOY_DOWN;
892 int button = joy & JOY_BUTTON;
893 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
894 int dx = (left ? -1 : right ? 1 : 0);
895 int dy = (up ? -1 : down ? 1 : 0);
900 case GAME_MODE_LEVELS:
901 case GAME_MODE_SETUP:
903 static unsigned long joystickmove_delay = 0;
905 if (joystick && !button &&
906 !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
907 newbutton = dx = dy = 0;
909 if (game_status == GAME_MODE_MAIN)
910 HandleMainMenu(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
911 else if (game_status == GAME_MODE_LEVELS)
912 HandleChooseLevel(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
913 else if (game_status == GAME_MODE_SETUP)
914 HandleSetupScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
918 case GAME_MODE_SCORES:
919 HandleHallOfFame(0,0, dx,dy, !newbutton);
923 HandleHelpScreen(!newbutton);
926 case GAME_MODE_EDITOR:
927 HandleLevelEditorIdle();
930 case GAME_MODE_PLAYING:
931 if (tape.playing || keyboard)
932 newbutton = ((joy & JOY_BUTTON) != 0);
934 if (AllPlayersGone && newbutton)
936 CloseDoor(DOOR_CLOSE_1);
937 game_status = GAME_MODE_MAIN;