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 SleepWhileUnmapped();
136 HandleFocusEvent((FocusChangeEvent *) event);
139 case EVENT_CLIENTMESSAGE:
140 HandleClientMessageEvent((ClientMessageEvent *) event);
143 #if defined(TARGET_SDL)
144 case SDL_JOYAXISMOTION:
145 case SDL_JOYBUTTONDOWN:
146 case SDL_JOYBUTTONUP:
147 HandleJoystickEvent(event);
156 void ClearEventQueue()
158 while (PendingEvent())
166 case EVENT_BUTTONRELEASE:
167 button_status = MB_RELEASED;
170 case EVENT_KEYRELEASE:
171 key_joystick_mapping = 0;
175 HandleOtherEvents(&event);
181 void ClearPlayerAction()
185 /* simulate key release events for still pressed keys */
186 key_joystick_mapping = 0;
187 for (i=0; i<MAX_PLAYERS; i++)
188 stored_player[i].action = 0;
191 void SleepWhileUnmapped()
193 boolean window_unmapped = TRUE;
195 KeyboardAutoRepeatOn();
197 while(window_unmapped)
205 case EVENT_BUTTONRELEASE:
206 button_status = MB_RELEASED;
209 case EVENT_KEYRELEASE:
210 key_joystick_mapping = 0;
213 case EVENT_MAPNOTIFY:
214 window_unmapped = FALSE;
217 case EVENT_UNMAPNOTIFY:
218 /* this is only to surely prevent the 'should not happen' case
219 * of recursively looping between 'SleepWhileUnmapped()' and
220 * 'HandleOtherEvents()' which usually calls this funtion.
225 HandleOtherEvents(&event);
230 if (game_status == PLAYING)
231 KeyboardAutoRepeatOff();
234 void HandleExposeEvent(ExposeEvent *event)
237 RedrawPlayfield(FALSE, event->x, event->y, event->width, event->height);
242 void HandleButtonEvent(ButtonEvent *event)
244 motion_status = FALSE;
246 if (event->type == EVENT_BUTTONPRESS)
247 button_status = event->button;
249 button_status = MB_RELEASED;
251 HandleButton(event->x, event->y, button_status);
254 void HandleMotionEvent(MotionEvent *event)
256 if (!PointerInWindow(window))
257 return; /* window and pointer are on different screens */
260 if (button_status == MB_RELEASED && game_status != LEVELED)
264 motion_status = TRUE;
266 HandleButton(event->x, event->y, button_status);
269 void HandleKeyEvent(KeyEvent *event)
271 int key_status = (event->type==EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
272 boolean with_modifiers = (game_status == PLAYING ? FALSE : TRUE);
273 Key key = GetEventKey(event, with_modifiers);
275 HandleKey(key, key_status);
278 void HandleFocusEvent(FocusChangeEvent *event)
280 static int old_joystick_status = -1;
282 if (event->type == EVENT_FOCUSOUT)
284 KeyboardAutoRepeatOn();
285 old_joystick_status = joystick.status;
286 joystick.status = JOYSTICK_NOT_AVAILABLE;
290 else if (event->type == EVENT_FOCUSIN)
292 /* When there are two Rocks'n'Diamonds windows which overlap and
293 the player moves the pointer from one game window to the other,
294 a 'FocusOut' event is generated for the window the pointer is
295 leaving and a 'FocusIn' event is generated for the window the
296 pointer is entering. In some cases, it can happen that the
297 'FocusIn' event is handled by the one game process before the
298 'FocusOut' event by the other game process. In this case the
299 X11 environment would end up with activated keyboard auto repeat,
300 because unfortunately this is a global setting and not (which
301 would be far better) set for each X11 window individually.
302 The effect would be keyboard auto repeat while playing the game
303 (game_status == PLAYING), which is not desired.
304 To avoid this special case, we just wait 1/10 second before
305 processing the 'FocusIn' event.
308 if (game_status == PLAYING)
311 KeyboardAutoRepeatOff();
313 if (old_joystick_status != -1)
314 joystick.status = old_joystick_status;
318 void HandleClientMessageEvent(ClientMessageEvent *event)
320 if (CheckCloseWindowEvent(event))
324 void HandleButton(int mx, int my, int button)
326 static int old_mx = 0, old_my = 0;
340 HandleGadgets(mx, my, button);
345 HandleMainMenu(mx,my, 0,0, button);
349 HandleTypeName(0, KSYM_Return);
353 HandleChooseLevel(mx,my, 0,0, button);
357 HandleHallOfFame(0,0, 0,0, button);
364 HandleHelpScreen(button);
368 HandleSetupScreen(mx,my, 0,0, button);
373 if (button == MB_RELEASED)
375 int sx = (mx - SX) / TILEX;
376 int sy = (my - SY) / TILEY;
378 if (IN_VIS_FIELD(sx,sy))
383 printf("INFO: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
385 if (!IN_LEV_FIELD(x, y))
388 printf(" Feld[%d][%d] == %d\n", x,y, Feld[x][y]);
389 printf(" Store[%d][%d] == %d\n", x,y, Store[x][y]);
390 printf(" Store2[%d][%d] == %d\n", x,y, Store2[x][y]);
391 printf(" StorePlayer[%d][%d] == %d\n", x,y, StorePlayer[x][y]);
392 printf(" MovPos[%d][%d] == %d\n", x,y, MovPos[x][y]);
393 printf(" MovDir[%d][%d] == %d\n", x,y, MovDir[x][y]);
394 printf(" MovDelay[%d][%d] == %d\n", x,y, MovDelay[x][y]);
406 void HandleKey(Key key, int key_status)
409 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
410 static struct SetupKeyboardInfo custom_key;
418 { &custom_key.left, DEFAULT_KEY_LEFT, JOY_LEFT },
419 { &custom_key.right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
420 { &custom_key.up, DEFAULT_KEY_UP, JOY_UP },
421 { &custom_key.down, DEFAULT_KEY_DOWN, JOY_DOWN },
422 { &custom_key.snap, DEFAULT_KEY_SNAP, JOY_BUTTON_1 },
423 { &custom_key.bomb, DEFAULT_KEY_BOMB, JOY_BUTTON_2 }
426 if (game_status == PLAYING)
428 /* only needed for single-step tape recording mode */
429 static boolean clear_button_2[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
430 static boolean bomb_placed[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
433 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
438 if (setup.input[pnr].use_joystick)
441 custom_key = setup.input[pnr].key;
444 if (key == *key_info[i].key_custom)
445 key_action |= key_info[i].action;
447 if (tape.single_step && clear_button_2[pnr])
449 stored_player[pnr].action &= ~KEY_BUTTON_2;
450 clear_button_2[pnr] = FALSE;
453 if (key_status == KEY_PRESSED)
454 stored_player[pnr].action |= key_action;
456 stored_player[pnr].action &= ~key_action;
458 if (tape.single_step && tape.recording && tape.pausing)
460 if (key_status == KEY_PRESSED &&
461 (key_action & (KEY_MOTION | KEY_BUTTON_1)))
463 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
465 if (key_action & KEY_MOTION)
467 if (stored_player[pnr].action & KEY_BUTTON_2)
468 bomb_placed[pnr] = TRUE;
471 else if (key_status == KEY_RELEASED &&
472 (key_action & KEY_BUTTON_2))
474 if (!bomb_placed[pnr])
476 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
478 stored_player[pnr].action |= KEY_BUTTON_2;
479 clear_button_2[pnr] = TRUE;
482 bomb_placed[pnr] = FALSE;
485 else if (tape.recording && tape.pausing && (key_action & KEY_ACTION))
486 TapeTogglePause(TAPE_TOGGLE_MANUAL);
494 if (key == key_info[i].key_default)
495 joy |= key_info[i].action;
500 if (key_status == KEY_PRESSED)
501 key_joystick_mapping |= joy;
503 key_joystick_mapping &= ~joy;
508 if (game_status != PLAYING)
509 key_joystick_mapping = 0;
511 if (key_status == KEY_RELEASED)
514 if ((key == KSYM_Return || key == setup.shortcut.toggle_pause) &&
515 game_status == PLAYING && AllPlayersGone)
517 CloseDoor(DOOR_CLOSE_1);
518 game_status = MAINMENU;
523 /* allow quick escape to the main menu with the Escape key */
524 if (key == KSYM_Escape &&
525 game_status != MAINMENU &&
526 game_status != PLAYING &&
527 game_status != LEVELED &&
528 game_status != CHOOSELEVEL &&
529 game_status != SETUP)
531 game_status = MAINMENU;
536 /* special key shortcuts */
537 if (game_status == MAINMENU || game_status == PLAYING)
539 if (key == setup.shortcut.save_game)
541 else if (key == setup.shortcut.load_game)
543 else if (key == setup.shortcut.toggle_pause)
544 TapeTogglePause(TAPE_TOGGLE_MANUAL);
550 if (game_status == PLAYING && (tape.playing || tape.pausing))
557 HandleGadgetsKeyInput(key);
562 HandleTypeName(0, key);
571 if (game_status == MAINMENU)
572 HandleMainMenu(0,0, 0,0, MB_MENU_CHOICE);
573 else if (game_status == CHOOSELEVEL)
574 HandleChooseLevel(0,0, 0,0, MB_MENU_CHOICE);
575 else if (game_status == SETUP)
576 HandleSetupScreen(0,0, 0,0, MB_MENU_CHOICE);
580 if (game_status == CHOOSELEVEL)
581 HandleChooseLevel(0,0, 0,0, MB_MENU_LEAVE);
582 else if (game_status == SETUP)
583 HandleSetupScreen(0,0, 0,0, MB_MENU_LEAVE);
587 if (game_status == CHOOSELEVEL)
588 HandleChooseLevel(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
589 else if (game_status == SETUP)
590 HandleSetupScreen(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
594 if (game_status == CHOOSELEVEL)
595 HandleChooseLevel(0,0, 0,SCR_FIELDY, MB_MENU_MARK);
596 else if (game_status == SETUP)
597 HandleSetupScreen(0,0, 0,SCR_FIELDY, MB_MENU_MARK);
612 HandleHelpScreen(MB_RELEASED);
619 game_status = MAINMENU;
625 HandleHallOfFame(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
629 HandleHallOfFame(0,0, 0,SCR_FIELDY, MB_MENU_MARK);
638 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
639 HandleLevelEditorKeyInput(key);
647 RequestQuitGame(setup.ask_on_escape);
663 if (GameFrameDelay == 500)
664 GameFrameDelay = GAME_FRAME_DELAY;
666 GameFrameDelay = 500;
669 GameFrameDelay = (key - KSYM_0) * 10;
670 printf("Game speed == %d%% (%d ms delay between two frames)\n",
671 GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
677 options.debug = FALSE;
678 printf("debug mode disabled\n");
682 options.debug = TRUE;
683 printf("debug mode enabled\n");
688 if (!global.fps_slowdown)
690 global.fps_slowdown = TRUE;
691 global.fps_slowdown_factor = 2;
692 printf("fps slowdown enabled -- display only every 2nd frame\n");
694 else if (global.fps_slowdown_factor == 2)
696 global.fps_slowdown_factor = 4;
697 printf("fps slowdown enabled -- display only every 4th frame\n");
701 global.fps_slowdown = FALSE;
702 global.fps_slowdown_factor = 1;
703 printf("fps slowdown disabled\n");
709 if (ScrollStepSize == TILEX/8)
710 ScrollStepSize = TILEX/4;
712 ScrollStepSize = TILEX/8;
713 printf("ScrollStepSize == %d\n", ScrollStepSize);
722 ScrollStepSize = TILEX/4;
727 ScrollStepSize = TILEX/8;
729 printf("MoveSpeed == %d\n", MoveSpeed);
734 ScrollStepSize = TILEX/8;
735 printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
739 ScrollStepSize = TILEX/4;
740 printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
744 ScrollStepSize = TILEX/2;
745 printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
749 ScrollStepSize = TILEX;
750 printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
755 local_player->dynamite = 1000;
766 for(i=0; i<MAX_PLAYERS; i++)
768 printf("Player %d:\n", i);
769 printf(" jx == %d, jy == %d\n",
770 stored_player[i].jx, stored_player[i].jy);
771 printf(" last_jx == %d, last_jy == %d\n",
772 stored_player[i].last_jx, stored_player[i].last_jy);
793 if (button_status && game_status != PLAYING)
795 HandleButton(0, 0, -button_status);
799 #if defined(PLATFORM_UNIX)
807 static int HandleJoystickForAllPlayers()
812 for (i=0; i<MAX_PLAYERS; i++)
817 if (!setup.input[i].use_joystick)
821 joy_action = Joystick(i);
822 result |= joy_action;
824 if (!setup.input[i].use_joystick)
827 stored_player[i].action = joy_action;
833 void HandleJoystick()
835 int joystick = HandleJoystickForAllPlayers();
836 int keyboard = key_joystick_mapping;
837 int joy = (joystick | keyboard);
838 int left = joy & JOY_LEFT;
839 int right = joy & JOY_RIGHT;
840 int up = joy & JOY_UP;
841 int down = joy & JOY_DOWN;
842 int button = joy & JOY_BUTTON;
843 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
844 int dx = (left ? -1 : right ? 1 : 0);
845 int dy = (up ? -1 : down ? 1 : 0);
853 static unsigned long joystickmove_delay = 0;
855 if (joystick && !button &&
856 !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
857 newbutton = dx = dy = 0;
859 if (game_status == MAINMENU)
860 HandleMainMenu(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
861 else if (game_status == CHOOSELEVEL)
862 HandleChooseLevel(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
863 else if (game_status == SETUP)
864 HandleSetupScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
869 HandleHallOfFame(0,0, dx,dy, !newbutton);
873 HandleHelpScreen(!newbutton);
877 HandleLevelEditorIdle();
881 if (tape.playing || keyboard)
882 newbutton = ((joy & JOY_BUTTON) != 0);
884 if (AllPlayersGone && newbutton)
886 CloseDoor(DOOR_CLOSE_1);
887 game_status = MAINMENU;