9eb8d62312979a1b418dd4d95edfbd563130bb63
[rocksndiamonds.git] / src / events.c
1 /***********************************************************
2 *  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
3 *----------------------------------------------------------*
4 *  ©1995 Artsoft Development                               *
5 *        Holger Schemel                                    *
6 *        33659 Bielefeld-Senne                             *
7 *        Telefon: (0521) 493245                            *
8 *        eMail: aeglos@valinor.owl.de                      *
9 *               aeglos@uni-paderborn.de                    *
10 *               q99492@pbhrzx.uni-paderborn.de             *
11 *----------------------------------------------------------*
12 *  events.c                                                *
13 ***********************************************************/
14
15 #include "events.h"
16 #include "screens.h"
17 #include "tools.h"
18 #include "game.h"
19 #include "editor.h"
20 #include "misc.h"
21
22 void EventLoop(void)
23 {
24   while(1)
25   {
26     if (XPending(display))      /* got an event */
27     {
28       XEvent event;
29
30       XNextEvent(display, &event);
31
32       switch(event.type)
33       {
34         case Expose:
35           HandleExposeEvent((XExposeEvent *) &event);
36           break;
37         case UnmapNotify:
38           SleepWhileUnmapped();
39           break;
40         case ButtonPress:
41           HandleButtonEvent((XButtonEvent *) &event);
42           break;
43         case ButtonRelease:
44           HandleButtonEvent((XButtonEvent *) &event);
45           break;
46         case MotionNotify:
47           HandleMotionEvent((XMotionEvent *) &event);
48           break;
49         case KeyPress:
50           HandleKeyEvent((XKeyEvent *) &event);
51           break;
52         case KeyRelease:
53           HandleKeyEvent((XKeyEvent *) &event);
54           break;
55         case FocusIn:
56           HandleFocusEvent(FOCUS_IN);
57           break;
58         case FocusOut:
59           HandleFocusEvent(FOCUS_OUT);
60           break;
61         default:
62           break;
63       }
64     }
65     else                        /* got no event, but don't be lazy... */
66     {
67       HandleNoXEvent();
68
69       XSync(display,FALSE);
70
71       if (game_status!=PLAYING)
72         Delay(10000);           /* don't use all CPU time when idle */
73     }
74
75     if (game_status==EXITGAME)
76       return;
77   }
78 }
79
80 void ClearEventQueue()
81 {
82   while(XPending(display))
83   {
84     XEvent event;
85
86     XNextEvent(display, &event);
87
88     switch(event.type)
89     {
90       case Expose:
91         HandleExposeEvent((XExposeEvent *) &event);
92         break;
93       case UnmapNotify:
94         SleepWhileUnmapped();
95         break;
96       case ButtonRelease:
97         button_status = MB_RELEASED;
98         break;
99       case KeyRelease:
100         key_status = KEY_RELEASED;
101         break;
102       case FocusIn:
103         HandleFocusEvent(FOCUS_IN);
104         break;
105       case FocusOut:
106         HandleFocusEvent(FOCUS_OUT);
107         break;
108       default:
109         break;
110     }
111   }
112 }
113
114 void SleepWhileUnmapped()
115 {
116   BOOL window_unmapped = TRUE;
117
118   XAutoRepeatOn(display);
119
120   while(window_unmapped)
121   {
122     XEvent event;
123
124     XNextEvent(display, &event);
125
126     switch(event.type)
127     {
128       case Expose:
129         HandleExposeEvent((XExposeEvent *) &event);
130         break;
131       case ButtonRelease:
132         button_status = MB_RELEASED;
133         break;
134       case KeyRelease:
135         key_status = KEY_RELEASED;
136         break;
137       case MapNotify:
138         window_unmapped = FALSE;
139         break;
140       default:
141         break;
142     }
143   }
144
145   if (game_status==PLAYING)
146     XAutoRepeatOff(display);
147 }
148
149 void HandleExposeEvent(XExposeEvent *event)
150 {
151   int x = event->x, y = event->y;
152   int width = event->width, height = event->height;
153
154   XCopyArea(display,drawto,window,gc, x,y, width,height, x,y);
155
156   if (direct_draw_on && game_status==PLAYING)
157   {
158     int xx,yy;
159     int x1 = (x-SX)/TILEX, y1 = (y-SY)/TILEY;
160     int x2 = (x-SX+width)/TILEX, y2 = (y-SY+height)/TILEY;
161
162     for(xx=0;xx<SCR_FIELDX;xx++)
163       for(yy=0;yy<SCR_FIELDY;yy++)
164         if (xx>=x1 && xx<=x2 && yy>=y1 && yy<=y2)
165           DrawScreenField(xx,yy);
166     DrawLevelElement(JX,JY,EL_SPIELFIGUR);
167   }
168
169   XFlush(display);
170 }
171
172 void HandleButtonEvent(XButtonEvent *event)
173 {
174   motion_status = FALSE;
175
176   if (event->type==ButtonPress)
177     button_status = event->button;
178   else
179     button_status = MB_RELEASED;
180
181   HandleButton(event->x, event->y, button_status);
182 }
183
184 void HandleMotionEvent(XMotionEvent *event)
185 {
186   motion_status = TRUE;
187
188   HandleButton(event->x, event->y, button_status);
189 }
190
191 void HandleKeyEvent(XKeyEvent *event)
192 {
193   static KeySym old_keycode = 0;
194   int new_keycode = event->keycode;
195   KeySym new_key = XLookupKeysym(event,event->state);
196   int new_key_status = (event->type==KeyPress ? KEY_PRESSED : KEY_RELEASED);
197
198   if (game_status==PLAYING &&
199       (old_keycode!=new_keycode || key_status!=new_key_status))
200   {
201     DigField(0,0,DF_NO_PUSH);
202     SnapField(0,0);
203   }
204
205   if (event->type==KeyPress)
206   {
207     key_status = KEY_PRESSED;
208     HandleKey(new_key);
209     old_keycode = new_keycode;
210   }
211   else if (key_status==KEY_PRESSED && old_keycode==new_keycode)
212     key_status = KEY_RELEASED;
213 }
214
215 void HandleFocusEvent(int focus_status)
216 {
217   if (focus_status==FOCUS_OUT)
218     XAutoRepeatOn(display);
219   else if (game_status==PLAYING)
220     XAutoRepeatOff(display);
221 }
222
223 void HandleButton(int mx, int my, int button)
224 {
225   static int old_mx = 0, old_my = 0;
226
227   if (mx<0 || my<0)
228   {
229     mx = old_mx;
230     my = old_my;
231   }
232   else
233   {
234     old_mx = mx;
235     old_my = my;
236
237     HandleVideoButtons(mx,my,button);
238     HandleSoundButtons(mx,my,button);
239     HandleGameButtons(mx,my,button);
240   }
241
242   switch(game_status)
243   {
244     case MAINMENU:
245       HandleMainMenu(mx,my,0,0,button);
246       break;
247     case TYPENAME:
248       HandleTypeName(0,XK_Return);
249       break;
250     case CHOOSELEVEL:
251       HandleChooseLevel(mx,my,0,0,button);
252       break;
253     case HALLOFFAME:
254       HandleHallOfFame(button);
255       break;
256     case LEVELED:
257       LevelEd(mx,my,button);
258       break;
259     case HELPSCREEN:
260       HandleHelpScreen(button);
261       break;
262     case SETUP:
263       HandleSetupScreen(mx,my,0,0,button);
264       break;
265     case PLAYING:
266       if (!LevelSolved)
267       {
268         switch(GameActions(mx,my,button))
269         {
270           case ACT_GAME_OVER:
271             game_status = MAINMENU;
272             DrawMainMenu();
273             BackToFront();
274             break;
275           case ACT_NEW_GAME:
276             game_status = PLAYING;
277             InitGame();
278             break;
279           case ACT_GO_ON:
280             break;
281           default:
282             break;
283         }
284       }
285       BackToFront();
286       Delay(10000);
287       break;
288     default:
289       break;
290   }
291 }
292
293 void HandleKey(KeySym key)
294 {
295   static KeySym old_key = 0;
296
297   if (!key)
298     key = old_key;
299   else
300     old_key = key;
301
302   if (key==XK_Escape && game_status!=MAINMENU)  /* quick quit to MAINMENU */
303   {
304     CloseDoor(DOOR_CLOSE_1 | DOOR_NO_DELAY);
305     game_status = MAINMENU;
306     DrawMainMenu();
307     return;
308   }
309
310   if (game_status==PLAYING && (tape.playing || tape.pausing))
311     return;
312
313   switch(game_status)
314   {
315     case TYPENAME:
316       HandleTypeName(0,key);
317       break;
318     case MAINMENU:
319     case CHOOSELEVEL:
320     case SETUP:
321     {
322       int dx = 0, dy = 0;
323
324       switch(key)
325       {
326         case XK_Return:
327           if (game_status==MAINMENU)
328             HandleMainMenu(0,0,0,0,MB_MENU_CHOICE);
329           else if (game_status==CHOOSELEVEL)
330             HandleChooseLevel(0,0,0,0,MB_MENU_CHOICE);
331           else if (game_status==SETUP)
332             HandleSetupScreen(0,0,0,0,MB_MENU_CHOICE);
333           break;
334         case XK_Left:
335 #ifdef XK_KP_Left
336         case XK_KP_Left:
337 #endif
338         case XK_KP_4:
339         case XK_J:
340         case XK_j:
341           dx = -1;
342           break;
343         case XK_Right:
344 #ifdef XK_KP_Right
345         case XK_KP_Right:
346 #endif
347         case XK_KP_6:
348         case XK_K:
349         case XK_k:
350           dx = 1;
351           break;
352         case XK_Up:
353 #ifdef XK_KP_Up
354         case XK_KP_Up:
355 #endif
356         case XK_KP_8:
357         case XK_I:
358         case XK_i:
359           dy = -1;
360           break;
361         case XK_Down:
362 #ifdef XK_KP_Down
363         case XK_KP_Down:
364 #endif
365         case XK_KP_2:
366         case XK_M:
367         case XK_m:
368           dy = 1;
369           break;
370         default:
371           break;
372       }
373
374       if (dx || dy)
375       {
376         if (game_status==MAINMENU)
377           HandleMainMenu(0,0,dx,dy,MB_MENU_MARK);
378         else if (game_status==CHOOSELEVEL)
379           HandleChooseLevel(0,0,dx,dy,MB_MENU_MARK);
380         else if (game_status==SETUP)
381           HandleSetupScreen(0,0,dx,dy,MB_MENU_MARK);
382       }
383       break;
384     }
385     case HELPSCREEN:
386       HandleHelpScreen(MB_RELEASED);
387       break;
388     case HALLOFFAME:
389       switch(key)
390       {
391         case XK_Return:
392           game_status = MAINMENU;
393           DrawMainMenu();
394           BackToFront();
395           break;
396         default:
397           break;
398       }
399       break;
400     case LEVELED:
401       LevelNameTyping(key);
402       break;
403     case PLAYING:
404     {
405       int mvx = 0, mvy = 0;
406       int sbx = 0, sby = 0;
407       int joy = 0;
408       BOOL bomb = FALSE;
409       BOOL moved = FALSE, snapped = FALSE, bombed = FALSE;
410
411       switch(key)
412       {
413         case XK_Left:           /* normale Richtungen */
414 #ifdef XK_KP_Left
415         case XK_KP_Left:
416 #endif
417         case XK_KP_4:
418         case XK_J:
419         case XK_j:
420           mvx = -1;
421           joy = JOY_LEFT;
422           break;
423         case XK_Right:
424 #ifdef XK_KP_Right
425         case XK_KP_Right:
426 #endif
427         case XK_KP_6:
428         case XK_K:
429         case XK_k:
430           mvx = 1;
431           joy = JOY_RIGHT;
432           break;
433         case XK_Up:
434 #ifdef XK_KP_Up
435         case XK_KP_Up:
436 #endif
437         case XK_KP_8:
438         case XK_I:
439         case XK_i:
440           mvy = -1;
441           joy = JOY_UP;
442           break;
443         case XK_Down:
444 #ifdef XK_KP_Down
445         case XK_KP_Down:
446 #endif
447         case XK_KP_2:
448         case XK_M:
449         case XK_m:
450           mvy = 1;
451           joy = JOY_DOWN;
452           break;
453 #ifdef XK_KP_Home
454         case XK_KP_Home:        /* Diagonalrichtungen */
455 #endif
456         case XK_KP_7:
457           mvx = -1;
458           mvy = -1;
459           joy = JOY_UP | JOY_LEFT;
460           break;
461 #ifdef XK_KP_Page_Up
462         case XK_KP_Page_Up:
463 #endif
464         case XK_KP_9:
465           mvx = 1;
466           mvy = -1;
467           joy = JOY_UP | JOY_RIGHT;
468           break;
469 #ifdef XK_KP_End
470         case XK_KP_End:
471 #endif
472         case XK_KP_1:
473           mvx = -1;
474           mvy = 1;
475           joy = JOY_DOWN | JOY_LEFT;
476           break;
477 #ifdef XK_KP_Page_Down
478         case XK_KP_Page_Down:
479 #endif
480         case XK_KP_3:
481           mvx = 1;
482           mvy = 1;
483           joy = JOY_DOWN | JOY_RIGHT;
484           break;
485         case XK_S:              /* Feld entfernen */
486         case XK_s:
487           sbx = -1;
488           joy = JOY_BUTTON_1 | JOY_LEFT;
489           break;
490         case XK_D:
491         case XK_d:
492           sbx = 1;
493           joy = JOY_BUTTON_1 | JOY_RIGHT;
494           break;
495         case XK_E:
496         case XK_e:
497           sby = -1;
498           joy = JOY_BUTTON_1 | JOY_UP;
499           break;
500         case XK_X:
501         case XK_x:
502           sby = 1;
503           joy = JOY_BUTTON_1 | JOY_DOWN;
504           break;
505         case XK_B:              /* Bombe legen */
506         case XK_b:
507           bomb = TRUE;
508           joy = JOY_BUTTON_2;
509           break;
510         case XK_Q:
511           Dynamite = 1000;
512           break;
513         default:
514           break;
515       }
516
517       if (mvx || mvy)
518         moved = MoveFigure(mvx,mvy);
519       else if (sbx || sby)
520         snapped = SnapField(sbx,sby);
521       else if (bomb)
522         bombed = PlaceBomb();
523
524       if (tape.recording && (moved || snapped || bombed))
525         TapeRecordAction(joy);
526
527       break;
528     }
529     default:
530       break;
531   }
532 }
533
534 void HandleNoXEvent()
535 {
536   if (button_status)
537   {
538     HandleButton(-1,-1,button_status);
539     return;
540   }
541
542   switch(game_status)
543   {
544     case MAINMENU:
545     case CHOOSELEVEL:
546     case HALLOFFAME:
547     case HELPSCREEN:
548     case SETUP:
549       HandleJoystick();
550       break;
551     case PLAYING:
552       HandleJoystick();
553       if (key_status)
554         HandleKey(0);
555       if (game_status!=PLAYING)
556         break;
557
558       if (!LevelSolved)
559       {
560         switch(GameActions(0,0,MB_NOT_PRESSED))
561         {
562           case ACT_GAME_OVER:
563             game_status = MAINMENU;
564             DrawMainMenu();
565             BackToFront();
566             break;
567           case ACT_NEW_GAME:
568             game_status = PLAYING;
569             InitGame();
570             break;
571           case ACT_GO_ON:
572             break;
573           default:
574             break;
575         }
576       }
577
578       Delay(10000);
579
580       break;
581     default:
582       break;
583   }
584 }
585
586 void HandleJoystick()
587 {
588   int joy       = Joystick();
589   int left      = joy & JOY_LEFT;
590   int right     = joy & JOY_RIGHT;
591   int up        = joy & JOY_UP;
592   int down      = joy & JOY_DOWN;
593   int button    = joy & JOY_BUTTON;
594   int button1   = joy & JOY_BUTTON_1;
595   int button2   = joy & JOY_BUTTON_2;
596   int newbutton = (JoystickButton()==JOY_BUTTON_NEW_PRESSED);
597
598   if (button_status || key_status)
599     return;
600
601   switch(game_status)
602   {
603     case MAINMENU:
604     case CHOOSELEVEL:
605     case SETUP:
606     {
607       int dx = 0, dy = 0;
608       static long joystickmove_delay = 0;
609
610       if (DelayReached(&joystickmove_delay,15) || button)
611       {
612         if (left)
613           dx = -1;
614         else if (right)
615           dx = 1;
616         if (up)
617           dy = -1;
618         else if (down)
619           dy = 1;
620       }
621
622       if (game_status==MAINMENU)
623         HandleMainMenu(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
624       else if (game_status==CHOOSELEVEL)
625         HandleChooseLevel(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
626       else if (game_status==SETUP)
627         HandleSetupScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
628       break;
629     }
630     case HALLOFFAME:
631       HandleHallOfFame(!newbutton);
632       break;
633     case HELPSCREEN:
634       HandleHelpScreen(!newbutton);
635       break;
636     case PLAYING:
637     {
638       int mvx = 0, mvy = 0;
639       BOOL moved = FALSE, snapped = FALSE, bombed = FALSE;
640
641       if (tape.playing)
642       {
643         joy     = TapePlayAction();
644
645         left    = joy & JOY_LEFT;
646         right   = joy & JOY_RIGHT;
647         up      = joy & JOY_UP;
648         down    = joy & JOY_DOWN;
649         button  = joy & JOY_BUTTON;
650         button1 = joy & JOY_BUTTON_1;
651         button2 = joy & JOY_BUTTON_2;
652       }
653       else if (tape.pausing)
654         joy = 0;
655
656       if (!joy)
657       {
658         DigField(0,0,DF_NO_PUSH);
659         break;
660       }
661
662       if (GameOver && newbutton)
663       {
664         CloseDoor(DOOR_CLOSE_1);
665         game_status = MAINMENU;
666         DrawMainMenu();
667         return;
668       }
669
670       if (left)
671         mvx = -1;
672       else if (right)
673         mvx = 1;
674       if (up)
675         mvy = -1;
676       else if (down)
677         mvy = 1;
678
679       if (button1)
680         snapped = SnapField(mvx,mvy);
681       else
682       {
683         if (button2)
684           bombed = PlaceBomb();
685         moved = MoveFigure(mvx,mvy);
686       }
687
688       if (tape.recording && (moved || snapped || bombed))
689       {
690         if (bombed && !moved)
691           joy &= JOY_BUTTON;
692         TapeRecordAction(joy);
693       }
694       else if (tape.playing && snapped)
695         SnapField(0,0);
696
697       break;
698     }
699     default:
700       break;
701   }
702 }