rnd-19980918
[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             GameSpeed = 500;
520           else
521             GameSpeed = (key - XK_0) * 10;
522           printf("GameSpeed == %d\n", GameSpeed);
523           break;
524
525         case XK_a:
526           if (ScrollStepSize == TILEX/8)
527             ScrollStepSize = TILEX/4;
528           else
529             ScrollStepSize = TILEX/8;
530           printf("ScrollStepSize == %d\n", ScrollStepSize);
531           break;
532
533         case XK_f:
534           ScrollStepSize = TILEX/8;
535           printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
536           break;
537         case XK_g:
538           ScrollStepSize = TILEX/4;
539           printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
540           break;
541         case XK_h:
542           ScrollStepSize = TILEX/2;
543           printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
544           break;
545         case XK_l:
546           ScrollStepSize = TILEX;
547           printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
548           break;
549
550 #ifndef MSDOS
551         case XK_Q:
552 #endif
553         case XK_q:
554           Dynamite = 1000;
555           break;
556
557         case XK_x:
558
559           {
560             int i,j,k, num_steps = 8, step_size = TILEX / num_steps;
561             static long scroll_delay=0;
562             long scroll_delay_value = 4*4 / num_steps;
563
564             printf("Scroll test\n");
565
566             for(i=0;i<3;i++)
567             {
568               for(j=0;j<SCR_FIELDX;j++)
569               {
570                 for(k=0;k<num_steps;k++)
571                 {
572                   int xxx = j*TILEX+k*step_size;
573                   int done = 0;
574
575                   while(!done)
576                   {
577                     if (DelayReached(&scroll_delay, scroll_delay_value))
578                     {
579                       XCopyArea(display,fieldbuffer,window,gc,
580                                 SX+xxx,SY,
581                                 SXSIZE-xxx,SYSIZE,
582                                 SX,SY);
583                       XCopyArea(display,fieldbuffer,window,gc,
584                                 SX,SY,
585                                 xxx,SYSIZE,
586                                 SX+SXSIZE-xxx,SY);
587   
588                       XFlush(display);
589                       XSync(display,FALSE);
590
591                       done = 1;
592                     }
593                     else
594                     {
595                       Delay(1);
596                     }
597                   }
598   
599                   /*
600                   Delay(160 / num_steps);
601                   */
602                   /*
603                   Delay(120 / num_steps);
604                   */
605                 }
606               }
607             }
608           }
609
610           break;
611
612         case XK_y:
613           {
614             printf("FX = %d, FY = %d\n", FX,FY);
615
616             XCopyArea(display,fieldbuffer,window,gc,
617                       0,0,
618                       MIN(WIN_XSIZE,FXSIZE),MIN(WIN_YSIZE,FYSIZE),
619                       0,0);
620             XFlush(display);
621             XSync(display,FALSE);
622             Delay(1000);
623           }
624
625           break;
626 #endif
627
628         default:
629           break;
630       }
631       break;
632     }
633     default:
634       break;
635   }
636 }
637
638 void HandleNoXEvent()
639 {
640   if (button_status && game_status != PLAYING)
641   {
642     HandleButton(-1,-1,button_status);
643     return;
644   }
645
646   switch(game_status)
647   {
648     case MAINMENU:
649     case CHOOSELEVEL:
650     case HALLOFFAME:
651     case HELPSCREEN:
652     case SETUP:
653       HandleJoystick();
654       break;
655     case PLAYING:
656       HandleJoystick();
657       HandleGameActions();
658       break;
659     default:
660       break;
661   }
662 }
663
664 void HandleJoystick()
665 {
666   int joystick  = Joystick();
667   int keyboard  = key_joystick_mapping;
668   int joy       = (tape.playing ? TapePlayAction() : (joystick | keyboard));
669   int left      = joy & JOY_LEFT;
670   int right     = joy & JOY_RIGHT;
671   int up        = joy & JOY_UP;
672   int down      = joy & JOY_DOWN;
673   int button    = joy & JOY_BUTTON;
674   int button1   = joy & JOY_BUTTON_1;
675   int button2   = joy & JOY_BUTTON_2;
676   int newbutton = (JoystickButton() == JOY_BUTTON_NEW_PRESSED);
677   int dx        = (left ? -1    : right ? 1     : 0);
678   int dy        = (up   ? -1    : down  ? 1     : 0);
679
680   if (game_status==PLAYING && (tape.playing || keyboard))
681     newbutton = ((joy & JOY_BUTTON) != 0);
682
683   switch(game_status)
684   {
685     case MAINMENU:
686     case CHOOSELEVEL:
687     case SETUP:
688     {
689       static long joystickmove_delay = 0;
690
691       if (joystick && !button && !DelayReached(&joystickmove_delay,15))
692         newbutton = dx = dy = 0;
693
694       if (game_status==MAINMENU)
695         HandleMainMenu(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
696       else if (game_status==CHOOSELEVEL)
697         HandleChooseLevel(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
698       else if (game_status==SETUP)
699         HandleSetupScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
700       break;
701     }
702     case HALLOFFAME:
703       HandleHallOfFame(!newbutton);
704       break;
705     case HELPSCREEN:
706       HandleHelpScreen(!newbutton);
707       break;
708     case PLAYING:
709     {
710       static int player_frame_reset_delay = 0;
711       BOOL moved = FALSE, snapped = FALSE, bombed = FALSE;
712
713       if (GameOver && newbutton)
714       {
715         CloseDoor(DOOR_CLOSE_1);
716         game_status = MAINMENU;
717         DrawMainMenu();
718         return;
719       }
720
721       if (tape.pausing || PlayerGone)
722         joy = 0;
723
724       if (joy)
725       {
726         player_frame_reset_delay = 0;
727
728         if (button1)
729           snapped = SnapField(dx,dy);
730         else
731         {
732           if (button2)
733             bombed = PlaceBomb();
734           moved = MoveFigure(dx,dy);
735         }
736
737         if (tape.recording && (moved || snapped || bombed))
738         {
739           if (bombed && !moved)
740             joy &= JOY_BUTTON;
741           TapeRecordAction(joy);
742         }
743         else if (tape.playing && snapped)
744           SnapField(0,0);                       /* stop snapping */
745       }
746       else
747       {
748         DigField(0,0,0,0,DF_NO_PUSH);
749         SnapField(0,0);
750         if (++player_frame_reset_delay > MoveSpeed)
751           PlayerFrame = 0;
752       }
753
754       if (tape.playing && !tape.pausing && !joy && tape.counter<tape.length)
755       {
756         int next_joy =
757           tape.pos[tape.counter].joystickdata & (JOY_LEFT|JOY_RIGHT);
758
759         if (next_joy == JOY_LEFT || next_joy == JOY_RIGHT)
760         {
761           int dx = (next_joy == JOY_LEFT ? -1 : +1);
762
763           if (IN_LEV_FIELD(JX+dx,JY) && IS_PUSHABLE(Feld[JX+dx][JY]))
764           {
765             int el = Feld[JX+dx][JY];
766             int push_delay = (IS_SB_ELEMENT(el) || el==EL_SONDE ? 2 : 10);
767
768             if (tape.delay_played + push_delay >= tape.pos[tape.counter].delay)
769             {
770               PlayerMovDir = next_joy;
771               PlayerFrame = FrameCounter % 4;
772               PlayerPushing = TRUE;
773             }
774           }
775         }
776       }
777       break;
778     }
779     default:
780       break;
781   }
782 }