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