/***********************************************************
-* Rocks'n'Diamonds -- McDuffin Strikes Back! *
+* Rocks'n'Diamonds -- McDuffin Strikes Back! *
*----------------------------------------------------------*
-* (c) 1995-98 Artsoft Entertainment *
-* Holger Schemel *
-* Oststrasse 11a *
-* 33604 Bielefeld *
-* phone: ++49 +521 290471 *
-* email: aeglos@valinor.owl.de *
+* (c) 1995-2001 Artsoft Entertainment *
+* Holger Schemel *
+* Detmolder Strasse 189 *
+* 33604 Bielefeld *
+* Germany *
+* e-mail: info@artsoft.org *
*----------------------------------------------------------*
-* events.c *
+* events.c *
***********************************************************/
+#include "libgame/libgame.h"
+
#include "events.h"
#include "init.h"
#include "screens.h"
#include "tools.h"
#include "game.h"
#include "editor.h"
-#include "misc.h"
+#include "files.h"
#include "tape.h"
-#include "joystick.h"
-#include "buttons.h"
#include "network.h"
/* values for key_status */
#define KEY_RELEASED FALSE
#define KEY_PRESSED TRUE
+
+/* event filter especially needed for SDL event filtering due to
+ delay problems with lots of mouse motion events when mouse
+ button not pressed */
+
+int FilterMouseMotionEvents(const Event *event)
+{
+ if (event->type != EVENT_MOTIONNOTIFY)
+ return 1;
+
+ /* get mouse motion events without pressed button only in level editor */
+ if (button_status == MB_RELEASED && game_status != LEVELED)
+ return 0;
+ else
+ return 1;
+}
+
+/* this is only really needed for non-SDL targets to filter unwanted events;
+ when using SDL with properly installed event filter, this function can be
+ replaced with a simple "NextEvent()" call, but it doesn't hurt either */
+
+static boolean NextValidEvent(Event *event)
+{
+ while (PendingEvent())
+ {
+ NextEvent(event);
+
+ if (FilterMouseMotionEvents(event))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
void EventLoop(void)
{
while(1)
{
- if (XPending(display)) /* got event from X server */
+ if (PendingEvent()) /* got event */
{
- XEvent event;
-
- XNextEvent(display, &event);
+ Event event;
- switch(event.type)
+ if (NextValidEvent(&event))
{
- case ButtonPress:
- case ButtonRelease:
- HandleButtonEvent((XButtonEvent *) &event);
- break;
-
- case MotionNotify:
- HandleMotionEvent((XMotionEvent *) &event);
- break;
-
- case KeyPress:
- case KeyRelease:
- HandleKeyEvent((XKeyEvent *) &event);
- break;
-
- default:
- HandleOtherEvents(&event);
- break;
+ switch(event.type)
+ {
+ case EVENT_BUTTONPRESS:
+ case EVENT_BUTTONRELEASE:
+ HandleButtonEvent((ButtonEvent *) &event);
+ break;
+
+ case EVENT_MOTIONNOTIFY:
+ HandleMotionEvent((MotionEvent *) &event);
+ break;
+
+ case EVENT_KEYPRESS:
+ case EVENT_KEYRELEASE:
+ HandleKeyEvent((KeyEvent *) &event);
+ break;
+
+ default:
+ HandleOtherEvents(&event);
+ break;
+ }
}
}
-
- HandleNoXEvent();
+ else
+ HandleNoEvent();
/* don't use all CPU time when idle; the main loop while playing
has its own synchronization and is CPU friendly, too */
- if (game_status != PLAYING)
+ if (game_status == PLAYING)
+ HandleGameActions();
+ else
{
- XSync(display, FALSE);
- Delay(10);
+ SyncDisplay();
+ if (!PendingEvent()) /* delay only if no pending events */
+ Delay(10);
}
/* refresh window contents from drawing buffer, if needed */
}
}
-void HandleOtherEvents(XEvent *event)
+void HandleOtherEvents(Event *event)
{
switch(event->type)
{
- case Expose:
- HandleExposeEvent((XExposeEvent *) event);
+ case EVENT_EXPOSE:
+ HandleExposeEvent((ExposeEvent *) event);
break;
- case UnmapNotify:
+ case EVENT_UNMAPNOTIFY:
SleepWhileUnmapped();
break;
- case FocusIn:
- case FocusOut:
- HandleFocusEvent((XFocusChangeEvent *) event);
+ case EVENT_FOCUSIN:
+ case EVENT_FOCUSOUT:
+ HandleFocusEvent((FocusChangeEvent *) event);
+ break;
+
+ case EVENT_CLIENTMESSAGE:
+ HandleClientMessageEvent((ClientMessageEvent *) event);
break;
- case ClientMessage:
- HandleClientMessageEvent((XClientMessageEvent *) event);
+#if defined(TARGET_SDL)
+ case SDL_JOYAXISMOTION:
+ case SDL_JOYBUTTONDOWN:
+ case SDL_JOYBUTTONUP:
+ HandleJoystickEvent(event);
break;
+#endif
default:
break;
void ClearEventQueue()
{
- while(XPending(display))
+ while (PendingEvent())
{
- XEvent event;
+ Event event;
- XNextEvent(display, &event);
+ NextEvent(&event);
switch(event.type)
{
- case ButtonRelease:
+ case EVENT_BUTTONRELEASE:
button_status = MB_RELEASED;
break;
- case KeyRelease:
+ case EVENT_KEYRELEASE:
key_joystick_mapping = 0;
break;
}
}
+void ClearPlayerAction()
+{
+ int i;
+
+ /* simulate key release events for still pressed keys */
+ key_joystick_mapping = 0;
+ for (i=0; i<MAX_PLAYERS; i++)
+ stored_player[i].action = 0;
+}
+
void SleepWhileUnmapped()
{
boolean window_unmapped = TRUE;
- XAutoRepeatOn(display);
+ KeyboardAutoRepeatOn();
while(window_unmapped)
{
- XEvent event;
+ Event event;
- XNextEvent(display, &event);
+ NextEvent(&event);
switch(event.type)
{
- case ButtonRelease:
+ case EVENT_BUTTONRELEASE:
button_status = MB_RELEASED;
break;
- case KeyRelease:
+ case EVENT_KEYRELEASE:
key_joystick_mapping = 0;
break;
- case MapNotify:
+ case EVENT_MAPNOTIFY:
window_unmapped = FALSE;
break;
- case UnmapNotify:
+ case EVENT_UNMAPNOTIFY:
/* this is only to surely prevent the 'should not happen' case
* of recursively looping between 'SleepWhileUnmapped()' and
* 'HandleOtherEvents()' which usually calls this funtion.
}
if (game_status == PLAYING)
- XAutoRepeatOff(display);
+ KeyboardAutoRepeatOff();
}
-void HandleExposeEvent(XExposeEvent *event)
+void HandleExposeEvent(ExposeEvent *event)
{
- int x = event->x, y = event->y;
- int width = event->width, height = event->height;
-
- if (setup.direct_draw && game_status==PLAYING)
- {
- int xx,yy;
- int x1 = (x-SX)/TILEX, y1 = (y-SY)/TILEY;
- int x2 = (x-SX+width)/TILEX, y2 = (y-SY+height)/TILEY;
-
- SetDrawtoField(DRAW_BACKBUFFER);
-
- for(xx=0; xx<SCR_FIELDX; xx++)
- for(yy=0; yy<SCR_FIELDY; yy++)
- if (xx>=x1 && xx<=x2 && yy>=y1 && yy<=y2)
- DrawScreenField(xx,yy);
- DrawAllPlayers();
-
- SetDrawtoField(DRAW_DIRECT);
- }
-
- if (setup.soft_scrolling && game_status == PLAYING)
- {
- int fx = FX, fy = FY;
-
- fx += (ScreenMovDir & (MV_LEFT|MV_RIGHT) ? ScreenGfxPos : 0);
- fy += (ScreenMovDir & (MV_UP|MV_DOWN) ? ScreenGfxPos : 0);
-
- XCopyArea(display,fieldbuffer,backbuffer,gc,
- fx,fy, SXSIZE,SYSIZE,
- SX,SY);
- }
-
- XCopyArea(display,drawto,window,gc, x,y, width,height, x,y);
-
- XFlush(display);
+#ifndef TARGET_SDL
+ RedrawPlayfield(FALSE, event->x, event->y, event->width, event->height);
+ FlushDisplay();
+#endif
}
-void HandleButtonEvent(XButtonEvent *event)
+void HandleButtonEvent(ButtonEvent *event)
{
motion_status = FALSE;
- if (event->type == ButtonPress)
+ if (event->type == EVENT_BUTTONPRESS)
button_status = event->button;
else
button_status = MB_RELEASED;
HandleButton(event->x, event->y, button_status);
}
-void HandleMotionEvent(XMotionEvent *event)
+void HandleMotionEvent(MotionEvent *event)
{
- Window root, child;
- int root_x, root_y;
- int win_x, win_y;
- unsigned int mask;
-
- if (!XQueryPointer(display, window, &root, &child, &root_x, &root_y,
- &win_x, &win_y, &mask))
- return;
+ if (!PointerInWindow(window))
+ return; /* window and pointer are on different screens */
- if (!button_status && game_status != LEVELED)
+#if 1
+ if (button_status == MB_RELEASED && game_status != LEVELED)
return;
+#endif
motion_status = TRUE;
- HandleButton(win_x, win_y, button_status);
+ HandleButton(event->x, event->y, button_status);
}
-void HandleKeyEvent(XKeyEvent *event)
+void HandleKeyEvent(KeyEvent *event)
{
- int key_status = (event->type == KeyPress ? KEY_PRESSED : KEY_RELEASED);
- KeySym key;
-
- if (game_status == PLAYING)
- {
- /* use '0' instead of 'event->state' to get the key without modifiers */
- key = XLookupKeysym(event, 0);
- }
- else
- {
- /* get the key with all modifiers */
- char buffer[10];
- int buffer_size = 10;
- XComposeStatus compose;
- int char_count;
-
- char_count = XLookupString(event, buffer, buffer_size, &key, &compose);
- buffer[char_count] = '\0';
- }
+ int key_status = (event->type==EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
+ boolean with_modifiers = (game_status == PLAYING ? FALSE : TRUE);
+ Key key = GetEventKey(event, with_modifiers);
HandleKey(key, key_status);
}
-void HandleFocusEvent(XFocusChangeEvent *event)
+void HandleFocusEvent(FocusChangeEvent *event)
{
static int old_joystick_status = -1;
- if (event->type == FocusOut)
+ if (event->type == EVENT_FOCUSOUT)
{
- int i;
-
- XAutoRepeatOn(display);
- old_joystick_status = joystick_status;
- joystick_status = JOYSTICK_OFF;
+ KeyboardAutoRepeatOn();
+ old_joystick_status = joystick.status;
+ joystick.status = JOYSTICK_NOT_AVAILABLE;
- /* simulate key release events for still pressed keys */
- key_joystick_mapping = 0;
- for (i=0; i<MAX_PLAYERS; i++)
- stored_player[i].action = 0;
+ ClearPlayerAction();
}
- else if (event->type == FocusIn)
+ else if (event->type == EVENT_FOCUSIN)
{
/* When there are two Rocks'n'Diamonds windows which overlap and
the player moves the pointer from one game window to the other,
would be far better) set for each X11 window individually.
The effect would be keyboard auto repeat while playing the game
(game_status == PLAYING), which is not desired.
- To avoid this special case, we just wait 1/50 second before
+ To avoid this special case, we just wait 1/10 second before
processing the 'FocusIn' event.
*/
- Delay(20);
if (game_status == PLAYING)
- XAutoRepeatOff(display);
+ {
+ Delay(100);
+ KeyboardAutoRepeatOff();
+ }
if (old_joystick_status != -1)
- joystick_status = old_joystick_status;
+ joystick.status = old_joystick_status;
}
}
-void HandleClientMessageEvent(XClientMessageEvent *event)
+void HandleClientMessageEvent(ClientMessageEvent *event)
{
-#ifndef MSDOS
- if ((event->window == window) &&
- (event->data.l[0] == XInternAtom(display, "WM_DELETE_WINDOW", FALSE)))
+ if (CheckCloseWindowEvent(event))
CloseAllAndExit(0);
-#endif
}
void HandleButton(int mx, int my, int button)
{
old_mx = mx;
old_my = my;
-
- /*
- HandleVideoButtons(mx,my, button);
- HandleSoundButtons(mx,my, button);
- HandleGameButtons(mx,my, button);
- */
}
HandleGadgets(mx, my, button);
break;
case TYPENAME:
- HandleTypeName(0, XK_Return);
+ HandleTypeName(0, KSYM_Return);
break;
case CHOOSELEVEL:
break;
case HALLOFFAME:
- HandleHallOfFame(button);
+ HandleHallOfFame(0,0, 0,0, button);
break;
case LEVELED:
HandleSetupScreen(mx,my, 0,0, button);
break;
- case SETUPINPUT:
- HandleSetupInputScreen(mx,my, 0,0, button);
- break;
-
case PLAYING:
#ifdef DEBUG
if (button == MB_RELEASED)
}
}
-void HandleKey(KeySym key, int key_status)
+void HandleKey(Key key, int key_status)
{
int joy = 0;
+ boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
static struct SetupKeyboardInfo custom_key;
static struct
{
- KeySym *keysym_custom;
- KeySym keysym_default;
+ Key *key_custom;
+ Key key_default;
byte action;
} key_info[] =
{
if (game_status == PLAYING)
{
+ /* only needed for single-step tape recording mode */
+ static boolean clear_button_2[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
+ static boolean bomb_placed[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
int pnr;
for (pnr=0; pnr<MAX_PLAYERS; pnr++)
custom_key = setup.input[pnr].key;
for (i=0; i<6; i++)
- if (key == *key_info[i].keysym_custom)
+ if (key == *key_info[i].key_custom)
key_action |= key_info[i].action;
+ if (tape.single_step && clear_button_2[pnr])
+ {
+ stored_player[pnr].action &= ~KEY_BUTTON_2;
+ clear_button_2[pnr] = FALSE;
+ }
+
if (key_status == KEY_PRESSED)
stored_player[pnr].action |= key_action;
else
stored_player[pnr].action &= ~key_action;
+
+ if (tape.single_step && tape.recording && tape.pausing)
+ {
+ if (key_status == KEY_PRESSED &&
+ (key_action & (KEY_MOTION | KEY_BUTTON_1)))
+ {
+ TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
+
+ if (key_action & KEY_MOTION)
+ {
+ if (stored_player[pnr].action & KEY_BUTTON_2)
+ bomb_placed[pnr] = TRUE;
+ }
+ }
+ else if (key_status == KEY_RELEASED &&
+ (key_action & KEY_BUTTON_2))
+ {
+ if (!bomb_placed[pnr])
+ {
+ TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
+
+ stored_player[pnr].action |= KEY_BUTTON_2;
+ clear_button_2[pnr] = TRUE;
+ }
+
+ bomb_placed[pnr] = FALSE;
+ }
+ }
+ else if (tape.recording && tape.pausing && (key_action & KEY_ACTION))
+ TapeTogglePause(TAPE_TOGGLE_MANUAL);
}
}
else
int i;
for (i=0; i<6; i++)
- if (key == key_info[i].keysym_default)
+ if (key == key_info[i].key_default)
joy |= key_info[i].action;
}
if (key_status == KEY_RELEASED)
return;
- if (key == XK_Return && game_status == PLAYING && AllPlayersGone)
+ if ((key == KSYM_Return || key == setup.shortcut.toggle_pause) &&
+ game_status == PLAYING && AllPlayersGone)
{
CloseDoor(DOOR_CLOSE_1);
game_status = MAINMENU;
}
/* allow quick escape to the main menu with the Escape key */
- if (key == XK_Escape && game_status != MAINMENU)
+ if (key == KSYM_Escape &&
+ game_status != MAINMENU &&
+ game_status != PLAYING &&
+ game_status != LEVELED &&
+ game_status != CHOOSELEVEL &&
+ game_status != SETUP)
{
- if (game_status == LEVELED)
- {
- /* draw smaller door */
- XCopyArea(display, pix[PIX_DOOR], drawto, gc,
- DOOR_GFX_PAGEX7, 64,
- 108, 64,
- EX - 4, EY - 12);
- redraw_mask |= REDRAW_ALL;
- }
-
- CloseDoor(DOOR_CLOSE_1 | DOOR_OPEN_2 | DOOR_NO_DELAY);
game_status = MAINMENU;
DrawMainMenu();
return;
}
+ /* special key shortcuts */
+ if (game_status == MAINMENU || game_status == PLAYING)
+ {
+ if (key == setup.shortcut.save_game)
+ TapeQuickSave();
+ else if (key == setup.shortcut.load_game)
+ TapeQuickLoad();
+ else if (key == setup.shortcut.toggle_pause)
+ TapeTogglePause(TAPE_TOGGLE_MANUAL);
+ }
#ifndef DEBUG
case MAINMENU:
case CHOOSELEVEL:
case SETUP:
- case SETUPINPUT:
switch(key)
{
- case XK_Return:
+ case KSYM_Return:
if (game_status == MAINMENU)
HandleMainMenu(0,0, 0,0, MB_MENU_CHOICE);
else if (game_status == CHOOSELEVEL)
HandleChooseLevel(0,0, 0,0, MB_MENU_CHOICE);
else if (game_status == SETUP)
HandleSetupScreen(0,0, 0,0, MB_MENU_CHOICE);
- else if (game_status == SETUPINPUT)
- HandleSetupInputScreen(0,0, 0,0, MB_MENU_CHOICE);
break;
+ case KSYM_Escape:
+ if (game_status == CHOOSELEVEL)
+ HandleChooseLevel(0,0, 0,0, MB_MENU_LEAVE);
+ else if (game_status == SETUP)
+ HandleSetupScreen(0,0, 0,0, MB_MENU_LEAVE);
+ break;
+
+ case KSYM_Page_Up:
+ if (game_status == CHOOSELEVEL)
+ HandleChooseLevel(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
+ break;
+
+ case KSYM_Page_Down:
+ if (game_status == CHOOSELEVEL)
+ HandleChooseLevel(0,0, 0,SCR_FIELDY, MB_MENU_MARK);
+ break;
+
+#ifdef DEBUG
+ case KSYM_t:
+ DumpTape(&tape);
+ break;
+#endif
+
default:
break;
}
case HALLOFFAME:
switch(key)
{
- case XK_Return:
+ case KSYM_Return:
game_status = MAINMENU;
DrawMainMenu();
BackToFront();
break;
+ case KSYM_Page_Up:
+ HandleHallOfFame(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
+ break;
+
+ case KSYM_Page_Down:
+ HandleHallOfFame(0,0, 0,SCR_FIELDY, MB_MENU_MARK);
+ break;
+
default:
break;
}
break;
case LEVELED:
- HandleLevelEditorKeyInput(key);
+ if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
+ HandleLevelEditorKeyInput(key);
break;
case PLAYING:
{
switch(key)
{
+ case KSYM_Escape:
+ RequestQuitGame(setup.ask_on_escape);
+ break;
#ifdef DEBUG
- case XK_0:
- case XK_1:
- case XK_2:
- case XK_3:
- case XK_4:
- case XK_5:
- case XK_6:
- case XK_7:
- case XK_8:
- case XK_9:
- if (key == XK_0)
+ case KSYM_0:
+ case KSYM_1:
+ case KSYM_2:
+ case KSYM_3:
+ case KSYM_4:
+ case KSYM_5:
+ case KSYM_6:
+ case KSYM_7:
+ case KSYM_8:
+ case KSYM_9:
+ if (key == KSYM_0)
{
if (GameFrameDelay == 500)
GameFrameDelay = GAME_FRAME_DELAY;
GameFrameDelay = 500;
}
else
- GameFrameDelay = (key - XK_0) * 10;
+ GameFrameDelay = (key - KSYM_0) * 10;
printf("Game speed == %d%% (%d ms delay between two frames)\n",
GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
break;
+ case KSYM_d:
+ if (options.debug)
+ {
+ options.debug = FALSE;
+ printf("debug mode disabled\n");
+ }
+ else
+ {
+ options.debug = TRUE;
+ printf("debug mode enabled\n");
+ }
+ break;
+
+ case KSYM_s:
+ if (!global.fps_slowdown)
+ {
+ global.fps_slowdown = TRUE;
+ global.fps_slowdown_factor = 2;
+ printf("fps slowdown enabled -- display only every 2nd frame\n");
+ }
+ else if (global.fps_slowdown_factor == 2)
+ {
+ global.fps_slowdown_factor = 4;
+ printf("fps slowdown enabled -- display only every 4th frame\n");
+ }
+ else
+ {
+ global.fps_slowdown = FALSE;
+ global.fps_slowdown_factor = 1;
+ printf("fps slowdown disabled\n");
+ }
+ break;
#if 0
- case XK_a:
+ case KSYM_a:
if (ScrollStepSize == TILEX/8)
ScrollStepSize = TILEX/4;
else
break;
#endif
-#if 1
- case XK_m:
+#if 0
+ case KSYM_m:
if (MoveSpeed == 8)
{
MoveSpeed = 4;
break;
#endif
- case XK_f:
+ case KSYM_f:
ScrollStepSize = TILEX/8;
printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
break;
- case XK_g:
+ case KSYM_g:
ScrollStepSize = TILEX/4;
printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
break;
- case XK_h:
+ case KSYM_h:
ScrollStepSize = TILEX/2;
printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
break;
- case XK_l:
+ case KSYM_l:
ScrollStepSize = TILEX;
printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
break;
-#ifndef MSDOS
- case XK_Q:
-#endif
- case XK_q:
+ case KSYM_Q:
+ case KSYM_q:
local_player->dynamite = 1000;
break;
#if 0
- case XK_z:
+ case KSYM_z:
{
int i;
}
}
-void HandleNoXEvent()
+void HandleNoEvent()
{
if (button_status && game_status != PLAYING)
{
return;
}
-#ifndef MSDOS
+#if defined(PLATFORM_UNIX)
if (options.network)
HandleNetworking();
#endif
HandleJoystick();
-
- if (game_status == PLAYING)
- HandleGameActions();
}
static int HandleJoystickForAllPlayers()
joy_action = Joystick(i);
result |= joy_action;
-
if (!setup.input[i].use_joystick)
continue;
-
stored_player[i].action = joy_action;
}
case MAINMENU:
case CHOOSELEVEL:
case SETUP:
- case SETUPINPUT:
{
static unsigned long joystickmove_delay = 0;
HandleChooseLevel(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
else if (game_status==SETUP)
HandleSetupScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
- else if (game_status==SETUPINPUT)
- HandleSetupInputScreen(0,0,dx,dy,
- newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
break;
}
case HALLOFFAME:
- HandleHallOfFame(!newbutton);
+ HandleHallOfFame(0,0, dx,dy, !newbutton);
break;
case HELPSCREEN: