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"
26 /* values for key_status */
27 #define KEY_NOT_PRESSED FALSE
28 #define KEY_RELEASED FALSE
29 #define KEY_PRESSED TRUE
32 static boolean cursor_inside_playfield = FALSE;
33 static boolean playfield_cursor_set = FALSE;
34 static unsigned long playfield_cursor_delay = 0;
37 /* event filter especially needed for SDL event filtering due to
38 delay problems with lots of mouse motion events when mouse button
39 not pressed (X11 can handle this with 'PointerMotionHintMask') */
41 int FilterMouseMotionEvents(const Event *event)
45 /* non-motion events are directly passed to event handler functions */
46 if (event->type != EVENT_MOTIONNOTIFY)
49 motion = (MotionEvent *)event;
50 cursor_inside_playfield = (motion->x >= SX && motion->x < SX + SXSIZE &&
51 motion->y >= SY && motion->y < SY + SYSIZE);
53 if (game_status == GAME_MODE_PLAYING && playfield_cursor_set)
55 SetMouseCursor(CURSOR_DEFAULT);
56 playfield_cursor_set = FALSE;
57 DelayReached(&playfield_cursor_delay, 0);
60 /* skip mouse motion events without pressed button outside level editor */
61 if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR &&
62 game_status != GAME_MODE_PLAYING)
68 /* this is only really needed for non-SDL targets to filter unwanted events;
69 when using SDL with properly installed event filter, this function can be
70 replaced with a simple "NextEvent()" call, but it doesn't hurt either */
72 static boolean NextValidEvent(Event *event)
74 while (PendingEvent())
78 if (FilterMouseMotionEvents(event))
89 if (PendingEvent()) /* got event */
93 if (NextValidEvent(&event))
97 case EVENT_BUTTONPRESS:
98 case EVENT_BUTTONRELEASE:
99 HandleButtonEvent((ButtonEvent *) &event);
102 case EVENT_MOTIONNOTIFY:
103 HandleMotionEvent((MotionEvent *) &event);
107 case EVENT_KEYRELEASE:
108 HandleKeyEvent((KeyEvent *) &event);
112 HandleOtherEvents(&event);
119 /* when playing, display a special mouse pointer inside the playfield */
120 if (game_status == GAME_MODE_PLAYING)
122 if (!playfield_cursor_set && cursor_inside_playfield &&
123 DelayReached(&playfield_cursor_delay, 1000))
125 SetMouseCursor(CURSOR_PLAYFIELD);
126 playfield_cursor_set = TRUE;
129 else if (playfield_cursor_set)
131 SetMouseCursor(CURSOR_DEFAULT);
132 playfield_cursor_set = FALSE;
138 /* don't use all CPU time when idle; the main loop while playing
139 has its own synchronization and is CPU friendly, too */
141 if (game_status == GAME_MODE_PLAYING)
146 if (!PendingEvent()) /* delay only if no pending events */
150 /* refresh window contents from drawing buffer, if needed */
153 if (game_status == GAME_MODE_QUIT)
158 void HandleOtherEvents(Event *event)
163 HandleExposeEvent((ExposeEvent *) event);
166 case EVENT_UNMAPNOTIFY:
168 /* This causes the game to stop not only when iconified, but also
169 when on another virtual desktop, which might be not desired. */
170 SleepWhileUnmapped();
176 HandleFocusEvent((FocusChangeEvent *) event);
179 case EVENT_CLIENTMESSAGE:
180 HandleClientMessageEvent((ClientMessageEvent *) event);
183 #if defined(TARGET_SDL)
184 case SDL_JOYAXISMOTION:
185 case SDL_JOYBUTTONDOWN:
186 case SDL_JOYBUTTONUP:
187 HandleJoystickEvent(event);
196 void ClearEventQueue()
198 while (PendingEvent())
206 case EVENT_BUTTONRELEASE:
207 button_status = MB_RELEASED;
210 case EVENT_KEYRELEASE:
211 key_joystick_mapping = 0;
215 HandleOtherEvents(&event);
221 void ClearPlayerAction()
225 /* simulate key release events for still pressed keys */
226 key_joystick_mapping = 0;
227 for (i=0; i<MAX_PLAYERS; i++)
228 stored_player[i].action = 0;
231 void SleepWhileUnmapped()
233 boolean window_unmapped = TRUE;
235 KeyboardAutoRepeatOn();
237 while(window_unmapped)
245 case EVENT_BUTTONRELEASE:
246 button_status = MB_RELEASED;
249 case EVENT_KEYRELEASE:
250 key_joystick_mapping = 0;
253 case EVENT_MAPNOTIFY:
254 window_unmapped = FALSE;
257 case EVENT_UNMAPNOTIFY:
258 /* this is only to surely prevent the 'should not happen' case
259 * of recursively looping between 'SleepWhileUnmapped()' and
260 * 'HandleOtherEvents()' which usually calls this funtion.
265 HandleOtherEvents(&event);
270 if (game_status == GAME_MODE_PLAYING)
271 KeyboardAutoRepeatOffUnlessAutoplay();
274 void HandleExposeEvent(ExposeEvent *event)
277 RedrawPlayfield(FALSE, event->x, event->y, event->width, event->height);
282 void HandleButtonEvent(ButtonEvent *event)
284 motion_status = FALSE;
286 if (event->type == EVENT_BUTTONPRESS)
287 button_status = event->button;
289 button_status = MB_RELEASED;
291 HandleButton(event->x, event->y, button_status);
294 void HandleMotionEvent(MotionEvent *event)
296 if (!PointerInWindow(window))
297 return; /* window and pointer are on different screens */
300 if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR)
304 motion_status = TRUE;
306 HandleButton(event->x, event->y, button_status);
309 void HandleKeyEvent(KeyEvent *event)
311 int key_status = (event->type==EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
312 boolean with_modifiers = (game_status == GAME_MODE_PLAYING ? FALSE : TRUE);
313 Key key = GetEventKey(event, with_modifiers);
315 HandleKey(key, key_status);
318 void HandleFocusEvent(FocusChangeEvent *event)
320 static int old_joystick_status = -1;
322 if (event->type == EVENT_FOCUSOUT)
324 KeyboardAutoRepeatOn();
325 old_joystick_status = joystick.status;
326 joystick.status = JOYSTICK_NOT_AVAILABLE;
330 else if (event->type == EVENT_FOCUSIN)
332 /* When there are two Rocks'n'Diamonds windows which overlap and
333 the player moves the pointer from one game window to the other,
334 a 'FocusOut' event is generated for the window the pointer is
335 leaving and a 'FocusIn' event is generated for the window the
336 pointer is entering. In some cases, it can happen that the
337 'FocusIn' event is handled by the one game process before the
338 'FocusOut' event by the other game process. In this case the
339 X11 environment would end up with activated keyboard auto repeat,
340 because unfortunately this is a global setting and not (which
341 would be far better) set for each X11 window individually.
342 The effect would be keyboard auto repeat while playing the game
343 (game_status == GAME_MODE_PLAYING), which is not desired.
344 To avoid this special case, we just wait 1/10 second before
345 processing the 'FocusIn' event.
348 if (game_status == GAME_MODE_PLAYING)
351 KeyboardAutoRepeatOffUnlessAutoplay();
353 if (old_joystick_status != -1)
354 joystick.status = old_joystick_status;
358 void HandleClientMessageEvent(ClientMessageEvent *event)
360 if (CheckCloseWindowEvent(event))
364 void HandleButton(int mx, int my, int button)
366 static int old_mx = 0, old_my = 0;
380 HandleGadgets(mx, my, button);
385 HandleMainMenu(mx,my, 0,0, button);
388 case GAME_MODE_PSEUDO_TYPENAME:
389 HandleTypeName(0, KSYM_Return);
392 case GAME_MODE_LEVELS:
393 HandleChooseLevel(mx,my, 0,0, button);
396 case GAME_MODE_SCORES:
397 HandleHallOfFame(0,0, 0,0, button);
400 case GAME_MODE_EDITOR:
404 HandleHelpScreen(button);
407 case GAME_MODE_SETUP:
408 HandleSetupScreen(mx,my, 0,0, button);
411 case GAME_MODE_PLAYING:
413 if (button == MB_RELEASED)
415 int sx = (mx - SX) / TILEX;
416 int sy = (my - SY) / TILEY;
418 if (IN_VIS_FIELD(sx,sy))
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\n", x,y, Feld[x][y]);
429 printf(" Store[%d][%d] == %d\n", x,y, Store[x][y]);
430 printf(" Store2[%d][%d] == %d\n", x,y, Store2[x][y]);
431 printf(" StorePlayer[%d][%d] == %d\n", x,y, StorePlayer[x][y]);
432 printf(" MovPos[%d][%d] == %d\n", x,y, MovPos[x][y]);
433 printf(" MovDir[%d][%d] == %d\n", x,y, MovDir[x][y]);
434 printf(" MovDelay[%d][%d] == %d\n", x,y, MovDelay[x][y]);
435 printf(" ChangeDelay[%d][%d] == %d\n", x,y, ChangeDelay[x][y]);
436 printf(" GfxElement[%d][%d] == %d\n", x,y, GfxElement[x][y]);
448 void HandleKey(Key key, int key_status)
451 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
452 static struct SetupKeyboardInfo custom_key;
460 { &custom_key.left, DEFAULT_KEY_LEFT, JOY_LEFT },
461 { &custom_key.right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
462 { &custom_key.up, DEFAULT_KEY_UP, JOY_UP },
463 { &custom_key.down, DEFAULT_KEY_DOWN, JOY_DOWN },
464 { &custom_key.snap, DEFAULT_KEY_SNAP, JOY_BUTTON_1 },
465 { &custom_key.bomb, DEFAULT_KEY_BOMB, JOY_BUTTON_2 }
468 if (game_status == GAME_MODE_PLAYING)
470 /* only needed for single-step tape recording mode */
471 static boolean clear_button_2[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
472 static boolean bomb_placed[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
475 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
480 if (setup.input[pnr].use_joystick)
483 custom_key = setup.input[pnr].key;
486 if (key == *key_info[i].key_custom)
487 key_action |= key_info[i].action;
489 if (tape.single_step && clear_button_2[pnr])
491 stored_player[pnr].action &= ~KEY_BUTTON_2;
492 clear_button_2[pnr] = FALSE;
495 if (key_status == KEY_PRESSED)
496 stored_player[pnr].action |= key_action;
498 stored_player[pnr].action &= ~key_action;
500 if (tape.single_step && tape.recording && tape.pausing)
502 if (key_status == KEY_PRESSED &&
503 (key_action & (KEY_MOTION | KEY_BUTTON_1)))
505 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
507 if (key_action & KEY_MOTION)
509 if (stored_player[pnr].action & KEY_BUTTON_2)
510 bomb_placed[pnr] = TRUE;
513 else if (key_status == KEY_RELEASED &&
514 (key_action & KEY_BUTTON_2))
516 if (!bomb_placed[pnr])
518 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
520 stored_player[pnr].action |= KEY_BUTTON_2;
521 clear_button_2[pnr] = TRUE;
524 bomb_placed[pnr] = FALSE;
527 else if (tape.recording && tape.pausing && (key_action & KEY_ACTION))
528 TapeTogglePause(TAPE_TOGGLE_MANUAL);
536 if (key == key_info[i].key_default)
537 joy |= key_info[i].action;
542 if (key_status == KEY_PRESSED)
543 key_joystick_mapping |= joy;
545 key_joystick_mapping &= ~joy;
550 if (game_status != GAME_MODE_PLAYING)
551 key_joystick_mapping = 0;
553 if (key_status == KEY_RELEASED)
556 if ((key == KSYM_Return || key == setup.shortcut.toggle_pause) &&
557 game_status == GAME_MODE_PLAYING && AllPlayersGone)
559 CloseDoor(DOOR_CLOSE_1);
560 game_status = GAME_MODE_MAIN;
565 /* allow quick escape to the main menu with the Escape key */
566 if (key == KSYM_Escape &&
567 game_status != GAME_MODE_MAIN &&
568 game_status != GAME_MODE_PLAYING &&
569 game_status != GAME_MODE_EDITOR &&
570 game_status != GAME_MODE_LEVELS &&
571 game_status != GAME_MODE_SETUP)
573 game_status = GAME_MODE_MAIN;
578 /* special key shortcuts */
579 if (game_status == GAME_MODE_MAIN || game_status == GAME_MODE_PLAYING)
581 if (key == setup.shortcut.save_game)
583 else if (key == setup.shortcut.load_game)
585 else if (key == setup.shortcut.toggle_pause)
586 TapeTogglePause(TAPE_TOGGLE_MANUAL);
592 if (game_status == GAME_MODE_PLAYING && (tape.playing || tape.pausing))
599 HandleGadgetsKeyInput(key);
603 case GAME_MODE_PSEUDO_TYPENAME:
604 HandleTypeName(0, key);
608 case GAME_MODE_LEVELS:
609 case GAME_MODE_SETUP:
613 if (game_status == GAME_MODE_MAIN)
614 HandleMainMenu(0,0, 0,0, MB_MENU_CHOICE);
615 else if (game_status == GAME_MODE_LEVELS)
616 HandleChooseLevel(0,0, 0,0, MB_MENU_CHOICE);
617 else if (game_status == GAME_MODE_SETUP)
618 HandleSetupScreen(0,0, 0,0, MB_MENU_CHOICE);
622 if (game_status == GAME_MODE_LEVELS)
623 HandleChooseLevel(0,0, 0,0, MB_MENU_LEAVE);
624 else if (game_status == GAME_MODE_SETUP)
625 HandleSetupScreen(0,0, 0,0, MB_MENU_LEAVE);
629 if (game_status == GAME_MODE_LEVELS)
630 HandleChooseLevel(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
631 else if (game_status == GAME_MODE_SETUP)
632 HandleSetupScreen(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
636 if (game_status == GAME_MODE_LEVELS)
637 HandleChooseLevel(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
638 else if (game_status == GAME_MODE_SETUP)
639 HandleSetupScreen(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
654 HandleHelpScreen(MB_RELEASED);
657 case GAME_MODE_SCORES:
661 game_status = GAME_MODE_MAIN;
667 HandleHallOfFame(0,0, 0, -1 * SCROLL_PAGE, MB_MENU_MARK);
671 HandleHallOfFame(0,0, 0, +1 * SCROLL_PAGE, MB_MENU_MARK);
679 case GAME_MODE_EDITOR:
680 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
681 HandleLevelEditorKeyInput(key);
684 case GAME_MODE_PLAYING:
689 RequestQuitGame(setup.ask_on_escape);
705 if (GameFrameDelay == 500)
706 GameFrameDelay = GAME_FRAME_DELAY;
708 GameFrameDelay = 500;
711 GameFrameDelay = (key - KSYM_0) * 10;
712 printf("Game speed == %d%% (%d ms delay between two frames)\n",
713 GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
719 options.debug = FALSE;
720 printf("debug mode disabled\n");
724 options.debug = TRUE;
725 printf("debug mode enabled\n");
730 if (!global.fps_slowdown)
732 global.fps_slowdown = TRUE;
733 global.fps_slowdown_factor = 2;
734 printf("fps slowdown enabled -- display only every 2nd frame\n");
736 else if (global.fps_slowdown_factor == 2)
738 global.fps_slowdown_factor = 4;
739 printf("fps slowdown enabled -- display only every 4th frame\n");
743 global.fps_slowdown = FALSE;
744 global.fps_slowdown_factor = 1;
745 printf("fps slowdown disabled\n");
751 if (ScrollStepSize == TILEX/8)
752 ScrollStepSize = TILEX/4;
754 ScrollStepSize = TILEX/8;
755 printf("ScrollStepSize == %d\n", ScrollStepSize);
764 ScrollStepSize = TILEX/4;
769 ScrollStepSize = TILEX/8;
771 printf("MoveSpeed == %d\n", MoveSpeed);
776 ScrollStepSize = TILEX/8;
777 printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
781 ScrollStepSize = TILEX/4;
782 printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
786 ScrollStepSize = TILEX/2;
787 printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
791 ScrollStepSize = TILEX;
792 printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
797 local_player->dynamite = 1000;
808 for(i=0; i<MAX_PLAYERS; i++)
810 printf("Player %d:\n", i);
811 printf(" jx == %d, jy == %d\n",
812 stored_player[i].jx, stored_player[i].jy);
813 printf(" last_jx == %d, last_jy == %d\n",
814 stored_player[i].last_jx, stored_player[i].last_jy);
835 if (button_status && game_status != GAME_MODE_PLAYING)
837 HandleButton(0, 0, -button_status);
841 #if defined(PLATFORM_UNIX)
849 static int HandleJoystickForAllPlayers()
854 for (i=0; i<MAX_PLAYERS; i++)
859 if (!setup.input[i].use_joystick)
863 joy_action = Joystick(i);
864 result |= joy_action;
866 if (!setup.input[i].use_joystick)
869 stored_player[i].action = joy_action;
875 void HandleJoystick()
877 int joystick = HandleJoystickForAllPlayers();
878 int keyboard = key_joystick_mapping;
879 int joy = (joystick | keyboard);
880 int left = joy & JOY_LEFT;
881 int right = joy & JOY_RIGHT;
882 int up = joy & JOY_UP;
883 int down = joy & JOY_DOWN;
884 int button = joy & JOY_BUTTON;
885 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
886 int dx = (left ? -1 : right ? 1 : 0);
887 int dy = (up ? -1 : down ? 1 : 0);
892 case GAME_MODE_LEVELS:
893 case GAME_MODE_SETUP:
895 static unsigned long joystickmove_delay = 0;
897 if (joystick && !button &&
898 !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
899 newbutton = dx = dy = 0;
901 if (game_status == GAME_MODE_MAIN)
902 HandleMainMenu(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
903 else if (game_status == GAME_MODE_LEVELS)
904 HandleChooseLevel(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
905 else if (game_status == GAME_MODE_SETUP)
906 HandleSetupScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
910 case GAME_MODE_SCORES:
911 HandleHallOfFame(0,0, dx,dy, !newbutton);
915 HandleHelpScreen(!newbutton);
918 case GAME_MODE_EDITOR:
919 HandleLevelEditorIdle();
922 case GAME_MODE_PLAYING:
923 if (tape.playing || keyboard)
924 newbutton = ((joy & JOY_BUTTON) != 0);
926 if (AllPlayersGone && newbutton)
928 CloseDoor(DOOR_CLOSE_1);
929 game_status = GAME_MODE_MAIN;