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 /* 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)
38 /* non-motion events are directly passed to event handler functions */
39 if (event->type != EVENT_MOTIONNOTIFY)
42 /* when playing, display a different mouse pointer inside the playfield */
43 if (game_status == PLAYING)
45 static boolean inside_field = FALSE;
46 MotionEvent *motion = (MotionEvent *)event;
48 if ((motion->x >= SX && motion->x < SX + SXSIZE &&
49 motion->y >= SY && motion->y < SY + SYSIZE) != inside_field)
51 inside_field = !inside_field;
53 SetMouseCursor(inside_field ? CURSOR_PLAYFIELD : CURSOR_DEFAULT);
57 /* skip mouse motion events without pressed button outside level editor */
58 if (button_status == MB_RELEASED && game_status != LEVELED)
64 /* this is only really needed for non-SDL targets to filter unwanted events;
65 when using SDL with properly installed event filter, this function can be
66 replaced with a simple "NextEvent()" call, but it doesn't hurt either */
68 static boolean NextValidEvent(Event *event)
70 while (PendingEvent())
74 if (FilterMouseMotionEvents(event))
85 if (PendingEvent()) /* got event */
89 if (NextValidEvent(&event))
93 case EVENT_BUTTONPRESS:
94 case EVENT_BUTTONRELEASE:
95 HandleButtonEvent((ButtonEvent *) &event);
98 case EVENT_MOTIONNOTIFY:
99 HandleMotionEvent((MotionEvent *) &event);
103 case EVENT_KEYRELEASE:
104 HandleKeyEvent((KeyEvent *) &event);
108 HandleOtherEvents(&event);
116 /* don't use all CPU time when idle; the main loop while playing
117 has its own synchronization and is CPU friendly, too */
119 if (game_status == PLAYING)
124 if (!PendingEvent()) /* delay only if no pending events */
128 /* refresh window contents from drawing buffer, if needed */
131 if (game_status == EXITGAME)
136 void HandleOtherEvents(Event *event)
141 HandleExposeEvent((ExposeEvent *) event);
144 case EVENT_UNMAPNOTIFY:
146 /* This causes the game to stop not only when iconified, but also
147 when on another virtual desktop, which might be not desired. */
148 SleepWhileUnmapped();
154 HandleFocusEvent((FocusChangeEvent *) event);
157 case EVENT_CLIENTMESSAGE:
158 HandleClientMessageEvent((ClientMessageEvent *) event);
161 #if defined(TARGET_SDL)
162 case SDL_JOYAXISMOTION:
163 case SDL_JOYBUTTONDOWN:
164 case SDL_JOYBUTTONUP:
165 HandleJoystickEvent(event);
174 void ClearEventQueue()
176 while (PendingEvent())
184 case EVENT_BUTTONRELEASE:
185 button_status = MB_RELEASED;
188 case EVENT_KEYRELEASE:
189 key_joystick_mapping = 0;
193 HandleOtherEvents(&event);
199 void ClearPlayerAction()
203 /* simulate key release events for still pressed keys */
204 key_joystick_mapping = 0;
205 for (i=0; i<MAX_PLAYERS; i++)
206 stored_player[i].action = 0;
209 void SleepWhileUnmapped()
211 boolean window_unmapped = TRUE;
213 KeyboardAutoRepeatOn();
215 while(window_unmapped)
223 case EVENT_BUTTONRELEASE:
224 button_status = MB_RELEASED;
227 case EVENT_KEYRELEASE:
228 key_joystick_mapping = 0;
231 case EVENT_MAPNOTIFY:
232 window_unmapped = FALSE;
235 case EVENT_UNMAPNOTIFY:
236 /* this is only to surely prevent the 'should not happen' case
237 * of recursively looping between 'SleepWhileUnmapped()' and
238 * 'HandleOtherEvents()' which usually calls this funtion.
243 HandleOtherEvents(&event);
248 if (game_status == PLAYING)
249 KeyboardAutoRepeatOff();
252 void HandleExposeEvent(ExposeEvent *event)
255 RedrawPlayfield(FALSE, event->x, event->y, event->width, event->height);
260 void HandleButtonEvent(ButtonEvent *event)
262 motion_status = FALSE;
264 if (event->type == EVENT_BUTTONPRESS)
265 button_status = event->button;
267 button_status = MB_RELEASED;
269 HandleButton(event->x, event->y, button_status);
272 void HandleMotionEvent(MotionEvent *event)
274 if (!PointerInWindow(window))
275 return; /* window and pointer are on different screens */
278 if (button_status == MB_RELEASED && game_status != LEVELED)
282 motion_status = TRUE;
284 HandleButton(event->x, event->y, button_status);
287 void HandleKeyEvent(KeyEvent *event)
289 int key_status = (event->type==EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
290 boolean with_modifiers = (game_status == PLAYING ? FALSE : TRUE);
291 Key key = GetEventKey(event, with_modifiers);
293 HandleKey(key, key_status);
296 void HandleFocusEvent(FocusChangeEvent *event)
298 static int old_joystick_status = -1;
300 if (event->type == EVENT_FOCUSOUT)
302 KeyboardAutoRepeatOn();
303 old_joystick_status = joystick.status;
304 joystick.status = JOYSTICK_NOT_AVAILABLE;
308 else if (event->type == EVENT_FOCUSIN)
310 /* When there are two Rocks'n'Diamonds windows which overlap and
311 the player moves the pointer from one game window to the other,
312 a 'FocusOut' event is generated for the window the pointer is
313 leaving and a 'FocusIn' event is generated for the window the
314 pointer is entering. In some cases, it can happen that the
315 'FocusIn' event is handled by the one game process before the
316 'FocusOut' event by the other game process. In this case the
317 X11 environment would end up with activated keyboard auto repeat,
318 because unfortunately this is a global setting and not (which
319 would be far better) set for each X11 window individually.
320 The effect would be keyboard auto repeat while playing the game
321 (game_status == PLAYING), which is not desired.
322 To avoid this special case, we just wait 1/10 second before
323 processing the 'FocusIn' event.
326 if (game_status == PLAYING)
329 KeyboardAutoRepeatOff();
331 if (old_joystick_status != -1)
332 joystick.status = old_joystick_status;
336 void HandleClientMessageEvent(ClientMessageEvent *event)
338 if (CheckCloseWindowEvent(event))
342 void HandleButton(int mx, int my, int button)
344 static int old_mx = 0, old_my = 0;
358 HandleGadgets(mx, my, button);
363 HandleMainMenu(mx,my, 0,0, button);
367 HandleTypeName(0, KSYM_Return);
371 HandleChooseLevel(mx,my, 0,0, button);
375 HandleHallOfFame(0,0, 0,0, button);
382 HandleHelpScreen(button);
386 HandleSetupScreen(mx,my, 0,0, button);
391 if (button == MB_RELEASED)
393 int sx = (mx - SX) / TILEX;
394 int sy = (my - SY) / TILEY;
396 if (IN_VIS_FIELD(sx,sy))
401 printf("INFO: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
403 if (!IN_LEV_FIELD(x, y))
406 printf(" Feld[%d][%d] == %d\n", x,y, Feld[x][y]);
407 printf(" Store[%d][%d] == %d\n", x,y, Store[x][y]);
408 printf(" Store2[%d][%d] == %d\n", x,y, Store2[x][y]);
409 printf(" StorePlayer[%d][%d] == %d\n", x,y, StorePlayer[x][y]);
410 printf(" MovPos[%d][%d] == %d\n", x,y, MovPos[x][y]);
411 printf(" MovDir[%d][%d] == %d\n", x,y, MovDir[x][y]);
412 printf(" MovDelay[%d][%d] == %d\n", x,y, MovDelay[x][y]);
424 void HandleKey(Key key, int key_status)
427 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
428 static struct SetupKeyboardInfo custom_key;
436 { &custom_key.left, DEFAULT_KEY_LEFT, JOY_LEFT },
437 { &custom_key.right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
438 { &custom_key.up, DEFAULT_KEY_UP, JOY_UP },
439 { &custom_key.down, DEFAULT_KEY_DOWN, JOY_DOWN },
440 { &custom_key.snap, DEFAULT_KEY_SNAP, JOY_BUTTON_1 },
441 { &custom_key.bomb, DEFAULT_KEY_BOMB, JOY_BUTTON_2 }
444 if (game_status == PLAYING)
446 /* only needed for single-step tape recording mode */
447 static boolean clear_button_2[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
448 static boolean bomb_placed[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
451 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
456 if (setup.input[pnr].use_joystick)
459 custom_key = setup.input[pnr].key;
462 if (key == *key_info[i].key_custom)
463 key_action |= key_info[i].action;
465 if (tape.single_step && clear_button_2[pnr])
467 stored_player[pnr].action &= ~KEY_BUTTON_2;
468 clear_button_2[pnr] = FALSE;
471 if (key_status == KEY_PRESSED)
472 stored_player[pnr].action |= key_action;
474 stored_player[pnr].action &= ~key_action;
476 if (tape.single_step && tape.recording && tape.pausing)
478 if (key_status == KEY_PRESSED &&
479 (key_action & (KEY_MOTION | KEY_BUTTON_1)))
481 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
483 if (key_action & KEY_MOTION)
485 if (stored_player[pnr].action & KEY_BUTTON_2)
486 bomb_placed[pnr] = TRUE;
489 else if (key_status == KEY_RELEASED &&
490 (key_action & KEY_BUTTON_2))
492 if (!bomb_placed[pnr])
494 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
496 stored_player[pnr].action |= KEY_BUTTON_2;
497 clear_button_2[pnr] = TRUE;
500 bomb_placed[pnr] = FALSE;
503 else if (tape.recording && tape.pausing && (key_action & KEY_ACTION))
504 TapeTogglePause(TAPE_TOGGLE_MANUAL);
512 if (key == key_info[i].key_default)
513 joy |= key_info[i].action;
518 if (key_status == KEY_PRESSED)
519 key_joystick_mapping |= joy;
521 key_joystick_mapping &= ~joy;
526 if (game_status != PLAYING)
527 key_joystick_mapping = 0;
529 if (key_status == KEY_RELEASED)
532 if ((key == KSYM_Return || key == setup.shortcut.toggle_pause) &&
533 game_status == PLAYING && AllPlayersGone)
535 CloseDoor(DOOR_CLOSE_1);
536 game_status = MAINMENU;
541 /* allow quick escape to the main menu with the Escape key */
542 if (key == KSYM_Escape &&
543 game_status != MAINMENU &&
544 game_status != PLAYING &&
545 game_status != LEVELED &&
546 game_status != CHOOSELEVEL &&
547 game_status != SETUP)
549 game_status = MAINMENU;
554 /* special key shortcuts */
555 if (game_status == MAINMENU || game_status == PLAYING)
557 if (key == setup.shortcut.save_game)
559 else if (key == setup.shortcut.load_game)
561 else if (key == setup.shortcut.toggle_pause)
562 TapeTogglePause(TAPE_TOGGLE_MANUAL);
568 if (game_status == PLAYING && (tape.playing || tape.pausing))
575 HandleGadgetsKeyInput(key);
580 HandleTypeName(0, key);
589 if (game_status == MAINMENU)
590 HandleMainMenu(0,0, 0,0, MB_MENU_CHOICE);
591 else if (game_status == CHOOSELEVEL)
592 HandleChooseLevel(0,0, 0,0, MB_MENU_CHOICE);
593 else if (game_status == SETUP)
594 HandleSetupScreen(0,0, 0,0, MB_MENU_CHOICE);
598 if (game_status == CHOOSELEVEL)
599 HandleChooseLevel(0,0, 0,0, MB_MENU_LEAVE);
600 else if (game_status == SETUP)
601 HandleSetupScreen(0,0, 0,0, MB_MENU_LEAVE);
605 if (game_status == CHOOSELEVEL)
606 HandleChooseLevel(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
607 else if (game_status == SETUP)
608 HandleSetupScreen(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
612 if (game_status == CHOOSELEVEL)
613 HandleChooseLevel(0,0, 0,SCR_FIELDY, MB_MENU_MARK);
614 else if (game_status == SETUP)
615 HandleSetupScreen(0,0, 0,SCR_FIELDY, MB_MENU_MARK);
630 HandleHelpScreen(MB_RELEASED);
637 game_status = MAINMENU;
643 HandleHallOfFame(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
647 HandleHallOfFame(0,0, 0,SCR_FIELDY, MB_MENU_MARK);
656 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
657 HandleLevelEditorKeyInput(key);
665 RequestQuitGame(setup.ask_on_escape);
681 if (GameFrameDelay == 500)
682 GameFrameDelay = GAME_FRAME_DELAY;
684 GameFrameDelay = 500;
687 GameFrameDelay = (key - KSYM_0) * 10;
688 printf("Game speed == %d%% (%d ms delay between two frames)\n",
689 GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
695 options.debug = FALSE;
696 printf("debug mode disabled\n");
700 options.debug = TRUE;
701 printf("debug mode enabled\n");
706 if (!global.fps_slowdown)
708 global.fps_slowdown = TRUE;
709 global.fps_slowdown_factor = 2;
710 printf("fps slowdown enabled -- display only every 2nd frame\n");
712 else if (global.fps_slowdown_factor == 2)
714 global.fps_slowdown_factor = 4;
715 printf("fps slowdown enabled -- display only every 4th frame\n");
719 global.fps_slowdown = FALSE;
720 global.fps_slowdown_factor = 1;
721 printf("fps slowdown disabled\n");
727 if (ScrollStepSize == TILEX/8)
728 ScrollStepSize = TILEX/4;
730 ScrollStepSize = TILEX/8;
731 printf("ScrollStepSize == %d\n", ScrollStepSize);
740 ScrollStepSize = TILEX/4;
745 ScrollStepSize = TILEX/8;
747 printf("MoveSpeed == %d\n", MoveSpeed);
752 ScrollStepSize = TILEX/8;
753 printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
757 ScrollStepSize = TILEX/4;
758 printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
762 ScrollStepSize = TILEX/2;
763 printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
767 ScrollStepSize = TILEX;
768 printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
773 local_player->dynamite = 1000;
784 for(i=0; i<MAX_PLAYERS; i++)
786 printf("Player %d:\n", i);
787 printf(" jx == %d, jy == %d\n",
788 stored_player[i].jx, stored_player[i].jy);
789 printf(" last_jx == %d, last_jy == %d\n",
790 stored_player[i].last_jx, stored_player[i].last_jy);
811 if (button_status && game_status != PLAYING)
813 HandleButton(0, 0, -button_status);
817 #if defined(PLATFORM_UNIX)
825 static int HandleJoystickForAllPlayers()
830 for (i=0; i<MAX_PLAYERS; i++)
835 if (!setup.input[i].use_joystick)
839 joy_action = Joystick(i);
840 result |= joy_action;
842 if (!setup.input[i].use_joystick)
845 stored_player[i].action = joy_action;
851 void HandleJoystick()
853 int joystick = HandleJoystickForAllPlayers();
854 int keyboard = key_joystick_mapping;
855 int joy = (joystick | keyboard);
856 int left = joy & JOY_LEFT;
857 int right = joy & JOY_RIGHT;
858 int up = joy & JOY_UP;
859 int down = joy & JOY_DOWN;
860 int button = joy & JOY_BUTTON;
861 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
862 int dx = (left ? -1 : right ? 1 : 0);
863 int dy = (up ? -1 : down ? 1 : 0);
871 static unsigned long joystickmove_delay = 0;
873 if (joystick && !button &&
874 !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
875 newbutton = dx = dy = 0;
877 if (game_status == MAINMENU)
878 HandleMainMenu(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
879 else if (game_status == CHOOSELEVEL)
880 HandleChooseLevel(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
881 else if (game_status == SETUP)
882 HandleSetupScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
887 HandleHallOfFame(0,0, dx,dy, !newbutton);
891 HandleHelpScreen(!newbutton);
895 HandleLevelEditorIdle();
899 if (tape.playing || keyboard)
900 newbutton = ((joy & JOY_BUTTON) != 0);
902 if (AllPlayersGone && newbutton)
904 CloseDoor(DOOR_CLOSE_1);
905 game_status = MAINMENU;