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