edce9a9c4ac35ae3a8026938199847e2e3f24c10
[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 "init.h"
17 #include "screens.h"
18 #include "tools.h"
19 #include "game.h"
20 #include "editor.h"
21 #include "misc.h"
22 #include "tape.h"
23 #include "joystick.h"
24
25 void EventLoop(void)
26 {
27   while(1)
28   {
29     if (XPending(display))      /* got an event */
30     {
31       XEvent event;
32
33       XNextEvent(display, &event);
34
35       switch(event.type)
36       {
37         case Expose:
38           HandleExposeEvent((XExposeEvent *) &event);
39           break;
40         case UnmapNotify:
41           SleepWhileUnmapped();
42           break;
43         case ButtonPress:
44         case ButtonRelease:
45           HandleButtonEvent((XButtonEvent *) &event);
46           break;
47         case MotionNotify:
48           HandleMotionEvent((XMotionEvent *) &event);
49           break;
50         case KeyPress:
51         case KeyRelease:
52           HandleKeyEvent((XKeyEvent *) &event);
53           break;
54         case FocusIn:
55         case FocusOut:
56           HandleFocusEvent((XFocusChangeEvent *) &event);
57           break;
58         case ClientMessage:
59           HandleClientMessageEvent((XClientMessageEvent *) &event);
60           break;
61         default:
62           break;
63       }
64     }
65     else                        /* got no event, but don't be lazy... */
66     {
67       HandleNoXEvent();
68       Delay(1000);              /* don't use all CPU time when idle */
69     }
70
71     XSync(display,FALSE);
72
73     if (game_status==EXITGAME)
74       return;
75   }
76 }
77
78 void ClearEventQueue()
79 {
80   while(XPending(display))
81   {
82     XEvent event;
83
84     XNextEvent(display, &event);
85
86     switch(event.type)
87     {
88       case Expose:
89         HandleExposeEvent((XExposeEvent *) &event);
90         break;
91       case UnmapNotify:
92         SleepWhileUnmapped();
93         break;
94       case ButtonRelease:
95         button_status = MB_RELEASED;
96         break;
97       case KeyRelease:
98         key_joystick_mapping = 0;
99         break;
100       case FocusIn:
101       case FocusOut:
102         HandleFocusEvent((XFocusChangeEvent *) &event);
103         break;
104       case ClientMessage:
105         HandleClientMessageEvent((XClientMessageEvent *) &event);
106         break;
107       default:
108         break;
109     }
110   }
111 }
112
113 void SleepWhileUnmapped()
114 {
115   BOOL window_unmapped = TRUE;
116
117   XAutoRepeatOn(display);
118
119   while(window_unmapped)
120   {
121     XEvent event;
122
123     XNextEvent(display, &event);
124
125     switch(event.type)
126     {
127       case Expose:
128         HandleExposeEvent((XExposeEvent *) &event);
129         break;
130       case ButtonRelease:
131         button_status = MB_RELEASED;
132         break;
133       case KeyRelease:
134         key_joystick_mapping = 0;
135         break;
136       case MapNotify:
137         window_unmapped = FALSE;
138         break;
139       case ClientMessage:
140         HandleClientMessageEvent((XClientMessageEvent *) &event);
141         break;
142       default:
143         break;
144     }
145   }
146
147   if (game_status==PLAYING)
148     XAutoRepeatOff(display);
149 }
150
151 void HandleExposeEvent(XExposeEvent *event)
152 {
153   int x = event->x, y = event->y;
154   int width = event->width, height = event->height;
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     SetDrawtoField(DRAW_BACKBUFFER);
163
164     for(xx=0;xx<SCR_FIELDX;xx++)
165       for(yy=0;yy<SCR_FIELDY;yy++)
166         if (xx>=x1 && xx<=x2 && yy>=y1 && yy<=y2)
167           DrawScreenField(xx,yy);
168     DrawPlayerField();
169
170     SetDrawtoField(DRAW_DIRECT);
171   }
172
173   if (soft_scrolling_on && game_status==PLAYING)
174     XCopyArea(display,fieldbuffer,backbuffer,gc,
175               FX,FY, SXSIZE,SYSIZE,
176               SX,SY);
177
178   XCopyArea(display,drawto,window,gc, x,y, width,height, x,y);
179
180   XFlush(display);
181 }
182
183 void HandleButtonEvent(XButtonEvent *event)
184 {
185   motion_status = FALSE;
186
187   if (event->type==ButtonPress)
188     button_status = event->button;
189   else
190     button_status = MB_RELEASED;
191
192   HandleButton(event->x, event->y, button_status);
193 }
194
195 void HandleMotionEvent(XMotionEvent *event)
196 {
197   motion_status = TRUE;
198
199   HandleButton(event->x, event->y, button_status);
200 }
201
202 void HandleKeyEvent(XKeyEvent *event)
203 {
204   int key_status = (event->type == KeyPress ? KEY_PRESSED : KEY_RELEASED);
205   unsigned int event_state = (game_status != PLAYING ? event->state : 0);
206   KeySym key = XLookupKeysym(event, event_state);
207
208   HandleKey(key, key_status);
209 }
210
211 void HandleFocusEvent(XFocusChangeEvent *event)
212 {
213   static int old_joystick_status = -1;
214
215   if (event->type == FocusOut)
216   {
217     XAutoRepeatOn(display);
218     old_joystick_status = joystick_status;
219     joystick_status = JOYSTICK_OFF;
220     key_joystick_mapping = 0;
221   }
222   else if (event->type == FocusIn)
223   {
224     if (game_status == PLAYING)
225       XAutoRepeatOff(display);
226     if (old_joystick_status != -1)
227       joystick_status = old_joystick_status;
228   }
229 }
230
231 void HandleClientMessageEvent(XClientMessageEvent *event)
232 {
233   if ((event->window == window) &&
234       (event->data.l[0] == XInternAtom(display, "WM_DELETE_WINDOW", FALSE)))
235     CloseAll();
236 }
237
238 void HandleButton(int mx, int my, int button)
239 {
240   static int old_mx = 0, old_my = 0;
241
242   if (mx<0 || my<0)
243   {
244     mx = old_mx;
245     my = old_my;
246   }
247   else
248   {
249     old_mx = mx;
250     old_my = my;
251
252     HandleVideoButtons(mx,my,button);
253     HandleSoundButtons(mx,my,button);
254     HandleGameButtons(mx,my,button);
255   }
256
257   switch(game_status)
258   {
259     case MAINMENU:
260       HandleMainMenu(mx,my,0,0,button);
261       break;
262     case TYPENAME:
263       HandleTypeName(0,XK_Return);
264       break;
265     case CHOOSELEVEL:
266       HandleChooseLevel(mx,my,0,0,button);
267       break;
268     case HALLOFFAME:
269       HandleHallOfFame(button);
270       break;
271     case LEVELED:
272       LevelEd(mx,my,button);
273       break;
274     case HELPSCREEN:
275       HandleHelpScreen(button);
276       break;
277     case SETUP:
278       HandleSetupScreen(mx,my,0,0,button);
279       break;
280     case PLAYING:
281       HandleGameActions();
282       break;
283     default:
284       break;
285   }
286 }
287
288 int GameSpeed = 2;
289 int MoveSpeed = 8;
290
291 void HandleKey(KeySym key, int key_status)
292 {
293   int joy = 0;
294
295   /* Map cursor keys to joystick directions */
296
297   switch(key)
298   {
299     case XK_Left:               /* normale Richtungen */
300 #ifdef XK_KP_Left
301     case XK_KP_Left:
302 #endif
303     case XK_KP_4:
304 #ifndef MSDOS
305     case XK_J:
306 #endif
307     case XK_j:
308       joy |= JOY_LEFT;
309       break;
310     case XK_Right:
311 #ifdef XK_KP_Right
312     case XK_KP_Right:
313 #endif
314     case XK_KP_6:
315 #ifndef MSDOS
316     case XK_K:
317 #endif
318     case XK_k:
319       joy |= JOY_RIGHT;
320       break;
321     case XK_Up:
322 #ifdef XK_KP_Up
323     case XK_KP_Up:
324 #endif
325     case XK_KP_8:
326 #ifndef MSDOS
327     case XK_I:
328 #endif
329     case XK_i:
330       joy |= JOY_UP;
331       break;
332     case XK_Down:
333 #ifdef XK_KP_Down
334     case XK_KP_Down:
335 #endif
336     case XK_KP_2:
337 #ifndef MSDOS
338     case XK_M:
339 #endif
340     case XK_m:
341       joy |= JOY_DOWN;
342       break;
343 #ifdef XK_KP_Home
344     case XK_KP_Home:            /* Diagonalrichtungen */
345 #endif
346     case XK_KP_7:
347       joy |= JOY_UP | JOY_LEFT;
348       break;
349 #ifdef XK_KP_Page_Up
350     case XK_KP_Page_Up:
351 #endif
352     case XK_KP_9:
353       joy = JOY_UP | JOY_RIGHT;
354       break;
355 #ifdef XK_KP_End
356     case XK_KP_End:
357 #endif
358     case XK_KP_1:
359       joy |= JOY_DOWN | JOY_LEFT;
360       break;
361 #ifdef XK_KP_Page_Down
362     case XK_KP_Page_Down:
363 #endif
364     case XK_KP_3:
365       joy |= JOY_DOWN | JOY_RIGHT;
366       break;
367 #ifndef MSDOS
368     case XK_S:                  /* Feld entfernen */
369 #endif
370     case XK_s:
371       joy |= JOY_BUTTON_1 | JOY_LEFT;
372       break;
373 #ifndef MSDOS
374     case XK_D:
375 #endif
376     case XK_d:
377       joy |= JOY_BUTTON_1 | JOY_RIGHT;
378       break;
379 #ifndef MSDOS
380     case XK_E:
381 #endif
382     case XK_e:
383       joy |= JOY_BUTTON_1 | JOY_UP;
384       break;
385 #ifndef MSDOS
386     case XK_X:
387 #endif
388     case XK_x:
389       joy |= JOY_BUTTON_1 | JOY_DOWN;
390       break;
391     case XK_Shift_L:            /* Linker Feuerknopf */
392 #ifndef MSDOS
393     case XK_Control_L:
394     case XK_Alt_L:
395     case XK_Meta_L:
396 #endif
397       joy |= JOY_BUTTON_1;
398       break;
399     case XK_Shift_R:            /* Rechter Feuerknopf */
400 #ifndef MSDOS
401     case XK_Control_R:
402     case XK_Alt_R:
403     case XK_Meta_R:
404     case XK_Mode_switch:
405     case XK_Multi_key:
406     case XK_B:                  /* (Bombe legen) */
407 #endif
408     case XK_b:
409       joy |= JOY_BUTTON_2;
410       break;
411     default:
412       break;
413   }
414
415   if (joy)
416   {
417     if (key_status == KEY_PRESSED)
418       key_joystick_mapping |= joy;
419     else
420       key_joystick_mapping &= ~joy;
421
422     HandleJoystick();
423   }
424
425   if (game_status != PLAYING)
426     key_joystick_mapping = 0;
427
428   if (key_status == KEY_RELEASED)
429     return;
430
431   if (key==XK_Return && game_status==PLAYING && GameOver)
432   {
433     CloseDoor(DOOR_CLOSE_1);
434     game_status = MAINMENU;
435     DrawMainMenu();
436     return;
437   }
438
439   if (key==XK_Escape && game_status!=MAINMENU)  /* quick quit to MAINMENU */
440   {
441     CloseDoor(DOOR_CLOSE_1 | DOOR_NO_DELAY);
442     game_status = MAINMENU;
443     DrawMainMenu();
444     return;
445   }
446
447   if (game_status==PLAYING && (tape.playing || tape.pausing))
448     return;
449
450   switch(game_status)
451   {
452     case TYPENAME:
453       HandleTypeName(0,key);
454       break;
455     case MAINMENU:
456     case CHOOSELEVEL:
457     case SETUP:
458     {
459       switch(key)
460       {
461         case XK_Return:
462           if (game_status==MAINMENU)
463             HandleMainMenu(0,0,0,0,MB_MENU_CHOICE);
464           else if (game_status==CHOOSELEVEL)
465             HandleChooseLevel(0,0,0,0,MB_MENU_CHOICE);
466           else if (game_status==SETUP)
467             HandleSetupScreen(0,0,0,0,MB_MENU_CHOICE);
468           break;
469         default:
470           break;
471       }
472       break;
473     }
474     case HELPSCREEN:
475       HandleHelpScreen(MB_RELEASED);
476       break;
477     case HALLOFFAME:
478       switch(key)
479       {
480         case XK_Return:
481           game_status = MAINMENU;
482           DrawMainMenu();
483           BackToFront();
484           break;
485         default:
486           break;
487       }
488       break;
489     case LEVELED:
490       LevelNameTyping(key);
491       break;
492     case PLAYING:
493     {
494       switch(key)
495       {
496
497 #ifdef DEBUG
498         case XK_0:
499         case XK_1:
500         case XK_2:
501         case XK_3:
502         case XK_4:
503         case XK_5:
504         case XK_6:
505         case XK_7:
506         case XK_8:
507         case XK_9:
508           if (key == XK_0)
509             GameSpeed = 50;
510           else
511             GameSpeed = key - XK_0;
512           printf("GameSpeed == %d\n", GameSpeed);
513           break;
514
515         case XK_a:
516           if (ScrollStepSize == TILEX/8)
517             ScrollStepSize = TILEX/4;
518           else
519             ScrollStepSize = TILEX/8;
520           printf("ScrollStepSize == %d\n", ScrollStepSize);
521           break;
522
523         case XK_f:
524           ScrollStepSize = TILEX/8;
525           printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
526           break;
527         case XK_g:
528           ScrollStepSize = TILEX/4;
529           printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
530           break;
531         case XK_h:
532           ScrollStepSize = TILEX/2;
533           printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
534           break;
535         case XK_l:
536           ScrollStepSize = TILEX;
537           printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
538           break;
539
540 #ifndef MSDOS
541         case XK_Q:
542 #endif
543         case XK_q:
544           Dynamite = 1000;
545           break;
546
547         case XK_x:
548
549           {
550             int i,j,k, num_steps = 8, step_size = TILEX / num_steps;
551             static long scroll_delay=0;
552             long scroll_delay_value = 4*4 / num_steps;
553
554             printf("Scroll test\n");
555
556             for(i=0;i<3;i++)
557             {
558               for(j=0;j<SCR_FIELDX;j++)
559               {
560                 for(k=0;k<num_steps;k++)
561                 {
562                   int xxx = j*TILEX+k*step_size;
563                   int done = 0;
564
565                   while(!done)
566                   {
567                     if (DelayReached(&scroll_delay, scroll_delay_value))
568                     {
569                       XCopyArea(display,fieldbuffer,window,gc,
570                                 SX+xxx,SY,
571                                 SXSIZE-xxx,SYSIZE,
572                                 SX,SY);
573                       XCopyArea(display,fieldbuffer,window,gc,
574                                 SX,SY,
575                                 xxx,SYSIZE,
576                                 SX+SXSIZE-xxx,SY);
577   
578                       XFlush(display);
579                       XSync(display,FALSE);
580
581                       done = 1;
582                     }
583                     else
584                     {
585                       Delay(1000);
586                     }
587                   }
588   
589                   /*
590                   Delay(160000 / num_steps);
591                   */
592                   /*
593                   Delay(120000 / num_steps);
594                   */
595                 }
596               }
597             }
598           }
599
600           break;
601
602         case XK_y:
603           {
604             printf("FX = %d, FY = %d\n", FX,FY);
605
606             XCopyArea(display,fieldbuffer,window,gc,
607                       0,0,
608                       MIN(WIN_XSIZE,FXSIZE),MIN(WIN_YSIZE,FYSIZE),
609                       0,0);
610             XFlush(display);
611             XSync(display,FALSE);
612             Delay(1000000);
613           }
614
615           break;
616
617         case XK_z:
618           {
619             static int test_picture_pos = 0;
620
621             printf("test picture %d\n", test_picture_pos);
622
623             XCopyArea(display,test_pix[test_picture_pos],window,gc,
624                       0,0, 100,100,
625                       0,0);
626             /*
627             XCopyArea(display,test_clipmask[test_picture_pos],window,gc,
628                       0,0, 100,100,
629                       100,0);
630                       */
631             XFlush(display);
632             XSync(display,FALSE);
633             Delay(1000000);
634
635             test_picture_pos = (test_picture_pos + 1) % test_picture_count;
636           }
637
638           break;
639 #endif
640
641         default:
642           break;
643       }
644       break;
645     }
646     default:
647       break;
648   }
649 }
650
651 void HandleNoXEvent()
652 {
653   if (button_status && game_status != PLAYING)
654   {
655     HandleButton(-1,-1,button_status);
656     return;
657   }
658
659   switch(game_status)
660   {
661     case MAINMENU:
662     case CHOOSELEVEL:
663     case HALLOFFAME:
664     case HELPSCREEN:
665     case SETUP:
666       HandleJoystick();
667       break;
668     case PLAYING:
669       HandleJoystick();
670       HandleGameActions();
671       break;
672     default:
673       break;
674   }
675 }
676
677 void HandleJoystick()
678 {
679   int joystick  = Joystick();
680   int keyboard  = key_joystick_mapping;
681   int joy       = (tape.playing ? TapePlayAction() : (joystick | keyboard));
682   int left      = joy & JOY_LEFT;
683   int right     = joy & JOY_RIGHT;
684   int up        = joy & JOY_UP;
685   int down      = joy & JOY_DOWN;
686   int button    = joy & JOY_BUTTON;
687   int button1   = joy & JOY_BUTTON_1;
688   int button2   = joy & JOY_BUTTON_2;
689   int newbutton = (JoystickButton() == JOY_BUTTON_NEW_PRESSED);
690   int dx        = (left ? -1    : right ? 1     : 0);
691   int dy        = (up   ? -1    : down  ? 1     : 0);
692
693   if (game_status==PLAYING && (tape.playing || keyboard))
694     newbutton = ((joy & JOY_BUTTON) != 0);
695
696   switch(game_status)
697   {
698     case MAINMENU:
699     case CHOOSELEVEL:
700     case SETUP:
701     {
702       static long joystickmove_delay = 0;
703
704       if (joystick && !button && !DelayReached(&joystickmove_delay,15))
705         newbutton = dx = dy = 0;
706
707       if (game_status==MAINMENU)
708         HandleMainMenu(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
709       else if (game_status==CHOOSELEVEL)
710         HandleChooseLevel(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
711       else if (game_status==SETUP)
712         HandleSetupScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
713       break;
714     }
715     case HALLOFFAME:
716       HandleHallOfFame(!newbutton);
717       break;
718     case HELPSCREEN:
719       HandleHelpScreen(!newbutton);
720       break;
721     case PLAYING:
722     {
723       BOOL moved = FALSE, snapped = FALSE, bombed = FALSE;
724
725       if (GameOver && newbutton)
726       {
727         CloseDoor(DOOR_CLOSE_1);
728         game_status = MAINMENU;
729         DrawMainMenu();
730         return;
731       }
732
733       if (tape.pausing || PlayerGone)
734         joy = 0;
735
736       if (joy)
737       {
738         if (button1)
739           snapped = SnapField(dx,dy);
740         else
741         {
742           if (button2)
743             bombed = PlaceBomb();
744           moved = MoveFigure(dx,dy);
745         }
746
747         if (tape.recording && (moved || snapped || bombed))
748         {
749           if (bombed && !moved)
750             joy &= JOY_BUTTON;
751           TapeRecordAction(joy);
752         }
753         else if (tape.playing && snapped)
754           SnapField(0,0);                       /* stop snapping */
755       }
756       else
757       {
758         DigField(0,0,0,0,DF_NO_PUSH);
759         SnapField(0,0);
760         PlayerFrame = 0;
761       }
762
763       if (tape.playing && !tape.pausing && !joy && tape.counter<tape.length)
764       {
765         int next_joy =
766           tape.pos[tape.counter].joystickdata & (JOY_LEFT|JOY_RIGHT);
767
768         if (next_joy == JOY_LEFT || next_joy == JOY_RIGHT)
769         {
770           int dx = (next_joy == JOY_LEFT ? -1 : +1);
771
772           if (IN_LEV_FIELD(JX+dx,JY) && IS_PUSHABLE(Feld[JX+dx][JY]))
773           {
774             int el = Feld[JX+dx][JY];
775             int push_delay = (IS_SB_ELEMENT(el) || el==EL_SONDE ? 2 : 10);
776
777             if (tape.delay_played + push_delay >= tape.pos[tape.counter].delay)
778             {
779               PlayerMovDir = next_joy;
780               PlayerFrame = FrameCounter % 4;
781               PlayerPushing = TRUE;
782             }
783           }
784         }
785       }
786       break;
787     }
788     default:
789       break;
790   }
791 }