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