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