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)
43 /* non-motion events are directly passed to event handler functions */
44 if (event->type != EVENT_MOTIONNOTIFY)
47 if (game_status == PLAYING)
49 MotionEvent *motion = (MotionEvent *)event;
51 cursor_inside_playfield =
52 (motion->x >= SX && motion->x < SX + SXSIZE &&
53 motion->y >= SY && motion->y < SY + SYSIZE);
55 if (playfield_cursor_set)
57 SetMouseCursor(CURSOR_DEFAULT);
58 DelayReached(&playfield_cursor_delay, 0);
59 playfield_cursor_set = FALSE;
63 /* skip mouse motion events without pressed button outside level editor */
64 if (button_status == MB_RELEASED && game_status != LEVELED)
70 /* this is only really needed for non-SDL targets to filter unwanted events;
71 when using SDL with properly installed event filter, this function can be
72 replaced with a simple "NextEvent()" call, but it doesn't hurt either */
74 static boolean NextValidEvent(Event *event)
76 while (PendingEvent())
80 if (FilterMouseMotionEvents(event))
91 if (PendingEvent()) /* got event */
95 if (NextValidEvent(&event))
99 case EVENT_BUTTONPRESS:
100 case EVENT_BUTTONRELEASE:
101 HandleButtonEvent((ButtonEvent *) &event);
104 case EVENT_MOTIONNOTIFY:
105 HandleMotionEvent((MotionEvent *) &event);
109 case EVENT_KEYRELEASE:
110 HandleKeyEvent((KeyEvent *) &event);
114 HandleOtherEvents(&event);
121 /* when playing, display a special mouse pointer inside the playfield */
122 if (game_status == PLAYING && cursor_inside_playfield &&
123 DelayReached(&playfield_cursor_delay, 1000))
125 SetMouseCursor(CURSOR_PLAYFIELD);
126 playfield_cursor_set = TRUE;
132 /* don't use all CPU time when idle; the main loop while playing
133 has its own synchronization and is CPU friendly, too */
135 if (game_status == PLAYING)
140 if (!PendingEvent()) /* delay only if no pending events */
144 /* refresh window contents from drawing buffer, if needed */
147 if (game_status == EXITGAME)
152 void HandleOtherEvents(Event *event)
157 HandleExposeEvent((ExposeEvent *) event);
160 case EVENT_UNMAPNOTIFY:
162 /* This causes the game to stop not only when iconified, but also
163 when on another virtual desktop, which might be not desired. */
164 SleepWhileUnmapped();
170 HandleFocusEvent((FocusChangeEvent *) event);
173 case EVENT_CLIENTMESSAGE:
174 HandleClientMessageEvent((ClientMessageEvent *) event);
177 #if defined(TARGET_SDL)
178 case SDL_JOYAXISMOTION:
179 case SDL_JOYBUTTONDOWN:
180 case SDL_JOYBUTTONUP:
181 HandleJoystickEvent(event);
190 void ClearEventQueue()
192 while (PendingEvent())
200 case EVENT_BUTTONRELEASE:
201 button_status = MB_RELEASED;
204 case EVENT_KEYRELEASE:
205 key_joystick_mapping = 0;
209 HandleOtherEvents(&event);
215 void ClearPlayerAction()
219 /* simulate key release events for still pressed keys */
220 key_joystick_mapping = 0;
221 for (i=0; i<MAX_PLAYERS; i++)
222 stored_player[i].action = 0;
225 void SleepWhileUnmapped()
227 boolean window_unmapped = TRUE;
229 KeyboardAutoRepeatOn();
231 while(window_unmapped)
239 case EVENT_BUTTONRELEASE:
240 button_status = MB_RELEASED;
243 case EVENT_KEYRELEASE:
244 key_joystick_mapping = 0;
247 case EVENT_MAPNOTIFY:
248 window_unmapped = FALSE;
251 case EVENT_UNMAPNOTIFY:
252 /* this is only to surely prevent the 'should not happen' case
253 * of recursively looping between 'SleepWhileUnmapped()' and
254 * 'HandleOtherEvents()' which usually calls this funtion.
259 HandleOtherEvents(&event);
264 if (game_status == PLAYING)
265 KeyboardAutoRepeatOff();
268 void HandleExposeEvent(ExposeEvent *event)
271 RedrawPlayfield(FALSE, event->x, event->y, event->width, event->height);
276 void HandleButtonEvent(ButtonEvent *event)
278 motion_status = FALSE;
280 if (event->type == EVENT_BUTTONPRESS)
281 button_status = event->button;
283 button_status = MB_RELEASED;
285 HandleButton(event->x, event->y, button_status);
288 void HandleMotionEvent(MotionEvent *event)
290 if (!PointerInWindow(window))
291 return; /* window and pointer are on different screens */
294 if (button_status == MB_RELEASED && game_status != LEVELED)
298 motion_status = TRUE;
300 HandleButton(event->x, event->y, button_status);
303 void HandleKeyEvent(KeyEvent *event)
305 int key_status = (event->type==EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
306 boolean with_modifiers = (game_status == PLAYING ? FALSE : TRUE);
307 Key key = GetEventKey(event, with_modifiers);
309 HandleKey(key, key_status);
312 void HandleFocusEvent(FocusChangeEvent *event)
314 static int old_joystick_status = -1;
316 if (event->type == EVENT_FOCUSOUT)
318 KeyboardAutoRepeatOn();
319 old_joystick_status = joystick.status;
320 joystick.status = JOYSTICK_NOT_AVAILABLE;
324 else if (event->type == EVENT_FOCUSIN)
326 /* When there are two Rocks'n'Diamonds windows which overlap and
327 the player moves the pointer from one game window to the other,
328 a 'FocusOut' event is generated for the window the pointer is
329 leaving and a 'FocusIn' event is generated for the window the
330 pointer is entering. In some cases, it can happen that the
331 'FocusIn' event is handled by the one game process before the
332 'FocusOut' event by the other game process. In this case the
333 X11 environment would end up with activated keyboard auto repeat,
334 because unfortunately this is a global setting and not (which
335 would be far better) set for each X11 window individually.
336 The effect would be keyboard auto repeat while playing the game
337 (game_status == PLAYING), which is not desired.
338 To avoid this special case, we just wait 1/10 second before
339 processing the 'FocusIn' event.
342 if (game_status == PLAYING)
345 KeyboardAutoRepeatOff();
347 if (old_joystick_status != -1)
348 joystick.status = old_joystick_status;
352 void HandleClientMessageEvent(ClientMessageEvent *event)
354 if (CheckCloseWindowEvent(event))
358 void HandleButton(int mx, int my, int button)
360 static int old_mx = 0, old_my = 0;
374 HandleGadgets(mx, my, button);
379 HandleMainMenu(mx,my, 0,0, button);
383 HandleTypeName(0, KSYM_Return);
387 HandleChooseLevel(mx,my, 0,0, button);
391 HandleHallOfFame(0,0, 0,0, button);
398 HandleHelpScreen(button);
402 HandleSetupScreen(mx,my, 0,0, button);
407 if (button == MB_RELEASED)
409 int sx = (mx - SX) / TILEX;
410 int sy = (my - SY) / TILEY;
412 if (IN_VIS_FIELD(sx,sy))
417 printf("INFO: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
419 if (!IN_LEV_FIELD(x, y))
422 printf(" Feld[%d][%d] == %d\n", x,y, Feld[x][y]);
423 printf(" Store[%d][%d] == %d\n", x,y, Store[x][y]);
424 printf(" Store2[%d][%d] == %d\n", x,y, Store2[x][y]);
425 printf(" StorePlayer[%d][%d] == %d\n", x,y, StorePlayer[x][y]);
426 printf(" MovPos[%d][%d] == %d\n", x,y, MovPos[x][y]);
427 printf(" MovDir[%d][%d] == %d\n", x,y, MovDir[x][y]);
428 printf(" MovDelay[%d][%d] == %d\n", x,y, MovDelay[x][y]);
440 void HandleKey(Key key, int key_status)
443 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
444 static struct SetupKeyboardInfo custom_key;
452 { &custom_key.left, DEFAULT_KEY_LEFT, JOY_LEFT },
453 { &custom_key.right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
454 { &custom_key.up, DEFAULT_KEY_UP, JOY_UP },
455 { &custom_key.down, DEFAULT_KEY_DOWN, JOY_DOWN },
456 { &custom_key.snap, DEFAULT_KEY_SNAP, JOY_BUTTON_1 },
457 { &custom_key.bomb, DEFAULT_KEY_BOMB, JOY_BUTTON_2 }
460 if (game_status == PLAYING)
462 /* only needed for single-step tape recording mode */
463 static boolean clear_button_2[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
464 static boolean bomb_placed[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
467 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
472 if (setup.input[pnr].use_joystick)
475 custom_key = setup.input[pnr].key;
478 if (key == *key_info[i].key_custom)
479 key_action |= key_info[i].action;
481 if (tape.single_step && clear_button_2[pnr])
483 stored_player[pnr].action &= ~KEY_BUTTON_2;
484 clear_button_2[pnr] = FALSE;
487 if (key_status == KEY_PRESSED)
488 stored_player[pnr].action |= key_action;
490 stored_player[pnr].action &= ~key_action;
492 if (tape.single_step && tape.recording && tape.pausing)
494 if (key_status == KEY_PRESSED &&
495 (key_action & (KEY_MOTION | KEY_BUTTON_1)))
497 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
499 if (key_action & KEY_MOTION)
501 if (stored_player[pnr].action & KEY_BUTTON_2)
502 bomb_placed[pnr] = TRUE;
505 else if (key_status == KEY_RELEASED &&
506 (key_action & KEY_BUTTON_2))
508 if (!bomb_placed[pnr])
510 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
512 stored_player[pnr].action |= KEY_BUTTON_2;
513 clear_button_2[pnr] = TRUE;
516 bomb_placed[pnr] = FALSE;
519 else if (tape.recording && tape.pausing && (key_action & KEY_ACTION))
520 TapeTogglePause(TAPE_TOGGLE_MANUAL);
528 if (key == key_info[i].key_default)
529 joy |= key_info[i].action;
534 if (key_status == KEY_PRESSED)
535 key_joystick_mapping |= joy;
537 key_joystick_mapping &= ~joy;
542 if (game_status != PLAYING)
543 key_joystick_mapping = 0;
545 if (key_status == KEY_RELEASED)
548 if ((key == KSYM_Return || key == setup.shortcut.toggle_pause) &&
549 game_status == PLAYING && AllPlayersGone)
551 CloseDoor(DOOR_CLOSE_1);
552 game_status = MAINMENU;
557 /* allow quick escape to the main menu with the Escape key */
558 if (key == KSYM_Escape &&
559 game_status != MAINMENU &&
560 game_status != PLAYING &&
561 game_status != LEVELED &&
562 game_status != CHOOSELEVEL &&
563 game_status != SETUP)
565 game_status = MAINMENU;
570 /* special key shortcuts */
571 if (game_status == MAINMENU || game_status == PLAYING)
573 if (key == setup.shortcut.save_game)
575 else if (key == setup.shortcut.load_game)
577 else if (key == setup.shortcut.toggle_pause)
578 TapeTogglePause(TAPE_TOGGLE_MANUAL);
584 if (game_status == PLAYING && (tape.playing || tape.pausing))
591 HandleGadgetsKeyInput(key);
596 HandleTypeName(0, key);
605 if (game_status == MAINMENU)
606 HandleMainMenu(0,0, 0,0, MB_MENU_CHOICE);
607 else if (game_status == CHOOSELEVEL)
608 HandleChooseLevel(0,0, 0,0, MB_MENU_CHOICE);
609 else if (game_status == SETUP)
610 HandleSetupScreen(0,0, 0,0, MB_MENU_CHOICE);
614 if (game_status == CHOOSELEVEL)
615 HandleChooseLevel(0,0, 0,0, MB_MENU_LEAVE);
616 else if (game_status == SETUP)
617 HandleSetupScreen(0,0, 0,0, MB_MENU_LEAVE);
621 if (game_status == CHOOSELEVEL)
622 HandleChooseLevel(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
623 else if (game_status == SETUP)
624 HandleSetupScreen(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
628 if (game_status == CHOOSELEVEL)
629 HandleChooseLevel(0,0, 0,SCR_FIELDY, MB_MENU_MARK);
630 else if (game_status == SETUP)
631 HandleSetupScreen(0,0, 0,SCR_FIELDY, MB_MENU_MARK);
646 HandleHelpScreen(MB_RELEASED);
653 game_status = MAINMENU;
659 HandleHallOfFame(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
663 HandleHallOfFame(0,0, 0,SCR_FIELDY, MB_MENU_MARK);
672 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
673 HandleLevelEditorKeyInput(key);
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);
789 local_player->dynamite = 1000;
800 for(i=0; i<MAX_PLAYERS; i++)
802 printf("Player %d:\n", i);
803 printf(" jx == %d, jy == %d\n",
804 stored_player[i].jx, stored_player[i].jy);
805 printf(" last_jx == %d, last_jy == %d\n",
806 stored_player[i].last_jx, stored_player[i].last_jy);
827 if (button_status && game_status != PLAYING)
829 HandleButton(0, 0, -button_status);
833 #if defined(PLATFORM_UNIX)
841 static int HandleJoystickForAllPlayers()
846 for (i=0; i<MAX_PLAYERS; i++)
851 if (!setup.input[i].use_joystick)
855 joy_action = Joystick(i);
856 result |= joy_action;
858 if (!setup.input[i].use_joystick)
861 stored_player[i].action = joy_action;
867 void HandleJoystick()
869 int joystick = HandleJoystickForAllPlayers();
870 int keyboard = key_joystick_mapping;
871 int joy = (joystick | keyboard);
872 int left = joy & JOY_LEFT;
873 int right = joy & JOY_RIGHT;
874 int up = joy & JOY_UP;
875 int down = joy & JOY_DOWN;
876 int button = joy & JOY_BUTTON;
877 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
878 int dx = (left ? -1 : right ? 1 : 0);
879 int dy = (up ? -1 : down ? 1 : 0);
887 static unsigned long joystickmove_delay = 0;
889 if (joystick && !button &&
890 !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
891 newbutton = dx = dy = 0;
893 if (game_status == MAINMENU)
894 HandleMainMenu(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
895 else if (game_status == CHOOSELEVEL)
896 HandleChooseLevel(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
897 else if (game_status == SETUP)
898 HandleSetupScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
903 HandleHallOfFame(0,0, dx,dy, !newbutton);
907 HandleHelpScreen(!newbutton);
911 HandleLevelEditorIdle();
915 if (tape.playing || keyboard)
916 newbutton = ((joy & JOY_BUTTON) != 0);
918 if (AllPlayersGone && newbutton)
920 CloseDoor(DOOR_CLOSE_1);
921 game_status = MAINMENU;