1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2001 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);
590 if (game_status == CHOOSELEVEL)
591 HandleChooseLevel(0,0, 0,SCR_FIELDY, MB_MENU_MARK);
606 HandleHelpScreen(MB_RELEASED);
613 game_status = MAINMENU;
619 HandleHallOfFame(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
623 HandleHallOfFame(0,0, 0,SCR_FIELDY, MB_MENU_MARK);
632 if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
633 HandleLevelEditorKeyInput(key);
641 RequestQuitGame(setup.ask_on_escape);
657 if (GameFrameDelay == 500)
658 GameFrameDelay = GAME_FRAME_DELAY;
660 GameFrameDelay = 500;
663 GameFrameDelay = (key - KSYM_0) * 10;
664 printf("Game speed == %d%% (%d ms delay between two frames)\n",
665 GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
671 options.debug = FALSE;
672 printf("debug mode disabled\n");
676 options.debug = TRUE;
677 printf("debug mode enabled\n");
682 if (!global.fps_slowdown)
684 global.fps_slowdown = TRUE;
685 global.fps_slowdown_factor = 2;
686 printf("fps slowdown enabled -- display only every 2nd frame\n");
688 else if (global.fps_slowdown_factor == 2)
690 global.fps_slowdown_factor = 4;
691 printf("fps slowdown enabled -- display only every 4th frame\n");
695 global.fps_slowdown = FALSE;
696 global.fps_slowdown_factor = 1;
697 printf("fps slowdown disabled\n");
703 if (ScrollStepSize == TILEX/8)
704 ScrollStepSize = TILEX/4;
706 ScrollStepSize = TILEX/8;
707 printf("ScrollStepSize == %d\n", ScrollStepSize);
716 ScrollStepSize = TILEX/4;
721 ScrollStepSize = TILEX/8;
723 printf("MoveSpeed == %d\n", MoveSpeed);
728 ScrollStepSize = TILEX/8;
729 printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
733 ScrollStepSize = TILEX/4;
734 printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
738 ScrollStepSize = TILEX/2;
739 printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
743 ScrollStepSize = TILEX;
744 printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
749 local_player->dynamite = 1000;
760 for(i=0; i<MAX_PLAYERS; i++)
762 printf("Player %d:\n", i);
763 printf(" jx == %d, jy == %d\n",
764 stored_player[i].jx, stored_player[i].jy);
765 printf(" last_jx == %d, last_jy == %d\n",
766 stored_player[i].last_jx, stored_player[i].last_jy);
787 if (button_status && game_status != PLAYING)
789 HandleButton(0, 0, -button_status);
793 #if defined(PLATFORM_UNIX)
801 static int HandleJoystickForAllPlayers()
806 for (i=0; i<MAX_PLAYERS; i++)
811 if (!setup.input[i].use_joystick)
815 joy_action = Joystick(i);
816 result |= joy_action;
818 if (!setup.input[i].use_joystick)
821 stored_player[i].action = joy_action;
827 void HandleJoystick()
829 int joystick = HandleJoystickForAllPlayers();
830 int keyboard = key_joystick_mapping;
831 int joy = (joystick | keyboard);
832 int left = joy & JOY_LEFT;
833 int right = joy & JOY_RIGHT;
834 int up = joy & JOY_UP;
835 int down = joy & JOY_DOWN;
836 int button = joy & JOY_BUTTON;
837 int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
838 int dx = (left ? -1 : right ? 1 : 0);
839 int dy = (up ? -1 : down ? 1 : 0);
847 static unsigned long joystickmove_delay = 0;
849 if (joystick && !button &&
850 !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
851 newbutton = dx = dy = 0;
853 if (game_status==MAINMENU)
854 HandleMainMenu(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
855 else if (game_status==CHOOSELEVEL)
856 HandleChooseLevel(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
857 else if (game_status==SETUP)
858 HandleSetupScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
863 HandleHallOfFame(0,0, dx,dy, !newbutton);
867 HandleHelpScreen(!newbutton);
871 if (tape.playing || keyboard)
872 newbutton = ((joy & JOY_BUTTON) != 0);
874 if (AllPlayersGone && newbutton)
876 CloseDoor(DOOR_CLOSE_1);
877 game_status = MAINMENU;