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:
129 SleepWhileUnmapped();
134 HandleFocusEvent((FocusChangeEvent *) event);
137 case EVENT_CLIENTMESSAGE:
138 HandleClientMessageEvent((ClientMessageEvent *) event);
141 #if defined(TARGET_SDL)
142 case SDL_JOYAXISMOTION:
143 case SDL_JOYBUTTONDOWN:
144 case SDL_JOYBUTTONUP:
145 HandleJoystickEvent(event);
154 void ClearEventQueue()
156 while (PendingEvent())
164 case EVENT_BUTTONRELEASE:
165 button_status = MB_RELEASED;
168 case EVENT_KEYRELEASE:
169 key_joystick_mapping = 0;
173 HandleOtherEvents(&event);
179 void ClearPlayerAction()
183 /* simulate key release events for still pressed keys */
184 key_joystick_mapping = 0;
185 for (i=0; i<MAX_PLAYERS; i++)
186 stored_player[i].action = 0;
189 void SleepWhileUnmapped()
191 boolean window_unmapped = TRUE;
193 KeyboardAutoRepeatOn();
195 while(window_unmapped)
203 case EVENT_BUTTONRELEASE:
204 button_status = MB_RELEASED;
207 case EVENT_KEYRELEASE:
208 key_joystick_mapping = 0;
211 case EVENT_MAPNOTIFY:
212 window_unmapped = FALSE;
215 case EVENT_UNMAPNOTIFY:
216 /* this is only to surely prevent the 'should not happen' case
217 * of recursively looping between 'SleepWhileUnmapped()' and
218 * 'HandleOtherEvents()' which usually calls this funtion.
223 HandleOtherEvents(&event);
228 if (game_status == PLAYING)
229 KeyboardAutoRepeatOff();
232 void HandleExposeEvent(ExposeEvent *event)
235 RedrawPlayfield(FALSE, event->x, event->y, event->width, event->height);
240 void HandleButtonEvent(ButtonEvent *event)
242 motion_status = FALSE;
244 if (event->type == EVENT_BUTTONPRESS)
245 button_status = event->button;
247 button_status = MB_RELEASED;
249 HandleButton(event->x, event->y, button_status);
252 void HandleMotionEvent(MotionEvent *event)
254 if (!PointerInWindow(window))
255 return; /* window and pointer are on different screens */
258 if (button_status == MB_RELEASED && game_status != LEVELED)
262 motion_status = TRUE;
264 HandleButton(event->x, event->y, button_status);
267 void HandleKeyEvent(KeyEvent *event)
269 int key_status = (event->type==EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
270 boolean with_modifiers = (game_status == PLAYING ? FALSE : TRUE);
271 Key key = GetEventKey(event, with_modifiers);
273 HandleKey(key, key_status);
276 void HandleFocusEvent(FocusChangeEvent *event)
278 static int old_joystick_status = -1;
280 if (event->type == EVENT_FOCUSOUT)
282 KeyboardAutoRepeatOn();
283 old_joystick_status = joystick.status;
284 joystick.status = JOYSTICK_NOT_AVAILABLE;
288 else if (event->type == EVENT_FOCUSIN)
290 /* When there are two Rocks'n'Diamonds windows which overlap and
291 the player moves the pointer from one game window to the other,
292 a 'FocusOut' event is generated for the window the pointer is
293 leaving and a 'FocusIn' event is generated for the window the
294 pointer is entering. In some cases, it can happen that the
295 'FocusIn' event is handled by the one game process before the
296 'FocusOut' event by the other game process. In this case the
297 X11 environment would end up with activated keyboard auto repeat,
298 because unfortunately this is a global setting and not (which
299 would be far better) set for each X11 window individually.
300 The effect would be keyboard auto repeat while playing the game
301 (game_status == PLAYING), which is not desired.
302 To avoid this special case, we just wait 1/10 second before
303 processing the 'FocusIn' event.
306 if (game_status == PLAYING)
309 KeyboardAutoRepeatOff();
311 if (old_joystick_status != -1)
312 joystick.status = old_joystick_status;
316 void HandleClientMessageEvent(ClientMessageEvent *event)
318 if (CheckCloseWindowEvent(event))
322 void HandleButton(int mx, int my, int button)
324 static int old_mx = 0, old_my = 0;
338 HandleGadgets(mx, my, button);
343 HandleMainMenu(mx,my, 0,0, button);
347 HandleTypeName(0, KSYM_Return);
351 HandleChooseLevel(mx,my, 0,0, button);
355 HandleHallOfFame(0,0, 0,0, button);
362 HandleHelpScreen(button);
366 HandleSetupScreen(mx,my, 0,0, button);
371 if (button == MB_RELEASED)
373 int sx = (mx - SX) / TILEX;
374 int sy = (my - SY) / TILEY;
376 if (IN_VIS_FIELD(sx,sy))
381 printf("INFO: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
383 if (!IN_LEV_FIELD(x, y))
386 printf(" Feld[%d][%d] == %d\n", x,y, Feld[x][y]);
387 printf(" Store[%d][%d] == %d\n", x,y, Store[x][y]);
388 printf(" Store2[%d][%d] == %d\n", x,y, Store2[x][y]);
389 printf(" StorePlayer[%d][%d] == %d\n", x,y, StorePlayer[x][y]);
390 printf(" MovPos[%d][%d] == %d\n", x,y, MovPos[x][y]);
391 printf(" MovDir[%d][%d] == %d\n", x,y, MovDir[x][y]);
392 printf(" MovDelay[%d][%d] == %d\n", x,y, MovDelay[x][y]);
404 void HandleKey(Key key, int key_status)
407 boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
408 static struct SetupKeyboardInfo custom_key;
416 { &custom_key.left, DEFAULT_KEY_LEFT, JOY_LEFT },
417 { &custom_key.right, DEFAULT_KEY_RIGHT, JOY_RIGHT },
418 { &custom_key.up, DEFAULT_KEY_UP, JOY_UP },
419 { &custom_key.down, DEFAULT_KEY_DOWN, JOY_DOWN },
420 { &custom_key.snap, DEFAULT_KEY_SNAP, JOY_BUTTON_1 },
421 { &custom_key.bomb, DEFAULT_KEY_BOMB, JOY_BUTTON_2 }
424 if (game_status == PLAYING)
426 /* only needed for single-step tape recording mode */
427 static boolean clear_button_2[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
428 static boolean bomb_placed[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
431 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
436 if (setup.input[pnr].use_joystick)
439 custom_key = setup.input[pnr].key;
442 if (key == *key_info[i].key_custom)
443 key_action |= key_info[i].action;
445 if (tape.single_step && clear_button_2[pnr])
447 stored_player[pnr].action &= ~KEY_BUTTON_2;
448 clear_button_2[pnr] = FALSE;
451 if (key_status == KEY_PRESSED)
452 stored_player[pnr].action |= key_action;
454 stored_player[pnr].action &= ~key_action;
456 if (tape.single_step && tape.recording && tape.pausing)
458 if (key_status == KEY_PRESSED &&
459 (key_action & (KEY_MOTION | KEY_BUTTON_1)))
461 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
463 if (key_action & KEY_MOTION)
465 if (stored_player[pnr].action & KEY_BUTTON_2)
466 bomb_placed[pnr] = TRUE;
469 else if (key_status == KEY_RELEASED &&
470 (key_action & KEY_BUTTON_2))
472 if (!bomb_placed[pnr])
474 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
476 stored_player[pnr].action |= KEY_BUTTON_2;
477 clear_button_2[pnr] = TRUE;
480 bomb_placed[pnr] = FALSE;
483 else if (tape.recording && tape.pausing && (key_action & KEY_ACTION))
484 TapeTogglePause(TAPE_TOGGLE_MANUAL);
492 if (key == key_info[i].key_default)
493 joy |= key_info[i].action;
498 if (key_status == KEY_PRESSED)
499 key_joystick_mapping |= joy;
501 key_joystick_mapping &= ~joy;
506 if (game_status != PLAYING)
507 key_joystick_mapping = 0;
509 if (key_status == KEY_RELEASED)
512 if ((key == KSYM_Return || key == setup.shortcut.toggle_pause) &&
513 game_status == PLAYING && AllPlayersGone)
515 CloseDoor(DOOR_CLOSE_1);
516 game_status = MAINMENU;
521 /* allow quick escape to the main menu with the Escape key */
522 if (key == KSYM_Escape &&
523 game_status != MAINMENU &&
524 game_status != PLAYING &&
525 game_status != LEVELED &&
526 game_status != CHOOSELEVEL &&
527 game_status != SETUP)
529 game_status = MAINMENU;
534 /* special key shortcuts */
535 if (game_status == MAINMENU || game_status == PLAYING)
537 if (key == setup.shortcut.save_game)
539 else if (key == setup.shortcut.load_game)
541 else if (key == setup.shortcut.toggle_pause)
542 TapeTogglePause(TAPE_TOGGLE_MANUAL);
548 if (game_status == PLAYING && (tape.playing || tape.pausing))
555 HandleGadgetsKeyInput(key);
560 HandleTypeName(0, key);
569 if (game_status == MAINMENU)
570 HandleMainMenu(0,0, 0,0, MB_MENU_CHOICE);
571 else if (game_status == CHOOSELEVEL)
572 HandleChooseLevel(0,0, 0,0, MB_MENU_CHOICE);
573 else if (game_status == SETUP)
574 HandleSetupScreen(0,0, 0,0, MB_MENU_CHOICE);
578 if (game_status == CHOOSELEVEL)
579 HandleChooseLevel(0,0, 0,0, MB_MENU_LEAVE);
580 else if (game_status == SETUP)
581 HandleSetupScreen(0,0, 0,0, MB_MENU_LEAVE);
585 if (game_status == CHOOSELEVEL)
586 HandleChooseLevel(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
587 else if (game_status == SETUP)
588 HandleSetupScreen(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
592 if (game_status == CHOOSELEVEL)
593 HandleChooseLevel(0,0, 0,SCR_FIELDY, MB_MENU_MARK);
594 else if (game_status == SETUP)
595 HandleSetupScreen(0,0, 0,SCR_FIELDY, MB_MENU_MARK);
610 HandleHelpScreen(MB_RELEASED);
617 game_status = MAINMENU;
623 HandleHallOfFame(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
627 HandleHallOfFame(0,0, 0,SCR_FIELDY, MB_MENU_MARK);
636 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
637 HandleLevelEditorKeyInput(key);
645 RequestQuitGame(setup.ask_on_escape);
661 if (GameFrameDelay == 500)
662 GameFrameDelay = GAME_FRAME_DELAY;
664 GameFrameDelay = 500;
667 GameFrameDelay = (key - KSYM_0) * 10;
668 printf("Game speed == %d%% (%d ms delay between two frames)\n",
669 GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
675 options.debug = FALSE;
676 printf("debug mode disabled\n");
680 options.debug = TRUE;
681 printf("debug mode enabled\n");
686 if (!global.fps_slowdown)
688 global.fps_slowdown = TRUE;
689 global.fps_slowdown_factor = 2;
690 printf("fps slowdown enabled -- display only every 2nd frame\n");
692 else if (global.fps_slowdown_factor == 2)
694 global.fps_slowdown_factor = 4;
695 printf("fps slowdown enabled -- display only every 4th frame\n");
699 global.fps_slowdown = FALSE;
700 global.fps_slowdown_factor = 1;
701 printf("fps slowdown disabled\n");
707 if (ScrollStepSize == TILEX/8)
708 ScrollStepSize = TILEX/4;
710 ScrollStepSize = TILEX/8;
711 printf("ScrollStepSize == %d\n", ScrollStepSize);
720 ScrollStepSize = TILEX/4;
725 ScrollStepSize = TILEX/8;
727 printf("MoveSpeed == %d\n", MoveSpeed);
732 ScrollStepSize = TILEX/8;
733 printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
737 ScrollStepSize = TILEX/4;
738 printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
742 ScrollStepSize = TILEX/2;
743 printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
747 ScrollStepSize = TILEX;
748 printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
753 local_player->dynamite = 1000;
764 for(i=0; i<MAX_PLAYERS; i++)
766 printf("Player %d:\n", i);
767 printf(" jx == %d, jy == %d\n",
768 stored_player[i].jx, stored_player[i].jy);
769 printf(" last_jx == %d, last_jy == %d\n",
770 stored_player[i].last_jx, stored_player[i].last_jy);
791 if (button_status && game_status != PLAYING)
793 HandleButton(0, 0, -button_status);
797 #if defined(PLATFORM_UNIX)
805 static int HandleJoystickForAllPlayers()
810 for (i=0; i<MAX_PLAYERS; i++)
815 if (!setup.input[i].use_joystick)
819 joy_action = Joystick(i);
820 result |= joy_action;
822 if (!setup.input[i].use_joystick)
825 stored_player[i].action = joy_action;
831 void HandleJoystick()
833 int joystick = HandleJoystickForAllPlayers();
834 int keyboard = key_joystick_mapping;
835 int joy = (joystick | keyboard);
836 int left = joy & JOY_LEFT;
837 int right = joy & JOY_RIGHT;
838 int up = joy & JOY_UP;
839 int down = joy & JOY_DOWN;
840 int button = joy & JOY_BUTTON;
841 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
842 int dx = (left ? -1 : right ? 1 : 0);
843 int dy = (up ? -1 : down ? 1 : 0);
851 static unsigned long joystickmove_delay = 0;
853 if (joystick && !button &&
854 !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
855 newbutton = dx = dy = 0;
857 if (game_status == MAINMENU)
858 HandleMainMenu(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
859 else if (game_status == CHOOSELEVEL)
860 HandleChooseLevel(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
861 else if (game_status == SETUP)
862 HandleSetupScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
867 HandleHallOfFame(0,0, dx,dy, !newbutton);
871 HandleHelpScreen(!newbutton);
875 if (tape.playing || keyboard)
876 newbutton = ((joy & JOY_BUTTON) != 0);
878 if (AllPlayersGone && newbutton)
880 CloseDoor(DOOR_CLOSE_1);
881 game_status = MAINMENU;