2fe47e4de408086cd4b5d5cc7e1a8c0923c8830d
[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 #include "buttons.h"
24 #include "network.h"
25
26 /* values for key_status */
27 #define KEY_NOT_PRESSED         FALSE
28 #define KEY_RELEASED            FALSE
29 #define KEY_PRESSED             TRUE
30
31 void EventLoop(void)
32 {
33   while(1)
34   {
35     if (XPending(display))      /* got event from X server */
36     {
37       XEvent event;
38
39       XNextEvent(display, &event);
40
41       switch(event.type)
42       {
43         case ButtonPress:
44         case ButtonRelease:
45           HandleButtonEvent((XButtonEvent *) &event);
46           break;
47
48         case MotionNotify:
49           HandleMotionEvent((XMotionEvent *) &event);
50           break;
51
52         case KeyPress:
53         case KeyRelease:
54           HandleKeyEvent((XKeyEvent *) &event);
55           break;
56
57         default:
58           HandleOtherEvents(&event);
59           break;
60       }
61     }
62
63     HandleNoXEvent();
64
65     /* don't use all CPU time when idle; the main loop while playing
66        has its own synchronization and is CPU friendly, too */
67
68     if (game_status != PLAYING)
69     {
70       XSync(display, FALSE);
71       Delay(10);
72     }
73
74     if (game_status == EXITGAME)
75       return;
76   }
77 }
78
79 void HandleOtherEvents(XEvent *event)
80 {
81   switch(event->type)
82   {
83     case Expose:
84       HandleExposeEvent((XExposeEvent *) event);
85       break;
86
87     case UnmapNotify:
88       SleepWhileUnmapped();
89       break;
90
91     case FocusIn:
92     case FocusOut:
93       HandleFocusEvent((XFocusChangeEvent *) event);
94       break;
95
96     case ClientMessage:
97       HandleClientMessageEvent((XClientMessageEvent *) event);
98       break;
99
100     default:
101       break;
102   }
103 }
104
105 void ClearEventQueue()
106 {
107   while(XPending(display))
108   {
109     XEvent event;
110
111     XNextEvent(display, &event);
112
113     switch(event.type)
114     {
115       case ButtonRelease:
116         button_status = MB_RELEASED;
117         break;
118
119       case KeyRelease:
120         key_joystick_mapping = 0;
121         break;
122
123       default:
124         HandleOtherEvents(&event);
125         break;
126     }
127   }
128 }
129
130 void SleepWhileUnmapped()
131 {
132   boolean window_unmapped = TRUE;
133
134   XAutoRepeatOn(display);
135
136   while(window_unmapped)
137   {
138     XEvent event;
139
140     XNextEvent(display, &event);
141
142     switch(event.type)
143     {
144       case ButtonRelease:
145         button_status = MB_RELEASED;
146         break;
147
148       case KeyRelease:
149         key_joystick_mapping = 0;
150         break;
151
152       case MapNotify:
153         window_unmapped = FALSE;
154         break;
155
156       case UnmapNotify:
157         /* this is only to surely prevent the 'should not happen' case
158          * of recursively looping between 'SleepWhileUnmapped()' and
159          * 'HandleOtherEvents()' which usually calls this funtion.
160          */
161         break;
162
163       default:
164         HandleOtherEvents(&event);
165         break;
166     }
167   }
168
169   if (game_status==PLAYING)
170     XAutoRepeatOff(display);
171 }
172
173 void HandleExposeEvent(XExposeEvent *event)
174 {
175   int x = event->x, y = event->y;
176   int width = event->width, height = event->height;
177
178   if (setup.direct_draw && game_status==PLAYING)
179   {
180     int xx,yy;
181     int x1 = (x-SX)/TILEX, y1 = (y-SY)/TILEY;
182     int x2 = (x-SX+width)/TILEX, y2 = (y-SY+height)/TILEY;
183
184     SetDrawtoField(DRAW_BACKBUFFER);
185
186     for(xx=0; xx<SCR_FIELDX; xx++)
187       for(yy=0; yy<SCR_FIELDY; yy++)
188         if (xx>=x1 && xx<=x2 && yy>=y1 && yy<=y2)
189           DrawScreenField(xx,yy);
190     DrawAllPlayers();
191
192     SetDrawtoField(DRAW_DIRECT);
193   }
194
195   if (setup.soft_scrolling && game_status == PLAYING)
196   {
197     int fx = FX, fy = FY;
198
199     fx += (ScreenMovDir & (MV_LEFT|MV_RIGHT) ? ScreenGfxPos : 0);
200     fy += (ScreenMovDir & (MV_UP|MV_DOWN)    ? ScreenGfxPos : 0);
201
202     XCopyArea(display,fieldbuffer,backbuffer,gc,
203               fx,fy, SXSIZE,SYSIZE,
204               SX,SY);
205   }
206
207   XCopyArea(display,drawto,window,gc, x,y, width,height, x,y);
208
209   XFlush(display);
210 }
211
212 void HandleButtonEvent(XButtonEvent *event)
213 {
214   motion_status = FALSE;
215
216   if (event->type==ButtonPress)
217     button_status = event->button;
218   else
219     button_status = MB_RELEASED;
220
221   HandleButton(event->x, event->y, button_status);
222 }
223
224 void HandleMotionEvent(XMotionEvent *event)
225 {
226   motion_status = TRUE;
227
228   HandleButton(event->x, event->y, button_status);
229 }
230
231 void HandleKeyEvent(XKeyEvent *event)
232 {
233   int key_status = (event->type == KeyPress ? KEY_PRESSED : KEY_RELEASED);
234   unsigned int event_state = (game_status != PLAYING ? event->state : 0);
235   KeySym key = XLookupKeysym(event, event_state);
236
237   HandleKey(key, key_status);
238 }
239
240 void HandleFocusEvent(XFocusChangeEvent *event)
241 {
242   static int old_joystick_status = -1;
243
244   if (event->type == FocusOut)
245   {
246     XAutoRepeatOn(display);
247     old_joystick_status = joystick_status;
248     joystick_status = JOYSTICK_OFF;
249     key_joystick_mapping = 0;
250   }
251   else if (event->type == FocusIn)
252   {
253     if (game_status == PLAYING)
254       XAutoRepeatOff(display);
255     if (old_joystick_status != -1)
256       joystick_status = old_joystick_status;
257   }
258 }
259
260 void HandleClientMessageEvent(XClientMessageEvent *event)
261 {
262 #ifndef MSDOS
263   if ((event->window == window) &&
264       (event->data.l[0] == XInternAtom(display, "WM_DELETE_WINDOW", FALSE)))
265     CloseAllAndExit(0);
266 #endif
267 }
268
269 void HandleButton(int mx, int my, int button)
270 {
271   static int old_mx = 0, old_my = 0;
272
273   if (mx<0 || my<0)
274   {
275     mx = old_mx;
276     my = old_my;
277   }
278   else
279   {
280     old_mx = mx;
281     old_my = my;
282
283     HandleGadgets(mx,my, button);
284     HandleVideoButtons(mx,my, button);
285     HandleSoundButtons(mx,my, button);
286     HandleGameButtons(mx,my, button);
287   }
288
289   switch(game_status)
290   {
291     case MAINMENU:
292       HandleMainMenu(mx,my, 0,0, button);
293       break;
294
295     case TYPENAME:
296       HandleTypeName(0, XK_Return);
297       break;
298
299     case CHOOSELEVEL:
300       HandleChooseLevel(mx,my, 0,0, button);
301       break;
302
303     case HALLOFFAME:
304       HandleHallOfFame(button);
305       break;
306
307     case LEVELED:
308       LevelEd(mx,my, button);
309       break;
310
311     case HELPSCREEN:
312       HandleHelpScreen(button);
313       break;
314
315     case SETUP:
316       HandleSetupScreen(mx,my, 0,0, button);
317       break;
318
319     case SETUPINPUT:
320       HandleSetupInputScreen(mx,my, 0,0, button);
321       break;
322
323     case PLAYING:
324 #ifdef DEBUG
325       if (button == MB_RELEASED)
326       {
327         int sx = (mx - SX) / TILEX;
328         int sy = (my - SY) / TILEY;
329
330         if (IN_VIS_FIELD(sx,sy))
331         {
332           int x = LEVELX(sx);
333           int y = LEVELY(sy);
334
335           printf("INFO: Feld[%d][%d] == %d\n", x,y, Feld[x][y]);
336           printf("      Store[%d][%d] == %d\n", x,y, Store[x][y]);
337           printf("      Store2[%d][%d] == %d\n", x,y, Store2[x][y]);
338           printf("      StorePlayer[%d][%d] == %d\n", x,y, StorePlayer[x][y]);
339           printf("      MovPos[%d][%d] == %d\n", x,y, MovPos[x][y]);
340           printf("      MovDir[%d][%d] == %d\n", x,y, MovDir[x][y]);
341           printf("      MovDelay[%d][%d] == %d\n", x,y, MovDelay[x][y]);
342           printf("\n");
343         }
344       }
345 #endif
346       break;
347
348     default:
349       break;
350   }
351 }
352
353 void HandleKey(KeySym key, int key_status)
354 {
355   int joy = 0;
356   static struct SetupKeyboardInfo custom_key;
357   static struct
358   {
359     KeySym *keysym_custom;
360     KeySym keysym_default;
361     byte action;
362   } key_info[] =
363   {
364     { &custom_key.left,  DEFAULT_KEY_LEFT,  JOY_LEFT     },
365     { &custom_key.right, DEFAULT_KEY_RIGHT, JOY_RIGHT    },
366     { &custom_key.up,    DEFAULT_KEY_UP,    JOY_UP       },
367     { &custom_key.down,  DEFAULT_KEY_DOWN,  JOY_DOWN     },
368     { &custom_key.snap,  DEFAULT_KEY_SNAP,  JOY_BUTTON_1 },
369     { &custom_key.bomb,  DEFAULT_KEY_BOMB,  JOY_BUTTON_2 }
370   };
371
372   if (game_status == PLAYING)
373   {
374     int pnr;
375
376     for (pnr=0; pnr<MAX_PLAYERS; pnr++)
377     {
378       int i;
379       byte key_action = 0;
380
381       if (setup.input[pnr].use_joystick)
382         continue;
383
384       custom_key = setup.input[pnr].key;
385
386       for (i=0; i<6; i++)
387         if (key == *key_info[i].keysym_custom)
388           key_action |= key_info[i].action;
389
390       if (key_status == KEY_PRESSED)
391         stored_player[pnr].action |= key_action;
392       else
393         stored_player[pnr].action &= ~key_action;
394     }
395   }
396   else
397   {
398     int i;
399
400     for (i=0; i<6; i++)
401       if (key == key_info[i].keysym_default)
402         joy |= key_info[i].action;
403   }
404
405   if (joy)
406   {
407     if (key_status == KEY_PRESSED)
408       key_joystick_mapping |= joy;
409     else
410       key_joystick_mapping &= ~joy;
411
412     HandleJoystick();
413   }
414
415   if (game_status != PLAYING)
416     key_joystick_mapping = 0;
417
418   if (key_status == KEY_RELEASED)
419     return;
420
421   if (key == XK_Return && game_status == PLAYING && AllPlayersGone)
422   {
423     CloseDoor(DOOR_CLOSE_1);
424     game_status = MAINMENU;
425     DrawMainMenu();
426     return;
427   }
428
429   /* allow quick escape to the main menu with the Escape key */
430   if (key == XK_Escape && game_status != MAINMENU)
431   {
432     CloseDoor(DOOR_CLOSE_1 | DOOR_NO_DELAY);
433     game_status = MAINMENU;
434     DrawMainMenu();
435     return;
436   }
437
438
439
440 #ifndef DEBUG
441
442   if (game_status == PLAYING && (tape.playing || tape.pausing))
443     return;
444
445 #endif
446
447
448
449   switch(game_status)
450   {
451     case TYPENAME:
452       HandleTypeName(0, key);
453       break;
454
455     case MAINMENU:
456     case CHOOSELEVEL:
457     case SETUP:
458     case SETUPINPUT:
459       switch(key)
460       {
461         case XK_Return:
462           if (game_status == MAINMENU)
463             HandleMainMenu(0,0, 0,0, MB_MENU_CHOICE);
464           else if (game_status == CHOOSELEVEL)
465             HandleChooseLevel(0,0, 0,0, MB_MENU_CHOICE);
466           else if (game_status == SETUP)
467             HandleSetupScreen(0,0, 0,0, MB_MENU_CHOICE);
468           else if (game_status == SETUPINPUT)
469             HandleSetupInputScreen(0,0, 0,0, MB_MENU_CHOICE);
470           break;
471
472         default:
473           break;
474       }
475       break;
476
477     case HELPSCREEN:
478       HandleHelpScreen(MB_RELEASED);
479       break;
480
481     case HALLOFFAME:
482       switch(key)
483       {
484         case XK_Return:
485           game_status = MAINMENU;
486           DrawMainMenu();
487           BackToFront();
488           break;
489
490         default:
491           break;
492       }
493       break;
494
495     case LEVELED:
496       LevelNameTyping(key);
497       break;
498
499     case PLAYING:
500     {
501       switch(key)
502       {
503
504 #ifdef DEBUG
505         case XK_0:
506         case XK_1:
507         case XK_2:
508         case XK_3:
509         case XK_4:
510         case XK_5:
511         case XK_6:
512         case XK_7:
513         case XK_8:
514         case XK_9:
515           if (key == XK_0)
516           {
517             if (GameFrameDelay == 500)
518               GameFrameDelay = GAME_FRAME_DELAY;
519             else
520               GameFrameDelay = 500;
521           }
522           else
523             GameFrameDelay = (key - XK_0) * 10;
524           printf("Game speed == %d%% (%d ms delay between two frames)\n",
525                  GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
526           break;
527
528
529 #if 0
530         case XK_a:
531           if (ScrollStepSize == TILEX/8)
532             ScrollStepSize = TILEX/4;
533           else
534             ScrollStepSize = TILEX/8;
535           printf("ScrollStepSize == %d\n", ScrollStepSize);
536           break;
537 #endif
538
539         case XK_f:
540           ScrollStepSize = TILEX/8;
541           printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
542           break;
543
544         case XK_g:
545           ScrollStepSize = TILEX/4;
546           printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
547           break;
548
549         case XK_h:
550           ScrollStepSize = TILEX/2;
551           printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
552           break;
553
554         case XK_l:
555           ScrollStepSize = TILEX;
556           printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
557           break;
558
559 #ifndef MSDOS
560         case XK_Q:
561 #endif
562         case XK_q:
563           local_player->dynamite = 1000;
564           break;
565
566
567
568 #if 0
569
570         case XK_z:
571           {
572             int i;
573
574             for(i=0; i<MAX_PLAYERS; i++)
575             {
576               printf("Player %d:\n", i);
577               printf("  jx == %d, jy == %d\n",
578                      stored_player[i].jx, stored_player[i].jy);
579               printf("  last_jx == %d, last_jy == %d\n",
580                      stored_player[i].last_jx, stored_player[i].last_jy);
581             }
582             printf("\n");
583           }
584
585           break;
586 #endif
587 #endif
588
589         default:
590           break;
591       }
592       break;
593     }
594     default:
595       break;
596   }
597 }
598
599 void HandleNoXEvent()
600 {
601   if (button_status && game_status != PLAYING)
602   {
603     HandleButton(-1,-1, button_status);
604     return;
605   }
606
607 #ifndef MSDOS
608   if (options.network)
609     HandleNetworking();
610 #endif
611
612   HandleJoystick();
613
614   if (game_status == PLAYING)
615     HandleGameActions();
616 }
617
618 static int HandleJoystickForAllPlayers()
619 {
620   int i;
621   int result = 0;
622
623   for (i=0; i<MAX_PLAYERS; i++)
624   {
625     byte joy_action = 0;
626
627     /*
628     if (!setup.input[i].use_joystick)
629       continue;
630       */
631
632     joy_action = Joystick(i);
633     result |= joy_action;
634
635
636     if (!setup.input[i].use_joystick)
637       continue;
638
639
640     stored_player[i].action = joy_action;
641   }
642
643   return result;
644 }
645
646 void HandleJoystick()
647 {
648   int joystick  = HandleJoystickForAllPlayers();
649   int keyboard  = key_joystick_mapping;
650   int joy       = (joystick | keyboard);
651   int left      = joy & JOY_LEFT;
652   int right     = joy & JOY_RIGHT;
653   int up        = joy & JOY_UP;
654   int down      = joy & JOY_DOWN;
655   int button    = joy & JOY_BUTTON;
656   int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
657   int dx        = (left ? -1    : right ? 1     : 0);
658   int dy        = (up   ? -1    : down  ? 1     : 0);
659
660   switch(game_status)
661   {
662     case MAINMENU:
663     case CHOOSELEVEL:
664     case SETUP:
665     case SETUPINPUT:
666     {
667       static unsigned long joystickmove_delay = 0;
668
669       if (joystick && !button && !DelayReached(&joystickmove_delay, 150))
670         newbutton = dx = dy = 0;
671
672       if (game_status==MAINMENU)
673         HandleMainMenu(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
674       else if (game_status==CHOOSELEVEL)
675         HandleChooseLevel(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
676       else if (game_status==SETUP)
677         HandleSetupScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
678       else if (game_status==SETUPINPUT)
679         HandleSetupInputScreen(0,0,dx,dy,
680                                newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
681       break;
682     }
683
684     case HALLOFFAME:
685       HandleHallOfFame(!newbutton);
686       break;
687
688     case HELPSCREEN:
689       HandleHelpScreen(!newbutton);
690       break;
691
692     case PLAYING:
693       if (tape.playing || keyboard)
694         newbutton = ((joy & JOY_BUTTON) != 0);
695
696       if (AllPlayersGone && newbutton)
697       {
698         CloseDoor(DOOR_CLOSE_1);
699         game_status = MAINMENU;
700         DrawMainMenu();
701         return;
702       }
703
704       break;
705
706     default:
707       break;
708   }
709 }