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
36 int FilterMouseMotionEvents(const Event *event)
38 if (event->type != EVENT_MOTIONNOTIFY)
41 /* get mouse motion events without pressed button only in level editor */
42 if (button_status == MB_RELEASED && game_status != LEVELED)
48 /* this is only really needed for non-SDL targets to filter unwanted events;
49 when using SDL with properly installed event filter, this function can be
50 replaced with a simple "NextEvent()" call, but it doesn't hurt either */
52 static boolean NextValidEvent(Event *event)
54 while (PendingEvent())
58 if (FilterMouseMotionEvents(event))
69 if (PendingEvent()) /* got event */
73 if (NextValidEvent(&event))
77 case EVENT_BUTTONPRESS:
78 case EVENT_BUTTONRELEASE:
79 HandleButtonEvent((ButtonEvent *) &event);
82 case EVENT_MOTIONNOTIFY:
83 HandleMotionEvent((MotionEvent *) &event);
87 case EVENT_KEYRELEASE:
88 HandleKeyEvent((KeyEvent *) &event);
92 HandleOtherEvents(&event);
100 /* don't use all CPU time when idle; the main loop while playing
101 has its own synchronization and is CPU friendly, too */
103 if (game_status == PLAYING)
108 if (!PendingEvent()) /* delay only if no pending events */
112 /* refresh window contents from drawing buffer, if needed */
115 if (game_status == EXITGAME)
120 void HandleOtherEvents(Event *event)
125 HandleExposeEvent((ExposeEvent *) event);
128 case EVENT_UNMAPNOTIFY:
130 /* This causes the game to stop not only when iconified, but also
131 when on another virtual desktop, which might be not desired. */
132 SleepWhileUnmapped();
138 HandleFocusEvent((FocusChangeEvent *) event);
141 case EVENT_CLIENTMESSAGE:
142 HandleClientMessageEvent((ClientMessageEvent *) event);
145 #if defined(TARGET_SDL)
146 case SDL_JOYAXISMOTION:
147 case SDL_JOYBUTTONDOWN:
148 case SDL_JOYBUTTONUP:
149 HandleJoystickEvent(event);
158 void ClearEventQueue()
160 while (PendingEvent())
168 case EVENT_BUTTONRELEASE:
169 button_status = MB_RELEASED;
172 case EVENT_KEYRELEASE:
173 key_joystick_mapping = 0;
177 HandleOtherEvents(&event);
183 void ClearPlayerAction()
187 /* simulate key release events for still pressed keys */
188 key_joystick_mapping = 0;
189 for (i=0; i<MAX_PLAYERS; i++)
190 stored_player[i].action = 0;
193 void SleepWhileUnmapped()
195 boolean window_unmapped = TRUE;
197 KeyboardAutoRepeatOn();
199 while(window_unmapped)
207 case EVENT_BUTTONRELEASE:
208 button_status = MB_RELEASED;
211 case EVENT_KEYRELEASE:
212 key_joystick_mapping = 0;
215 case EVENT_MAPNOTIFY:
216 window_unmapped = FALSE;
219 case EVENT_UNMAPNOTIFY:
220 /* this is only to surely prevent the 'should not happen' case
221 * of recursively looping between 'SleepWhileUnmapped()' and
222 * 'HandleOtherEvents()' which usually calls this funtion.
227 HandleOtherEvents(&event);
232 if (game_status == PLAYING)
233 KeyboardAutoRepeatOff();
236 void HandleExposeEvent(ExposeEvent *event)
239 RedrawPlayfield(FALSE, event->x, event->y, event->width, event->height);
244 void HandleButtonEvent(ButtonEvent *event)
246 motion_status = FALSE;
248 if (event->type == EVENT_BUTTONPRESS)
249 button_status = event->button;
251 button_status = MB_RELEASED;
253 HandleButton(event->x, event->y, button_status);
256 void HandleMotionEvent(MotionEvent *event)
258 if (!PointerInWindow(window))
259 return; /* window and pointer are on different screens */
262 if (button_status == MB_RELEASED && game_status != LEVELED)
266 motion_status = TRUE;
268 HandleButton(event->x, event->y, button_status);
271 void HandleKeyEvent(KeyEvent *event)
273 int key_status = (event->type==EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
274 boolean with_modifiers = (game_status == PLAYING ? FALSE : TRUE);
275 Key key = GetEventKey(event, with_modifiers);
277 HandleKey(key, key_status);
280 void HandleFocusEvent(FocusChangeEvent *event)
282 static int old_joystick_status = -1;
284 if (event->type == EVENT_FOCUSOUT)
286 KeyboardAutoRepeatOn();
287 old_joystick_status = joystick.status;
288 joystick.status = JOYSTICK_NOT_AVAILABLE;
292 else if (event->type == EVENT_FOCUSIN)
294 /* When there are two Rocks'n'Diamonds windows which overlap and
295 the player moves the pointer from one game window to the other,
296 a 'FocusOut' event is generated for the window the pointer is
297 leaving and a 'FocusIn' event is generated for the window the
298 pointer is entering. In some cases, it can happen that the
299 'FocusIn' event is handled by the one game process before the
300 'FocusOut' event by the other game process. In this case the
301 X11 environment would end up with activated keyboard auto repeat,
302 because unfortunately this is a global setting and not (which
303 would be far better) set for each X11 window individually.
304 The effect would be keyboard auto repeat while playing the game
305 (game_status == PLAYING), which is not desired.
306 To avoid this special case, we just wait 1/10 second before
307 processing the 'FocusIn' event.
310 if (game_status == PLAYING)
313 KeyboardAutoRepeatOff();
315 if (old_joystick_status != -1)
316 joystick.status = old_joystick_status;
320 void HandleClientMessageEvent(ClientMessageEvent *event)
322 if (CheckCloseWindowEvent(event))
326 void HandleButton(int mx, int my, int button)
328 static int old_mx = 0, old_my = 0;
342 HandleGadgets(mx, my, button);
347 HandleMainMenu(mx,my, 0,0, button);
351 HandleTypeName(0, KSYM_Return);
355 HandleChooseLevel(mx,my, 0,0, button);
359 HandleHallOfFame(0,0, 0,0, button);
366 HandleHelpScreen(button);
370 HandleSetupScreen(mx,my, 0,0, button);
375 if (button == MB_RELEASED)
377 int sx = (mx - SX) / TILEX;
378 int sy = (my - SY) / TILEY;
380 if (IN_VIS_FIELD(sx,sy))
385 printf("INFO: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
387 if (!IN_LEV_FIELD(x, y))
390 printf(" Feld[%d][%d] == %d\n", x,y, Feld[x][y]);
391 printf(" Store[%d][%d] == %d\n", x,y, Store[x][y]);
392 printf(" Store2[%d][%d] == %d\n", x,y, Store2[x][y]);
393 printf(" StorePlayer[%d][%d] == %d\n", x,y, StorePlayer[x][y]);
394 printf(" MovPos[%d][%d] == %d\n", x,y, MovPos[x][y]);
395 printf(" MovDir[%d][%d] == %d\n", x,y, MovDir[x][y]);
396 printf(" MovDelay[%d][%d] == %d\n", x,y, MovDelay[x][y]);
408 void HandleKey(Key key, int key_status)
411 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
412 static struct SetupKeyboardInfo custom_key;
420 { &custom_key.left, DEFAULT_KEY_LEFT, JOY_LEFT },
421 { &custom_key.right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
422 { &custom_key.up, DEFAULT_KEY_UP, JOY_UP },
423 { &custom_key.down, DEFAULT_KEY_DOWN, JOY_DOWN },
424 { &custom_key.snap, DEFAULT_KEY_SNAP, JOY_BUTTON_1 },
425 { &custom_key.bomb, DEFAULT_KEY_BOMB, JOY_BUTTON_2 }
428 if (game_status == PLAYING)
430 /* only needed for single-step tape recording mode */
431 static boolean clear_button_2[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
432 static boolean bomb_placed[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
435 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
440 if (setup.input[pnr].use_joystick)
443 custom_key = setup.input[pnr].key;
446 if (key == *key_info[i].key_custom)
447 key_action |= key_info[i].action;
449 if (tape.single_step && clear_button_2[pnr])
451 stored_player[pnr].action &= ~KEY_BUTTON_2;
452 clear_button_2[pnr] = FALSE;
455 if (key_status == KEY_PRESSED)
456 stored_player[pnr].action |= key_action;
458 stored_player[pnr].action &= ~key_action;
460 if (tape.single_step && tape.recording && tape.pausing)
462 if (key_status == KEY_PRESSED &&
463 (key_action & (KEY_MOTION | KEY_BUTTON_1)))
465 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
467 if (key_action & KEY_MOTION)
469 if (stored_player[pnr].action & KEY_BUTTON_2)
470 bomb_placed[pnr] = TRUE;
473 else if (key_status == KEY_RELEASED &&
474 (key_action & KEY_BUTTON_2))
476 if (!bomb_placed[pnr])
478 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
480 stored_player[pnr].action |= KEY_BUTTON_2;
481 clear_button_2[pnr] = TRUE;
484 bomb_placed[pnr] = FALSE;
487 else if (tape.recording && tape.pausing && (key_action & KEY_ACTION))
488 TapeTogglePause(TAPE_TOGGLE_MANUAL);
496 if (key == key_info[i].key_default)
497 joy |= key_info[i].action;
502 if (key_status == KEY_PRESSED)
503 key_joystick_mapping |= joy;
505 key_joystick_mapping &= ~joy;
510 if (game_status != PLAYING)
511 key_joystick_mapping = 0;
513 if (key_status == KEY_RELEASED)
516 if ((key == KSYM_Return || key == setup.shortcut.toggle_pause) &&
517 game_status == PLAYING && AllPlayersGone)
519 CloseDoor(DOOR_CLOSE_1);
520 game_status = MAINMENU;
525 /* allow quick escape to the main menu with the Escape key */
526 if (key == KSYM_Escape &&
527 game_status != MAINMENU &&
528 game_status != PLAYING &&
529 game_status != LEVELED &&
530 game_status != CHOOSELEVEL &&
531 game_status != SETUP)
533 game_status = MAINMENU;
538 /* special key shortcuts */
539 if (game_status == MAINMENU || game_status == PLAYING)
541 if (key == setup.shortcut.save_game)
543 else if (key == setup.shortcut.load_game)
545 else if (key == setup.shortcut.toggle_pause)
546 TapeTogglePause(TAPE_TOGGLE_MANUAL);
552 if (game_status == PLAYING && (tape.playing || tape.pausing))
559 HandleGadgetsKeyInput(key);
564 HandleTypeName(0, key);
573 if (game_status == MAINMENU)
574 HandleMainMenu(0,0, 0,0, MB_MENU_CHOICE);
575 else if (game_status == CHOOSELEVEL)
576 HandleChooseLevel(0,0, 0,0, MB_MENU_CHOICE);
577 else if (game_status == SETUP)
578 HandleSetupScreen(0,0, 0,0, MB_MENU_CHOICE);
582 if (game_status == CHOOSELEVEL)
583 HandleChooseLevel(0,0, 0,0, MB_MENU_LEAVE);
584 else if (game_status == SETUP)
585 HandleSetupScreen(0,0, 0,0, MB_MENU_LEAVE);
589 if (game_status == CHOOSELEVEL)
590 HandleChooseLevel(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
591 else if (game_status == SETUP)
592 HandleSetupScreen(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
596 if (game_status == CHOOSELEVEL)
597 HandleChooseLevel(0,0, 0,SCR_FIELDY, MB_MENU_MARK);
598 else if (game_status == SETUP)
599 HandleSetupScreen(0,0, 0,SCR_FIELDY, MB_MENU_MARK);
614 HandleHelpScreen(MB_RELEASED);
621 game_status = MAINMENU;
627 HandleHallOfFame(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
631 HandleHallOfFame(0,0, 0,SCR_FIELDY, MB_MENU_MARK);
640 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
641 HandleLevelEditorKeyInput(key);
649 RequestQuitGame(setup.ask_on_escape);
665 if (GameFrameDelay == 500)
666 GameFrameDelay = GAME_FRAME_DELAY;
668 GameFrameDelay = 500;
671 GameFrameDelay = (key - KSYM_0) * 10;
672 printf("Game speed == %d%% (%d ms delay between two frames)\n",
673 GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
679 options.debug = FALSE;
680 printf("debug mode disabled\n");
684 options.debug = TRUE;
685 printf("debug mode enabled\n");
690 if (!global.fps_slowdown)
692 global.fps_slowdown = TRUE;
693 global.fps_slowdown_factor = 2;
694 printf("fps slowdown enabled -- display only every 2nd frame\n");
696 else if (global.fps_slowdown_factor == 2)
698 global.fps_slowdown_factor = 4;
699 printf("fps slowdown enabled -- display only every 4th frame\n");
703 global.fps_slowdown = FALSE;
704 global.fps_slowdown_factor = 1;
705 printf("fps slowdown disabled\n");
711 if (ScrollStepSize == TILEX/8)
712 ScrollStepSize = TILEX/4;
714 ScrollStepSize = TILEX/8;
715 printf("ScrollStepSize == %d\n", ScrollStepSize);
724 ScrollStepSize = TILEX/4;
729 ScrollStepSize = TILEX/8;
731 printf("MoveSpeed == %d\n", MoveSpeed);
736 ScrollStepSize = TILEX/8;
737 printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
741 ScrollStepSize = TILEX/4;
742 printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
746 ScrollStepSize = TILEX/2;
747 printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
751 ScrollStepSize = TILEX;
752 printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
757 local_player->dynamite = 1000;
768 for(i=0; i<MAX_PLAYERS; i++)
770 printf("Player %d:\n", i);
771 printf(" jx == %d, jy == %d\n",
772 stored_player[i].jx, stored_player[i].jy);
773 printf(" last_jx == %d, last_jy == %d\n",
774 stored_player[i].last_jx, stored_player[i].last_jy);
795 if (button_status && game_status != PLAYING)
797 HandleButton(0, 0, -button_status);
801 #if defined(PLATFORM_UNIX)
809 static int HandleJoystickForAllPlayers()
814 for (i=0; i<MAX_PLAYERS; i++)
819 if (!setup.input[i].use_joystick)
823 joy_action = Joystick(i);
824 result |= joy_action;
826 if (!setup.input[i].use_joystick)
829 stored_player[i].action = joy_action;
835 void HandleJoystick()
837 int joystick = HandleJoystickForAllPlayers();
838 int keyboard = key_joystick_mapping;
839 int joy = (joystick | keyboard);
840 int left = joy & JOY_LEFT;
841 int right = joy & JOY_RIGHT;
842 int up = joy & JOY_UP;
843 int down = joy & JOY_DOWN;
844 int button = joy & JOY_BUTTON;
845 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
846 int dx = (left ? -1 : right ? 1 : 0);
847 int dy = (up ? -1 : down ? 1 : 0);
855 static unsigned long joystickmove_delay = 0;
857 if (joystick && !button &&
858 !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
859 newbutton = dx = dy = 0;
861 if (game_status == MAINMENU)
862 HandleMainMenu(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
863 else if (game_status == CHOOSELEVEL)
864 HandleChooseLevel(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
865 else if (game_status == SETUP)
866 HandleSetupScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
871 HandleHallOfFame(0,0, dx,dy, !newbutton);
875 HandleHelpScreen(!newbutton);
879 HandleLevelEditorIdle();
883 if (tape.playing || keyboard)
884 newbutton = ((joy & JOY_BUTTON) != 0);
886 if (AllPlayersGone && newbutton)
888 CloseDoor(DOOR_CLOSE_1);
889 game_status = MAINMENU;