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