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