rnd-19990215-2
[rocksndiamonds.git] / src / events.c
1 /***********************************************************
2 *  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
3 *----------------------------------------------------------*
4 *  (c) 1995-98 Artsoft Entertainment                       *
5 *              Holger Schemel                              *
6 *              Oststrasse 11a                              *
7 *              33604 Bielefeld                             *
8 *              phone: ++49 +521 290471                     *
9 *              email: aeglos@valinor.owl.de                *
10 *----------------------------------------------------------*
11 *  events.c                                                *
12 ***********************************************************/
13
14 #include "events.h"
15 #include "init.h"
16 #include "screens.h"
17 #include "tools.h"
18 #include "game.h"
19 #include "editor.h"
20 #include "misc.h"
21 #include "tape.h"
22 #include "joystick.h"
23 #include "buttons.h"
24 #include "network.h"
25
26 /* values for key_status */
27 #define KEY_NOT_PRESSED         FALSE
28 #define KEY_RELEASED            FALSE
29 #define KEY_PRESSED             TRUE
30
31 void EventLoop(void)
32 {
33   while(1)
34   {
35     if (XPending(display))      /* got event from X server */
36     {
37       XEvent event;
38
39       XNextEvent(display, &event);
40
41       switch(event.type)
42       {
43         case ButtonPress:
44         case ButtonRelease:
45           HandleButtonEvent((XButtonEvent *) &event);
46           break;
47
48         case MotionNotify:
49           HandleMotionEvent((XMotionEvent *) &event);
50           break;
51
52         case KeyPress:
53         case KeyRelease:
54           HandleKeyEvent((XKeyEvent *) &event);
55           break;
56
57         default:
58           HandleOtherEvents(&event);
59           break;
60       }
61     }
62
63     HandleNoXEvent();
64
65     /* don't use all CPU time when idle; the main loop while playing
66        has its own synchronization and is CPU friendly, too */
67
68     if (game_status != PLAYING)
69     {
70       XSync(display, FALSE);
71       Delay(10);
72     }
73
74     /* refresh window contents from drawing buffer, if needed */
75     BackToFront();
76
77     if (game_status == EXITGAME)
78       return;
79   }
80 }
81
82 void HandleOtherEvents(XEvent *event)
83 {
84   switch(event->type)
85   {
86     case Expose:
87       HandleExposeEvent((XExposeEvent *) event);
88       break;
89
90     case UnmapNotify:
91       SleepWhileUnmapped();
92       break;
93
94     case FocusIn:
95     case FocusOut:
96       HandleFocusEvent((XFocusChangeEvent *) event);
97       break;
98
99     case ClientMessage:
100       HandleClientMessageEvent((XClientMessageEvent *) event);
101       break;
102
103     default:
104       break;
105   }
106 }
107
108 void ClearEventQueue()
109 {
110   while(XPending(display))
111   {
112     XEvent event;
113
114     XNextEvent(display, &event);
115
116     switch(event.type)
117     {
118       case ButtonRelease:
119         button_status = MB_RELEASED;
120         break;
121
122       case KeyRelease:
123         key_joystick_mapping = 0;
124         break;
125
126       default:
127         HandleOtherEvents(&event);
128         break;
129     }
130   }
131 }
132
133 void SleepWhileUnmapped()
134 {
135   boolean window_unmapped = TRUE;
136
137   XAutoRepeatOn(display);
138
139   while(window_unmapped)
140   {
141     XEvent event;
142
143     XNextEvent(display, &event);
144
145     switch(event.type)
146     {
147       case ButtonRelease:
148         button_status = MB_RELEASED;
149         break;
150
151       case KeyRelease:
152         key_joystick_mapping = 0;
153         break;
154
155       case MapNotify:
156         window_unmapped = FALSE;
157         break;
158
159       case UnmapNotify:
160         /* this is only to surely prevent the 'should not happen' case
161          * of recursively looping between 'SleepWhileUnmapped()' and
162          * 'HandleOtherEvents()' which usually calls this funtion.
163          */
164         break;
165
166       default:
167         HandleOtherEvents(&event);
168         break;
169     }
170   }
171
172   if (game_status == PLAYING)
173     XAutoRepeatOff(display);
174 }
175
176 void HandleExposeEvent(XExposeEvent *event)
177 {
178   int x = event->x, y = event->y;
179   int width = event->width, height = event->height;
180
181   if (setup.direct_draw && game_status==PLAYING)
182   {
183     int xx,yy;
184     int x1 = (x-SX)/TILEX, y1 = (y-SY)/TILEY;
185     int x2 = (x-SX+width)/TILEX, y2 = (y-SY+height)/TILEY;
186
187     SetDrawtoField(DRAW_BACKBUFFER);
188
189     for(xx=0; xx<SCR_FIELDX; xx++)
190       for(yy=0; yy<SCR_FIELDY; yy++)
191         if (xx>=x1 && xx<=x2 && yy>=y1 && yy<=y2)
192           DrawScreenField(xx,yy);
193     DrawAllPlayers();
194
195     SetDrawtoField(DRAW_DIRECT);
196   }
197
198   if (setup.soft_scrolling && game_status == PLAYING)
199   {
200     int fx = FX, fy = FY;
201
202     fx += (ScreenMovDir & (MV_LEFT|MV_RIGHT) ? ScreenGfxPos : 0);
203     fy += (ScreenMovDir & (MV_UP|MV_DOWN)    ? ScreenGfxPos : 0);
204
205     XCopyArea(display,fieldbuffer,backbuffer,gc,
206               fx,fy, SXSIZE,SYSIZE,
207               SX,SY);
208   }
209
210   XCopyArea(display,drawto,window,gc, x,y, width,height, x,y);
211
212   XFlush(display);
213 }
214
215 void HandleButtonEvent(XButtonEvent *event)
216 {
217   motion_status = FALSE;
218
219   if (event->type == ButtonPress)
220     button_status = event->button;
221   else
222     button_status = MB_RELEASED;
223
224   HandleButton(event->x, event->y, button_status);
225 }
226
227 void HandleMotionEvent(XMotionEvent *event)
228 {
229   Window root, child;
230   int root_x, root_y;
231   int win_x, win_y;
232   unsigned int mask;
233
234   if (!XQueryPointer(display, window, &root, &child, &root_x, &root_y,
235                      &win_x, &win_y, &mask))
236     return;
237
238   if (!button_status && game_status != LEVELED)
239     return;
240
241   motion_status = TRUE;
242
243   HandleButton(win_x, win_y, button_status);
244 }
245
246 void HandleKeyEvent(XKeyEvent *event)
247 {
248   int key_status = (event->type == KeyPress ? KEY_PRESSED : KEY_RELEASED);
249   KeySym key;
250
251   if (game_status == PLAYING)
252   {
253     /* use '0' instead of 'event->state' to get the key without modifiers */
254     key = XLookupKeysym(event, 0);
255   }
256   else
257   {
258     /* get the key with all modifiers */
259     char buffer[10];
260     int buffer_size = 10;
261     XComposeStatus compose;
262     int char_count;
263
264     char_count = XLookupString(event, buffer, buffer_size, &key, &compose);
265     buffer[char_count] = '\0';
266   }
267
268   HandleKey(key, key_status);
269 }
270
271 void HandleFocusEvent(XFocusChangeEvent *event)
272 {
273   static int old_joystick_status = -1;
274
275   if (event->type == FocusOut)
276   {
277     int i;
278
279     XAutoRepeatOn(display);
280     old_joystick_status = joystick_status;
281     joystick_status = JOYSTICK_OFF;
282
283     /* simulate key release events for still pressed keys */
284     key_joystick_mapping = 0;
285     for (i=0; i<MAX_PLAYERS; i++)
286       stored_player[i].action = 0;
287   }
288   else if (event->type == FocusIn)
289   {
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/50 second before
303        processing the 'FocusIn' event.
304     */
305
306     Delay(20);
307     if (game_status == PLAYING)
308       XAutoRepeatOff(display);
309     if (old_joystick_status != -1)
310       joystick_status = old_joystick_status;
311   }
312 }
313
314 void HandleClientMessageEvent(XClientMessageEvent *event)
315 {
316 #ifndef MSDOS
317   if ((event->window == window) &&
318       (event->data.l[0] == XInternAtom(display, "WM_DELETE_WINDOW", FALSE)))
319     CloseAllAndExit(0);
320 #endif
321 }
322
323 void HandleButton(int mx, int my, int button)
324 {
325   static int old_mx = 0, old_my = 0;
326
327   if (button < 0)
328   {
329     mx = old_mx;
330     my = old_my;
331     button = -button;
332   }
333   else
334   {
335     old_mx = mx;
336     old_my = my;
337   }
338
339   HandleGadgets(mx, my, button);
340
341   switch(game_status)
342   {
343     case MAINMENU:
344       HandleMainMenu(mx,my, 0,0, button);
345       break;
346
347     case TYPENAME:
348       HandleTypeName(0, XK_Return);
349       break;
350
351     case CHOOSELEVEL:
352       HandleChooseLevel(mx,my, 0,0, button);
353       break;
354
355     case HALLOFFAME:
356       HandleHallOfFame(button);
357       break;
358
359     case LEVELED:
360       break;
361
362     case HELPSCREEN:
363       HandleHelpScreen(button);
364       break;
365
366     case SETUP:
367       HandleSetupScreen(mx,my, 0,0, button);
368       break;
369
370     case SETUPINPUT:
371       HandleSetupInputScreen(mx,my, 0,0, button);
372       break;
373
374     case PLAYING:
375 #ifdef DEBUG
376       if (button == MB_RELEASED)
377       {
378         int sx = (mx - SX) / TILEX;
379         int sy = (my - SY) / TILEY;
380
381         if (IN_VIS_FIELD(sx,sy))
382         {
383           int x = LEVELX(sx);
384           int y = LEVELY(sy);
385
386           printf("INFO: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
387
388           if (!IN_LEV_FIELD(x, y))
389             break;
390
391           printf("      Feld[%d][%d] == %d\n", x,y, Feld[x][y]);
392           printf("      Store[%d][%d] == %d\n", x,y, Store[x][y]);
393           printf("      Store2[%d][%d] == %d\n", x,y, Store2[x][y]);
394           printf("      StorePlayer[%d][%d] == %d\n", x,y, StorePlayer[x][y]);
395           printf("      MovPos[%d][%d] == %d\n", x,y, MovPos[x][y]);
396           printf("      MovDir[%d][%d] == %d\n", x,y, MovDir[x][y]);
397           printf("      MovDelay[%d][%d] == %d\n", x,y, MovDelay[x][y]);
398           printf("\n");
399         }
400       }
401 #endif
402       break;
403
404     default:
405       break;
406   }
407 }
408
409 void HandleKey(KeySym key, int key_status)
410 {
411   int joy = 0;
412   static struct SetupKeyboardInfo custom_key;
413   static struct
414   {
415     KeySym *keysym_custom;
416     KeySym keysym_default;
417     byte action;
418   } key_info[] =
419   {
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 }
426   };
427
428   if (game_status == PLAYING)
429   {
430     int pnr;
431
432     for (pnr=0; pnr<MAX_PLAYERS; pnr++)
433     {
434       int i;
435       byte key_action = 0;
436
437       if (setup.input[pnr].use_joystick)
438         continue;
439
440       custom_key = setup.input[pnr].key;
441
442       for (i=0; i<6; i++)
443         if (key == *key_info[i].keysym_custom)
444           key_action |= key_info[i].action;
445
446       if (key_status == KEY_PRESSED)
447         stored_player[pnr].action |= key_action;
448       else
449         stored_player[pnr].action &= ~key_action;
450     }
451   }
452   else
453   {
454     int i;
455
456     for (i=0; i<6; i++)
457       if (key == key_info[i].keysym_default)
458         joy |= key_info[i].action;
459   }
460
461   if (joy)
462   {
463     if (key_status == KEY_PRESSED)
464       key_joystick_mapping |= joy;
465     else
466       key_joystick_mapping &= ~joy;
467
468     HandleJoystick();
469   }
470
471   if (game_status != PLAYING)
472     key_joystick_mapping = 0;
473
474   if (key_status == KEY_RELEASED)
475     return;
476
477   if (key == XK_Return && game_status == PLAYING && AllPlayersGone)
478   {
479     CloseDoor(DOOR_CLOSE_1);
480     game_status = MAINMENU;
481     DrawMainMenu();
482     return;
483   }
484
485   /* allow quick escape to the main menu with the Escape key */
486   if (key == XK_Escape && game_status != MAINMENU)
487   {
488     if (game_status == LEVELED)
489     {
490       /* draw smaller door */
491       XCopyArea(display, pix[PIX_DOOR], drawto, gc,
492                 DOOR_GFX_PAGEX7, 64,
493                 108, 64,
494                 EX - 4, EY - 12);
495       redraw_mask |= REDRAW_ALL;
496     }
497
498     CloseDoor(DOOR_CLOSE_1 | DOOR_OPEN_2 | DOOR_NO_DELAY);
499     game_status = MAINMENU;
500     DrawMainMenu();
501     return;
502   }
503
504
505
506 #ifndef DEBUG
507
508   if (game_status == PLAYING && (tape.playing || tape.pausing))
509     return;
510
511 #endif
512
513
514
515   HandleGadgetsKeyInput(key);
516
517   switch(game_status)
518   {
519     case TYPENAME:
520       HandleTypeName(0, key);
521       break;
522
523     case MAINMENU:
524     case CHOOSELEVEL:
525     case SETUP:
526     case SETUPINPUT:
527       switch(key)
528       {
529         case XK_Return:
530           if (game_status == MAINMENU)
531             HandleMainMenu(0,0, 0,0, MB_MENU_CHOICE);
532           else if (game_status == CHOOSELEVEL)
533             HandleChooseLevel(0,0, 0,0, MB_MENU_CHOICE);
534           else if (game_status == SETUP)
535             HandleSetupScreen(0,0, 0,0, MB_MENU_CHOICE);
536           else if (game_status == SETUPINPUT)
537             HandleSetupInputScreen(0,0, 0,0, MB_MENU_CHOICE);
538           break;
539
540         default:
541           break;
542       }
543       break;
544
545     case HELPSCREEN:
546       HandleHelpScreen(MB_RELEASED);
547       break;
548
549     case HALLOFFAME:
550       switch(key)
551       {
552         case XK_Return:
553           game_status = MAINMENU;
554           DrawMainMenu();
555           BackToFront();
556           break;
557
558         default:
559           break;
560       }
561       break;
562
563     case LEVELED:
564       HandleLevelEditorKeyInput(key);
565       break;
566
567     case PLAYING:
568     {
569       switch(key)
570       {
571
572 #ifdef DEBUG
573         case XK_0:
574         case XK_1:
575         case XK_2:
576         case XK_3:
577         case XK_4:
578         case XK_5:
579         case XK_6:
580         case XK_7:
581         case XK_8:
582         case XK_9:
583           if (key == XK_0)
584           {
585             if (GameFrameDelay == 500)
586               GameFrameDelay = GAME_FRAME_DELAY;
587             else
588               GameFrameDelay = 500;
589           }
590           else
591             GameFrameDelay = (key - XK_0) * 10;
592           printf("Game speed == %d%% (%d ms delay between two frames)\n",
593                  GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
594           break;
595
596
597 #if 0
598         case XK_a:
599           if (ScrollStepSize == TILEX/8)
600             ScrollStepSize = TILEX/4;
601           else
602             ScrollStepSize = TILEX/8;
603           printf("ScrollStepSize == %d\n", ScrollStepSize);
604           break;
605 #endif
606
607 #if 0
608         case XK_m:
609           if (MoveSpeed == 8)
610           {
611             MoveSpeed = 4;
612             ScrollStepSize = TILEX/4;
613           }
614           else
615           {
616             MoveSpeed = 8;
617             ScrollStepSize = TILEX/8;
618           }
619           printf("MoveSpeed == %d\n", MoveSpeed);
620           break;
621 #endif
622
623         case XK_f:
624           ScrollStepSize = TILEX/8;
625           printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
626           break;
627
628         case XK_g:
629           ScrollStepSize = TILEX/4;
630           printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
631           break;
632
633         case XK_h:
634           ScrollStepSize = TILEX/2;
635           printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
636           break;
637
638         case XK_l:
639           ScrollStepSize = TILEX;
640           printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
641           break;
642
643 #ifndef MSDOS
644         case XK_Q:
645 #endif
646         case XK_q:
647           local_player->dynamite = 1000;
648           break;
649
650
651
652 #if 0
653
654         case XK_z:
655           {
656             int i;
657
658             for(i=0; i<MAX_PLAYERS; i++)
659             {
660               printf("Player %d:\n", i);
661               printf("  jx == %d, jy == %d\n",
662                      stored_player[i].jx, stored_player[i].jy);
663               printf("  last_jx == %d, last_jy == %d\n",
664                      stored_player[i].last_jx, stored_player[i].last_jy);
665             }
666             printf("\n");
667           }
668
669           break;
670 #endif
671 #endif
672
673         default:
674           break;
675       }
676       break;
677     }
678     default:
679       break;
680   }
681 }
682
683 void HandleNoXEvent()
684 {
685   if (button_status && game_status != PLAYING)
686   {
687     HandleButton(0, 0, -button_status);
688     return;
689   }
690
691 #ifndef MSDOS
692   if (options.network)
693     HandleNetworking();
694 #endif
695
696   HandleJoystick();
697
698   if (game_status == PLAYING)
699     HandleGameActions();
700 }
701
702 static int HandleJoystickForAllPlayers()
703 {
704   int i;
705   int result = 0;
706
707   for (i=0; i<MAX_PLAYERS; i++)
708   {
709     byte joy_action = 0;
710
711     /*
712     if (!setup.input[i].use_joystick)
713       continue;
714       */
715
716     joy_action = Joystick(i);
717     result |= joy_action;
718
719
720     if (!setup.input[i].use_joystick)
721       continue;
722
723
724     stored_player[i].action = joy_action;
725   }
726
727   return result;
728 }
729
730 void HandleJoystick()
731 {
732   int joystick  = HandleJoystickForAllPlayers();
733   int keyboard  = key_joystick_mapping;
734   int joy       = (joystick | keyboard);
735   int left      = joy & JOY_LEFT;
736   int right     = joy & JOY_RIGHT;
737   int up        = joy & JOY_UP;
738   int down      = joy & JOY_DOWN;
739   int button    = joy & JOY_BUTTON;
740   int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
741   int dx        = (left ? -1    : right ? 1     : 0);
742   int dy        = (up   ? -1    : down  ? 1     : 0);
743
744   switch(game_status)
745   {
746     case MAINMENU:
747     case CHOOSELEVEL:
748     case SETUP:
749     case SETUPINPUT:
750     {
751       static unsigned long joystickmove_delay = 0;
752
753       if (joystick && !button &&
754           !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
755         newbutton = dx = dy = 0;
756
757       if (game_status==MAINMENU)
758         HandleMainMenu(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
759       else if (game_status==CHOOSELEVEL)
760         HandleChooseLevel(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
761       else if (game_status==SETUP)
762         HandleSetupScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
763       else if (game_status==SETUPINPUT)
764         HandleSetupInputScreen(0,0,dx,dy,
765                                newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
766       break;
767     }
768
769     case HALLOFFAME:
770       HandleHallOfFame(!newbutton);
771       break;
772
773     case HELPSCREEN:
774       HandleHelpScreen(!newbutton);
775       break;
776
777     case PLAYING:
778       if (tape.playing || keyboard)
779         newbutton = ((joy & JOY_BUTTON) != 0);
780
781       if (AllPlayersGone && newbutton)
782       {
783         CloseDoor(DOOR_CLOSE_1);
784         game_status = MAINMENU;
785         DrawMainMenu();
786         return;
787       }
788
789       break;
790
791     default:
792       break;
793   }
794 }