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 == 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 != LEVELED)
67 /* this is only really needed for non-SDL targets to filter unwanted events;
68 when using SDL with properly installed event filter, this function can be
69 replaced with a simple "NextEvent()" call, but it doesn't hurt either */
71 static boolean NextValidEvent(Event *event)
73 while (PendingEvent())
77 if (FilterMouseMotionEvents(event))
88 if (PendingEvent()) /* got event */
92 if (NextValidEvent(&event))
96 case EVENT_BUTTONPRESS:
97 case EVENT_BUTTONRELEASE:
98 HandleButtonEvent((ButtonEvent *) &event);
101 case EVENT_MOTIONNOTIFY:
102 HandleMotionEvent((MotionEvent *) &event);
106 case EVENT_KEYRELEASE:
107 HandleKeyEvent((KeyEvent *) &event);
111 HandleOtherEvents(&event);
118 /* when playing, display a special mouse pointer inside the playfield */
119 if (game_status == PLAYING)
121 if (!playfield_cursor_set && cursor_inside_playfield &&
122 DelayReached(&playfield_cursor_delay, 1000))
124 SetMouseCursor(CURSOR_PLAYFIELD);
125 playfield_cursor_set = TRUE;
128 else if (playfield_cursor_set)
130 SetMouseCursor(CURSOR_DEFAULT);
131 playfield_cursor_set = FALSE;
137 /* don't use all CPU time when idle; the main loop while playing
138 has its own synchronization and is CPU friendly, too */
140 if (game_status == PLAYING)
145 if (!PendingEvent()) /* delay only if no pending events */
149 /* refresh window contents from drawing buffer, if needed */
152 if (game_status == EXITGAME)
157 void HandleOtherEvents(Event *event)
162 HandleExposeEvent((ExposeEvent *) event);
165 case EVENT_UNMAPNOTIFY:
167 /* This causes the game to stop not only when iconified, but also
168 when on another virtual desktop, which might be not desired. */
169 SleepWhileUnmapped();
175 HandleFocusEvent((FocusChangeEvent *) event);
178 case EVENT_CLIENTMESSAGE:
179 HandleClientMessageEvent((ClientMessageEvent *) event);
182 #if defined(TARGET_SDL)
183 case SDL_JOYAXISMOTION:
184 case SDL_JOYBUTTONDOWN:
185 case SDL_JOYBUTTONUP:
186 HandleJoystickEvent(event);
195 void ClearEventQueue()
197 while (PendingEvent())
205 case EVENT_BUTTONRELEASE:
206 button_status = MB_RELEASED;
209 case EVENT_KEYRELEASE:
210 key_joystick_mapping = 0;
214 HandleOtherEvents(&event);
220 void ClearPlayerAction()
224 /* simulate key release events for still pressed keys */
225 key_joystick_mapping = 0;
226 for (i=0; i<MAX_PLAYERS; i++)
227 stored_player[i].action = 0;
230 void SleepWhileUnmapped()
232 boolean window_unmapped = TRUE;
234 KeyboardAutoRepeatOn();
236 while(window_unmapped)
244 case EVENT_BUTTONRELEASE:
245 button_status = MB_RELEASED;
248 case EVENT_KEYRELEASE:
249 key_joystick_mapping = 0;
252 case EVENT_MAPNOTIFY:
253 window_unmapped = FALSE;
256 case EVENT_UNMAPNOTIFY:
257 /* this is only to surely prevent the 'should not happen' case
258 * of recursively looping between 'SleepWhileUnmapped()' and
259 * 'HandleOtherEvents()' which usually calls this funtion.
264 HandleOtherEvents(&event);
269 if (game_status == PLAYING)
270 KeyboardAutoRepeatOff();
273 void HandleExposeEvent(ExposeEvent *event)
276 RedrawPlayfield(FALSE, event->x, event->y, event->width, event->height);
281 void HandleButtonEvent(ButtonEvent *event)
283 motion_status = FALSE;
285 if (event->type == EVENT_BUTTONPRESS)
286 button_status = event->button;
288 button_status = MB_RELEASED;
290 HandleButton(event->x, event->y, button_status);
293 void HandleMotionEvent(MotionEvent *event)
295 if (!PointerInWindow(window))
296 return; /* window and pointer are on different screens */
299 if (button_status == MB_RELEASED && game_status != LEVELED)
303 motion_status = TRUE;
305 HandleButton(event->x, event->y, button_status);
308 void HandleKeyEvent(KeyEvent *event)
310 int key_status = (event->type==EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
311 boolean with_modifiers = (game_status == PLAYING ? FALSE : TRUE);
312 Key key = GetEventKey(event, with_modifiers);
314 HandleKey(key, key_status);
317 void HandleFocusEvent(FocusChangeEvent *event)
319 static int old_joystick_status = -1;
321 if (event->type == EVENT_FOCUSOUT)
323 KeyboardAutoRepeatOn();
324 old_joystick_status = joystick.status;
325 joystick.status = JOYSTICK_NOT_AVAILABLE;
329 else if (event->type == EVENT_FOCUSIN)
331 /* When there are two Rocks'n'Diamonds windows which overlap and
332 the player moves the pointer from one game window to the other,
333 a 'FocusOut' event is generated for the window the pointer is
334 leaving and a 'FocusIn' event is generated for the window the
335 pointer is entering. In some cases, it can happen that the
336 'FocusIn' event is handled by the one game process before the
337 'FocusOut' event by the other game process. In this case the
338 X11 environment would end up with activated keyboard auto repeat,
339 because unfortunately this is a global setting and not (which
340 would be far better) set for each X11 window individually.
341 The effect would be keyboard auto repeat while playing the game
342 (game_status == PLAYING), which is not desired.
343 To avoid this special case, we just wait 1/10 second before
344 processing the 'FocusIn' event.
347 if (game_status == PLAYING)
350 KeyboardAutoRepeatOff();
352 if (old_joystick_status != -1)
353 joystick.status = old_joystick_status;
357 void HandleClientMessageEvent(ClientMessageEvent *event)
359 if (CheckCloseWindowEvent(event))
363 void HandleButton(int mx, int my, int button)
365 static int old_mx = 0, old_my = 0;
379 HandleGadgets(mx, my, button);
384 HandleMainMenu(mx,my, 0,0, button);
388 HandleTypeName(0, KSYM_Return);
392 HandleChooseLevel(mx,my, 0,0, button);
396 HandleHallOfFame(0,0, 0,0, button);
403 HandleHelpScreen(button);
407 HandleSetupScreen(mx,my, 0,0, button);
412 if (button == MB_RELEASED)
414 int sx = (mx - SX) / TILEX;
415 int sy = (my - SY) / TILEY;
417 if (IN_VIS_FIELD(sx,sy))
422 printf("INFO: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
424 if (!IN_LEV_FIELD(x, y))
427 printf(" Feld[%d][%d] == %d\n", x,y, Feld[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(" GfxElement[%d][%d] == %d\n", x,y, GfxElement[x][y]);
446 void HandleKey(Key key, int key_status)
449 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
450 static struct SetupKeyboardInfo custom_key;
458 { &custom_key.left, DEFAULT_KEY_LEFT, JOY_LEFT },
459 { &custom_key.right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
460 { &custom_key.up, DEFAULT_KEY_UP, JOY_UP },
461 { &custom_key.down, DEFAULT_KEY_DOWN, JOY_DOWN },
462 { &custom_key.snap, DEFAULT_KEY_SNAP, JOY_BUTTON_1 },
463 { &custom_key.bomb, DEFAULT_KEY_BOMB, JOY_BUTTON_2 }
466 if (game_status == PLAYING)
468 /* only needed for single-step tape recording mode */
469 static boolean clear_button_2[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
470 static boolean bomb_placed[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
473 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
478 if (setup.input[pnr].use_joystick)
481 custom_key = setup.input[pnr].key;
484 if (key == *key_info[i].key_custom)
485 key_action |= key_info[i].action;
487 if (tape.single_step && clear_button_2[pnr])
489 stored_player[pnr].action &= ~KEY_BUTTON_2;
490 clear_button_2[pnr] = FALSE;
493 if (key_status == KEY_PRESSED)
494 stored_player[pnr].action |= key_action;
496 stored_player[pnr].action &= ~key_action;
498 if (tape.single_step && tape.recording && tape.pausing)
500 if (key_status == KEY_PRESSED &&
501 (key_action & (KEY_MOTION | KEY_BUTTON_1)))
503 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
505 if (key_action & KEY_MOTION)
507 if (stored_player[pnr].action & KEY_BUTTON_2)
508 bomb_placed[pnr] = TRUE;
511 else if (key_status == KEY_RELEASED &&
512 (key_action & KEY_BUTTON_2))
514 if (!bomb_placed[pnr])
516 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
518 stored_player[pnr].action |= KEY_BUTTON_2;
519 clear_button_2[pnr] = TRUE;
522 bomb_placed[pnr] = FALSE;
525 else if (tape.recording && tape.pausing && (key_action & KEY_ACTION))
526 TapeTogglePause(TAPE_TOGGLE_MANUAL);
534 if (key == key_info[i].key_default)
535 joy |= key_info[i].action;
540 if (key_status == KEY_PRESSED)
541 key_joystick_mapping |= joy;
543 key_joystick_mapping &= ~joy;
548 if (game_status != PLAYING)
549 key_joystick_mapping = 0;
551 if (key_status == KEY_RELEASED)
554 if ((key == KSYM_Return || key == setup.shortcut.toggle_pause) &&
555 game_status == PLAYING && AllPlayersGone)
557 CloseDoor(DOOR_CLOSE_1);
558 game_status = MAINMENU;
563 /* allow quick escape to the main menu with the Escape key */
564 if (key == KSYM_Escape &&
565 game_status != MAINMENU &&
566 game_status != PLAYING &&
567 game_status != LEVELED &&
568 game_status != CHOOSELEVEL &&
569 game_status != SETUP)
571 game_status = MAINMENU;
576 /* special key shortcuts */
577 if (game_status == MAINMENU || game_status == PLAYING)
579 if (key == setup.shortcut.save_game)
581 else if (key == setup.shortcut.load_game)
583 else if (key == setup.shortcut.toggle_pause)
584 TapeTogglePause(TAPE_TOGGLE_MANUAL);
590 if (game_status == PLAYING && (tape.playing || tape.pausing))
597 HandleGadgetsKeyInput(key);
602 HandleTypeName(0, key);
611 if (game_status == MAINMENU)
612 HandleMainMenu(0,0, 0,0, MB_MENU_CHOICE);
613 else if (game_status == CHOOSELEVEL)
614 HandleChooseLevel(0,0, 0,0, MB_MENU_CHOICE);
615 else if (game_status == SETUP)
616 HandleSetupScreen(0,0, 0,0, MB_MENU_CHOICE);
620 if (game_status == CHOOSELEVEL)
621 HandleChooseLevel(0,0, 0,0, MB_MENU_LEAVE);
622 else if (game_status == SETUP)
623 HandleSetupScreen(0,0, 0,0, MB_MENU_LEAVE);
627 if (game_status == CHOOSELEVEL)
628 HandleChooseLevel(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
629 else if (game_status == SETUP)
630 HandleSetupScreen(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
634 if (game_status == CHOOSELEVEL)
635 HandleChooseLevel(0,0, 0,SCR_FIELDY, MB_MENU_MARK);
636 else if (game_status == SETUP)
637 HandleSetupScreen(0,0, 0,SCR_FIELDY, MB_MENU_MARK);
652 HandleHelpScreen(MB_RELEASED);
659 game_status = MAINMENU;
665 HandleHallOfFame(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
669 HandleHallOfFame(0,0, 0,SCR_FIELDY, MB_MENU_MARK);
678 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
679 HandleLevelEditorKeyInput(key);
687 RequestQuitGame(setup.ask_on_escape);
703 if (GameFrameDelay == 500)
704 GameFrameDelay = GAME_FRAME_DELAY;
706 GameFrameDelay = 500;
709 GameFrameDelay = (key - KSYM_0) * 10;
710 printf("Game speed == %d%% (%d ms delay between two frames)\n",
711 GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
717 options.debug = FALSE;
718 printf("debug mode disabled\n");
722 options.debug = TRUE;
723 printf("debug mode enabled\n");
728 if (!global.fps_slowdown)
730 global.fps_slowdown = TRUE;
731 global.fps_slowdown_factor = 2;
732 printf("fps slowdown enabled -- display only every 2nd frame\n");
734 else if (global.fps_slowdown_factor == 2)
736 global.fps_slowdown_factor = 4;
737 printf("fps slowdown enabled -- display only every 4th frame\n");
741 global.fps_slowdown = FALSE;
742 global.fps_slowdown_factor = 1;
743 printf("fps slowdown disabled\n");
749 if (ScrollStepSize == TILEX/8)
750 ScrollStepSize = TILEX/4;
752 ScrollStepSize = TILEX/8;
753 printf("ScrollStepSize == %d\n", ScrollStepSize);
762 ScrollStepSize = TILEX/4;
767 ScrollStepSize = TILEX/8;
769 printf("MoveSpeed == %d\n", MoveSpeed);
774 ScrollStepSize = TILEX/8;
775 printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
779 ScrollStepSize = TILEX/4;
780 printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
784 ScrollStepSize = TILEX/2;
785 printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
789 ScrollStepSize = TILEX;
790 printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
795 local_player->dynamite = 1000;
806 for(i=0; i<MAX_PLAYERS; i++)
808 printf("Player %d:\n", i);
809 printf(" jx == %d, jy == %d\n",
810 stored_player[i].jx, stored_player[i].jy);
811 printf(" last_jx == %d, last_jy == %d\n",
812 stored_player[i].last_jx, stored_player[i].last_jy);
833 if (button_status && game_status != PLAYING)
835 HandleButton(0, 0, -button_status);
839 #if defined(PLATFORM_UNIX)
847 static int HandleJoystickForAllPlayers()
852 for (i=0; i<MAX_PLAYERS; i++)
857 if (!setup.input[i].use_joystick)
861 joy_action = Joystick(i);
862 result |= joy_action;
864 if (!setup.input[i].use_joystick)
867 stored_player[i].action = joy_action;
873 void HandleJoystick()
875 int joystick = HandleJoystickForAllPlayers();
876 int keyboard = key_joystick_mapping;
877 int joy = (joystick | keyboard);
878 int left = joy & JOY_LEFT;
879 int right = joy & JOY_RIGHT;
880 int up = joy & JOY_UP;
881 int down = joy & JOY_DOWN;
882 int button = joy & JOY_BUTTON;
883 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
884 int dx = (left ? -1 : right ? 1 : 0);
885 int dy = (up ? -1 : down ? 1 : 0);
893 static unsigned long joystickmove_delay = 0;
895 if (joystick && !button &&
896 !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
897 newbutton = dx = dy = 0;
899 if (game_status == MAINMENU)
900 HandleMainMenu(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
901 else if (game_status == CHOOSELEVEL)
902 HandleChooseLevel(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
903 else if (game_status == SETUP)
904 HandleSetupScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
909 HandleHallOfFame(0,0, dx,dy, !newbutton);
913 HandleHelpScreen(!newbutton);
917 HandleLevelEditorIdle();
921 if (tape.playing || keyboard)
922 newbutton = ((joy & JOY_BUTTON) != 0);
924 if (AllPlayersGone && newbutton)
926 CloseDoor(DOOR_CLOSE_1);
927 game_status = MAINMENU;