rnd-19990205-1
[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     HandleVideoButtons(mx,my, button);
340     HandleSoundButtons(mx,my, button);
341     HandleGameButtons(mx,my, button);
342     */
343   }
344
345   HandleGadgets(mx, my, button);
346
347   switch(game_status)
348   {
349     case MAINMENU:
350       HandleMainMenu(mx,my, 0,0, button);
351       break;
352
353     case TYPENAME:
354       HandleTypeName(0, XK_Return);
355       break;
356
357     case CHOOSELEVEL:
358       HandleChooseLevel(mx,my, 0,0, button);
359       break;
360
361     case HALLOFFAME:
362       HandleHallOfFame(button);
363       break;
364
365     case LEVELED:
366       break;
367
368     case HELPSCREEN:
369       HandleHelpScreen(button);
370       break;
371
372     case SETUP:
373       HandleSetupScreen(mx,my, 0,0, button);
374       break;
375
376     case SETUPINPUT:
377       HandleSetupInputScreen(mx,my, 0,0, button);
378       break;
379
380     case PLAYING:
381 #ifdef DEBUG
382       if (button == MB_RELEASED)
383       {
384         int sx = (mx - SX) / TILEX;
385         int sy = (my - SY) / TILEY;
386
387         if (IN_VIS_FIELD(sx,sy))
388         {
389           int x = LEVELX(sx);
390           int y = LEVELY(sy);
391
392           printf("INFO: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
393
394           if (!IN_LEV_FIELD(x, y))
395             break;
396
397           printf("      Feld[%d][%d] == %d\n", x,y, Feld[x][y]);
398           printf("      Store[%d][%d] == %d\n", x,y, Store[x][y]);
399           printf("      Store2[%d][%d] == %d\n", x,y, Store2[x][y]);
400           printf("      StorePlayer[%d][%d] == %d\n", x,y, StorePlayer[x][y]);
401           printf("      MovPos[%d][%d] == %d\n", x,y, MovPos[x][y]);
402           printf("      MovDir[%d][%d] == %d\n", x,y, MovDir[x][y]);
403           printf("      MovDelay[%d][%d] == %d\n", x,y, MovDelay[x][y]);
404           printf("\n");
405         }
406       }
407 #endif
408       break;
409
410     default:
411       break;
412   }
413 }
414
415 void HandleKey(KeySym key, int key_status)
416 {
417   int joy = 0;
418   static struct SetupKeyboardInfo custom_key;
419   static struct
420   {
421     KeySym *keysym_custom;
422     KeySym keysym_default;
423     byte action;
424   } key_info[] =
425   {
426     { &custom_key.left,  DEFAULT_KEY_LEFT,  JOY_LEFT     },
427     { &custom_key.right, DEFAULT_KEY_RIGHT, JOY_RIGHT    },
428     { &custom_key.up,    DEFAULT_KEY_UP,    JOY_UP       },
429     { &custom_key.down,  DEFAULT_KEY_DOWN,  JOY_DOWN     },
430     { &custom_key.snap,  DEFAULT_KEY_SNAP,  JOY_BUTTON_1 },
431     { &custom_key.bomb,  DEFAULT_KEY_BOMB,  JOY_BUTTON_2 }
432   };
433
434   if (game_status == PLAYING)
435   {
436     int pnr;
437
438     for (pnr=0; pnr<MAX_PLAYERS; pnr++)
439     {
440       int i;
441       byte key_action = 0;
442
443       if (setup.input[pnr].use_joystick)
444         continue;
445
446       custom_key = setup.input[pnr].key;
447
448       for (i=0; i<6; i++)
449         if (key == *key_info[i].keysym_custom)
450           key_action |= key_info[i].action;
451
452       if (key_status == KEY_PRESSED)
453         stored_player[pnr].action |= key_action;
454       else
455         stored_player[pnr].action &= ~key_action;
456     }
457   }
458   else
459   {
460     int i;
461
462     for (i=0; i<6; i++)
463       if (key == key_info[i].keysym_default)
464         joy |= key_info[i].action;
465   }
466
467   if (joy)
468   {
469     if (key_status == KEY_PRESSED)
470       key_joystick_mapping |= joy;
471     else
472       key_joystick_mapping &= ~joy;
473
474     HandleJoystick();
475   }
476
477   if (game_status != PLAYING)
478     key_joystick_mapping = 0;
479
480   if (key_status == KEY_RELEASED)
481     return;
482
483   if (key == XK_Return && game_status == PLAYING && AllPlayersGone)
484   {
485     CloseDoor(DOOR_CLOSE_1);
486     game_status = MAINMENU;
487     DrawMainMenu();
488     return;
489   }
490
491   /* allow quick escape to the main menu with the Escape key */
492   if (key == XK_Escape && game_status != MAINMENU)
493   {
494     if (game_status == LEVELED)
495     {
496       /* draw smaller door */
497       XCopyArea(display, pix[PIX_DOOR], drawto, gc,
498                 DOOR_GFX_PAGEX7, 64,
499                 108, 64,
500                 EX - 4, EY - 12);
501       redraw_mask |= REDRAW_ALL;
502     }
503
504     CloseDoor(DOOR_CLOSE_1 | DOOR_OPEN_2 | DOOR_NO_DELAY);
505     game_status = MAINMENU;
506     DrawMainMenu();
507     return;
508   }
509
510
511
512 #ifndef DEBUG
513
514   if (game_status == PLAYING && (tape.playing || tape.pausing))
515     return;
516
517 #endif
518
519
520
521   HandleGadgetsKeyInput(key);
522
523   switch(game_status)
524   {
525     case TYPENAME:
526       HandleTypeName(0, key);
527       break;
528
529     case MAINMENU:
530     case CHOOSELEVEL:
531     case SETUP:
532     case SETUPINPUT:
533       switch(key)
534       {
535         case XK_Return:
536           if (game_status == MAINMENU)
537             HandleMainMenu(0,0, 0,0, MB_MENU_CHOICE);
538           else if (game_status == CHOOSELEVEL)
539             HandleChooseLevel(0,0, 0,0, MB_MENU_CHOICE);
540           else if (game_status == SETUP)
541             HandleSetupScreen(0,0, 0,0, MB_MENU_CHOICE);
542           else if (game_status == SETUPINPUT)
543             HandleSetupInputScreen(0,0, 0,0, MB_MENU_CHOICE);
544           break;
545
546         default:
547           break;
548       }
549       break;
550
551     case HELPSCREEN:
552       HandleHelpScreen(MB_RELEASED);
553       break;
554
555     case HALLOFFAME:
556       switch(key)
557       {
558         case XK_Return:
559           game_status = MAINMENU;
560           DrawMainMenu();
561           BackToFront();
562           break;
563
564         default:
565           break;
566       }
567       break;
568
569     case LEVELED:
570       HandleLevelEditorKeyInput(key);
571       break;
572
573     case PLAYING:
574     {
575       switch(key)
576       {
577
578 #ifdef DEBUG
579         case XK_0:
580         case XK_1:
581         case XK_2:
582         case XK_3:
583         case XK_4:
584         case XK_5:
585         case XK_6:
586         case XK_7:
587         case XK_8:
588         case XK_9:
589           if (key == XK_0)
590           {
591             if (GameFrameDelay == 500)
592               GameFrameDelay = GAME_FRAME_DELAY;
593             else
594               GameFrameDelay = 500;
595           }
596           else
597             GameFrameDelay = (key - XK_0) * 10;
598           printf("Game speed == %d%% (%d ms delay between two frames)\n",
599                  GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
600           break;
601
602
603 #if 0
604         case XK_a:
605           if (ScrollStepSize == TILEX/8)
606             ScrollStepSize = TILEX/4;
607           else
608             ScrollStepSize = TILEX/8;
609           printf("ScrollStepSize == %d\n", ScrollStepSize);
610           break;
611 #endif
612
613 #if 1
614         case XK_m:
615           if (MoveSpeed == 8)
616           {
617             MoveSpeed = 4;
618             ScrollStepSize = TILEX/4;
619           }
620           else
621           {
622             MoveSpeed = 8;
623             ScrollStepSize = TILEX/8;
624           }
625           printf("MoveSpeed == %d\n", MoveSpeed);
626           break;
627 #endif
628
629         case XK_f:
630           ScrollStepSize = TILEX/8;
631           printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
632           break;
633
634         case XK_g:
635           ScrollStepSize = TILEX/4;
636           printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
637           break;
638
639         case XK_h:
640           ScrollStepSize = TILEX/2;
641           printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
642           break;
643
644         case XK_l:
645           ScrollStepSize = TILEX;
646           printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
647           break;
648
649 #ifndef MSDOS
650         case XK_Q:
651 #endif
652         case XK_q:
653           local_player->dynamite = 1000;
654           break;
655
656
657
658 #if 0
659
660         case XK_z:
661           {
662             int i;
663
664             for(i=0; i<MAX_PLAYERS; i++)
665             {
666               printf("Player %d:\n", i);
667               printf("  jx == %d, jy == %d\n",
668                      stored_player[i].jx, stored_player[i].jy);
669               printf("  last_jx == %d, last_jy == %d\n",
670                      stored_player[i].last_jx, stored_player[i].last_jy);
671             }
672             printf("\n");
673           }
674
675           break;
676 #endif
677 #endif
678
679         default:
680           break;
681       }
682       break;
683     }
684     default:
685       break;
686   }
687 }
688
689 void HandleNoXEvent()
690 {
691   if (button_status && game_status != PLAYING)
692   {
693     HandleButton(0, 0, -button_status);
694     return;
695   }
696
697 #ifndef MSDOS
698   if (options.network)
699     HandleNetworking();
700 #endif
701
702   HandleJoystick();
703
704   if (game_status == PLAYING)
705     HandleGameActions();
706 }
707
708 static int HandleJoystickForAllPlayers()
709 {
710   int i;
711   int result = 0;
712
713   for (i=0; i<MAX_PLAYERS; i++)
714   {
715     byte joy_action = 0;
716
717     /*
718     if (!setup.input[i].use_joystick)
719       continue;
720       */
721
722     joy_action = Joystick(i);
723     result |= joy_action;
724
725
726     if (!setup.input[i].use_joystick)
727       continue;
728
729
730     stored_player[i].action = joy_action;
731   }
732
733   return result;
734 }
735
736 void HandleJoystick()
737 {
738   int joystick  = HandleJoystickForAllPlayers();
739   int keyboard  = key_joystick_mapping;
740   int joy       = (joystick | keyboard);
741   int left      = joy & JOY_LEFT;
742   int right     = joy & JOY_RIGHT;
743   int up        = joy & JOY_UP;
744   int down      = joy & JOY_DOWN;
745   int button    = joy & JOY_BUTTON;
746   int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
747   int dx        = (left ? -1    : right ? 1     : 0);
748   int dy        = (up   ? -1    : down  ? 1     : 0);
749
750   switch(game_status)
751   {
752     case MAINMENU:
753     case CHOOSELEVEL:
754     case SETUP:
755     case SETUPINPUT:
756     {
757       static unsigned long joystickmove_delay = 0;
758
759       if (joystick && !button &&
760           !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
761         newbutton = dx = dy = 0;
762
763       if (game_status==MAINMENU)
764         HandleMainMenu(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
765       else if (game_status==CHOOSELEVEL)
766         HandleChooseLevel(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
767       else if (game_status==SETUP)
768         HandleSetupScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
769       else if (game_status==SETUPINPUT)
770         HandleSetupInputScreen(0,0,dx,dy,
771                                newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
772       break;
773     }
774
775     case HALLOFFAME:
776       HandleHallOfFame(!newbutton);
777       break;
778
779     case HELPSCREEN:
780       HandleHelpScreen(!newbutton);
781       break;
782
783     case PLAYING:
784       if (tape.playing || keyboard)
785         newbutton = ((joy & JOY_BUTTON) != 0);
786
787       if (AllPlayersGone && newbutton)
788       {
789         CloseDoor(DOOR_CLOSE_1);
790         game_status = MAINMENU;
791         DrawMainMenu();
792         return;
793       }
794
795       break;
796
797     default:
798       break;
799   }
800 }