rnd-19981217-1
[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 (button < 0)
274   {
275     mx = old_mx;
276     my = old_my;
277     button = -button;
278   }
279   else
280   {
281     old_mx = mx;
282     old_my = my;
283
284     HandleVideoButtons(mx,my, button);
285     HandleSoundButtons(mx,my, button);
286     HandleGameButtons(mx,my, button);
287   }
288
289   HandleGadgets(mx, my, button);
290
291   switch(game_status)
292   {
293     case MAINMENU:
294       HandleMainMenu(mx,my, 0,0, button);
295       break;
296
297     case TYPENAME:
298       HandleTypeName(0, XK_Return);
299       break;
300
301     case CHOOSELEVEL:
302       HandleChooseLevel(mx,my, 0,0, button);
303       break;
304
305     case HALLOFFAME:
306       HandleHallOfFame(button);
307       break;
308
309     case LEVELED:
310       LevelEd(mx,my, button);
311       break;
312
313     case HELPSCREEN:
314       HandleHelpScreen(button);
315       break;
316
317     case SETUP:
318       HandleSetupScreen(mx,my, 0,0, button);
319       break;
320
321     case SETUPINPUT:
322       HandleSetupInputScreen(mx,my, 0,0, button);
323       break;
324
325     case PLAYING:
326 #ifdef DEBUG
327       if (button == MB_RELEASED)
328       {
329         int sx = (mx - SX) / TILEX;
330         int sy = (my - SY) / TILEY;
331
332         if (IN_VIS_FIELD(sx,sy))
333         {
334           int x = LEVELX(sx);
335           int y = LEVELY(sy);
336
337           printf("INFO: Feld[%d][%d] == %d\n", x,y, Feld[x][y]);
338           printf("      Store[%d][%d] == %d\n", x,y, Store[x][y]);
339           printf("      Store2[%d][%d] == %d\n", x,y, Store2[x][y]);
340           printf("      StorePlayer[%d][%d] == %d\n", x,y, StorePlayer[x][y]);
341           printf("      MovPos[%d][%d] == %d\n", x,y, MovPos[x][y]);
342           printf("      MovDir[%d][%d] == %d\n", x,y, MovDir[x][y]);
343           printf("      MovDelay[%d][%d] == %d\n", x,y, MovDelay[x][y]);
344           printf("\n");
345         }
346       }
347 #endif
348       break;
349
350     default:
351       break;
352   }
353 }
354
355 void HandleKey(KeySym key, int key_status)
356 {
357   int joy = 0;
358   static struct SetupKeyboardInfo custom_key;
359   static struct
360   {
361     KeySym *keysym_custom;
362     KeySym keysym_default;
363     byte action;
364   } key_info[] =
365   {
366     { &custom_key.left,  DEFAULT_KEY_LEFT,  JOY_LEFT     },
367     { &custom_key.right, DEFAULT_KEY_RIGHT, JOY_RIGHT    },
368     { &custom_key.up,    DEFAULT_KEY_UP,    JOY_UP       },
369     { &custom_key.down,  DEFAULT_KEY_DOWN,  JOY_DOWN     },
370     { &custom_key.snap,  DEFAULT_KEY_SNAP,  JOY_BUTTON_1 },
371     { &custom_key.bomb,  DEFAULT_KEY_BOMB,  JOY_BUTTON_2 }
372   };
373
374   if (game_status == PLAYING)
375   {
376     int pnr;
377
378     for (pnr=0; pnr<MAX_PLAYERS; pnr++)
379     {
380       int i;
381       byte key_action = 0;
382
383       if (setup.input[pnr].use_joystick)
384         continue;
385
386       custom_key = setup.input[pnr].key;
387
388       for (i=0; i<6; i++)
389         if (key == *key_info[i].keysym_custom)
390           key_action |= key_info[i].action;
391
392       if (key_status == KEY_PRESSED)
393         stored_player[pnr].action |= key_action;
394       else
395         stored_player[pnr].action &= ~key_action;
396     }
397   }
398   else
399   {
400     int i;
401
402     for (i=0; i<6; i++)
403       if (key == key_info[i].keysym_default)
404         joy |= key_info[i].action;
405   }
406
407   if (joy)
408   {
409     if (key_status == KEY_PRESSED)
410       key_joystick_mapping |= joy;
411     else
412       key_joystick_mapping &= ~joy;
413
414     HandleJoystick();
415   }
416
417   if (game_status != PLAYING)
418     key_joystick_mapping = 0;
419
420   if (key_status == KEY_RELEASED)
421     return;
422
423   if (key == XK_Return && game_status == PLAYING && AllPlayersGone)
424   {
425     CloseDoor(DOOR_CLOSE_1);
426     game_status = MAINMENU;
427     DrawMainMenu();
428     return;
429   }
430
431   /* allow quick escape to the main menu with the Escape key */
432   if (key == XK_Escape && game_status != MAINMENU)
433   {
434     if (game_status == LEVELED)
435     {
436       /* draw smaller door */
437       XCopyArea(display, pix[PIX_DOOR], drawto, gc,
438                 DOOR_GFX_PAGEX7, 64,
439                 108, 64,
440                 EX - 4, EY - 12);
441       redraw_mask |= REDRAW_ALL;
442     }
443
444     CloseDoor(DOOR_CLOSE_1 | DOOR_OPEN_2 | DOOR_NO_DELAY);
445     game_status = MAINMENU;
446     DrawMainMenu();
447     return;
448   }
449
450
451
452 #ifndef DEBUG
453
454   if (game_status == PLAYING && (tape.playing || tape.pausing))
455     return;
456
457 #endif
458
459
460
461   switch(game_status)
462   {
463     case TYPENAME:
464       HandleTypeName(0, key);
465       break;
466
467     case MAINMENU:
468     case CHOOSELEVEL:
469     case SETUP:
470     case SETUPINPUT:
471       switch(key)
472       {
473         case XK_Return:
474           if (game_status == MAINMENU)
475             HandleMainMenu(0,0, 0,0, MB_MENU_CHOICE);
476           else if (game_status == CHOOSELEVEL)
477             HandleChooseLevel(0,0, 0,0, MB_MENU_CHOICE);
478           else if (game_status == SETUP)
479             HandleSetupScreen(0,0, 0,0, MB_MENU_CHOICE);
480           else if (game_status == SETUPINPUT)
481             HandleSetupInputScreen(0,0, 0,0, MB_MENU_CHOICE);
482           break;
483
484         default:
485           break;
486       }
487       break;
488
489     case HELPSCREEN:
490       HandleHelpScreen(MB_RELEASED);
491       break;
492
493     case HALLOFFAME:
494       switch(key)
495       {
496         case XK_Return:
497           game_status = MAINMENU;
498           DrawMainMenu();
499           BackToFront();
500           break;
501
502         default:
503           break;
504       }
505       break;
506
507     case LEVELED:
508       LevelNameTyping(key);
509       break;
510
511     case PLAYING:
512     {
513       switch(key)
514       {
515
516 #ifdef DEBUG
517         case XK_0:
518         case XK_1:
519         case XK_2:
520         case XK_3:
521         case XK_4:
522         case XK_5:
523         case XK_6:
524         case XK_7:
525         case XK_8:
526         case XK_9:
527           if (key == XK_0)
528           {
529             if (GameFrameDelay == 500)
530               GameFrameDelay = GAME_FRAME_DELAY;
531             else
532               GameFrameDelay = 500;
533           }
534           else
535             GameFrameDelay = (key - XK_0) * 10;
536           printf("Game speed == %d%% (%d ms delay between two frames)\n",
537                  GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
538           break;
539
540
541 #if 0
542         case XK_a:
543           if (ScrollStepSize == TILEX/8)
544             ScrollStepSize = TILEX/4;
545           else
546             ScrollStepSize = TILEX/8;
547           printf("ScrollStepSize == %d\n", ScrollStepSize);
548           break;
549 #endif
550
551         case XK_f:
552           ScrollStepSize = TILEX/8;
553           printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
554           break;
555
556         case XK_g:
557           ScrollStepSize = TILEX/4;
558           printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
559           break;
560
561         case XK_h:
562           ScrollStepSize = TILEX/2;
563           printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
564           break;
565
566         case XK_l:
567           ScrollStepSize = TILEX;
568           printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
569           break;
570
571 #ifndef MSDOS
572         case XK_Q:
573 #endif
574         case XK_q:
575           local_player->dynamite = 1000;
576           break;
577
578
579
580 #if 0
581
582         case XK_z:
583           {
584             int i;
585
586             for(i=0; i<MAX_PLAYERS; i++)
587             {
588               printf("Player %d:\n", i);
589               printf("  jx == %d, jy == %d\n",
590                      stored_player[i].jx, stored_player[i].jy);
591               printf("  last_jx == %d, last_jy == %d\n",
592                      stored_player[i].last_jx, stored_player[i].last_jy);
593             }
594             printf("\n");
595           }
596
597           break;
598 #endif
599 #endif
600
601         default:
602           break;
603       }
604       break;
605     }
606     default:
607       break;
608   }
609 }
610
611 void HandleNoXEvent()
612 {
613   if (button_status && game_status != PLAYING)
614   {
615     HandleButton(0, 0, -button_status);
616     return;
617   }
618
619 #ifndef MSDOS
620   if (options.network)
621     HandleNetworking();
622 #endif
623
624   HandleJoystick();
625
626   if (game_status == PLAYING)
627     HandleGameActions();
628 }
629
630 static int HandleJoystickForAllPlayers()
631 {
632   int i;
633   int result = 0;
634
635   for (i=0; i<MAX_PLAYERS; i++)
636   {
637     byte joy_action = 0;
638
639     /*
640     if (!setup.input[i].use_joystick)
641       continue;
642       */
643
644     joy_action = Joystick(i);
645     result |= joy_action;
646
647
648     if (!setup.input[i].use_joystick)
649       continue;
650
651
652     stored_player[i].action = joy_action;
653   }
654
655   return result;
656 }
657
658 void HandleJoystick()
659 {
660   int joystick  = HandleJoystickForAllPlayers();
661   int keyboard  = key_joystick_mapping;
662   int joy       = (joystick | keyboard);
663   int left      = joy & JOY_LEFT;
664   int right     = joy & JOY_RIGHT;
665   int up        = joy & JOY_UP;
666   int down      = joy & JOY_DOWN;
667   int button    = joy & JOY_BUTTON;
668   int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
669   int dx        = (left ? -1    : right ? 1     : 0);
670   int dy        = (up   ? -1    : down  ? 1     : 0);
671
672   switch(game_status)
673   {
674     case MAINMENU:
675     case CHOOSELEVEL:
676     case SETUP:
677     case SETUPINPUT:
678     {
679       static unsigned long joystickmove_delay = 0;
680
681       if (joystick && !button &&
682           !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
683         newbutton = dx = dy = 0;
684
685       if (game_status==MAINMENU)
686         HandleMainMenu(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
687       else if (game_status==CHOOSELEVEL)
688         HandleChooseLevel(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
689       else if (game_status==SETUP)
690         HandleSetupScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
691       else if (game_status==SETUPINPUT)
692         HandleSetupInputScreen(0,0,dx,dy,
693                                newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
694       break;
695     }
696
697     case HALLOFFAME:
698       HandleHallOfFame(!newbutton);
699       break;
700
701     case HELPSCREEN:
702       HandleHelpScreen(!newbutton);
703       break;
704
705     case PLAYING:
706       if (tape.playing || keyboard)
707         newbutton = ((joy & JOY_BUTTON) != 0);
708
709       if (AllPlayersGone && newbutton)
710       {
711         CloseDoor(DOOR_CLOSE_1);
712         game_status = MAINMENU;
713         DrawMainMenu();
714         return;
715       }
716
717       break;
718
719     default:
720       break;
721   }
722 }