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