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