rnd-20000720-1-src
[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 (PendingEvent())         /* got event */
36     {
37       Event event;
38
39       NextEvent(&event);
40
41       switch(event.type)
42       {
43         case EVENT_BUTTONPRESS:
44         case EVENT_BUTTONRELEASE:
45           HandleButtonEvent((ButtonEvent *) &event);
46           break;
47
48         case EVENT_MOTIONNOTIFY:
49           HandleMotionEvent((MotionEvent *) &event);
50           break;
51
52         case EVENT_KEYPRESS:
53         case EVENT_KEYRELEASE:
54           HandleKeyEvent((KeyEvent *) &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       SyncDisplay();
71       if (!PendingEvent())      /* delay only if no pending events */
72         Delay(10);
73     }
74
75     /* refresh window contents from drawing buffer, if needed */
76     BackToFront();
77
78     if (game_status == EXITGAME)
79       return;
80   }
81 }
82
83 void HandleOtherEvents(Event *event)
84 {
85   switch(event->type)
86   {
87     case EVENT_EXPOSE:
88       HandleExposeEvent((ExposeEvent *) event);
89       break;
90
91     case EVENT_UNMAPNOTIFY:
92       SleepWhileUnmapped();
93       break;
94
95     case EVENT_FOCUSIN:
96     case EVENT_FOCUSOUT:
97       HandleFocusEvent((FocusChangeEvent *) event);
98       break;
99
100     case EVENT_CLIENTMESSAGE:
101       HandleClientMessageEvent((ClientMessageEvent *) event);
102       break;
103
104     default:
105       break;
106   }
107 }
108
109 void ClearEventQueue()
110 {
111   while (PendingEvent())
112   {
113     Event event;
114
115     NextEvent(&event);
116
117     switch(event.type)
118     {
119       case EVENT_BUTTONRELEASE:
120         button_status = MB_RELEASED;
121         break;
122
123       case EVENT_KEYRELEASE:
124         key_joystick_mapping = 0;
125         break;
126
127       default:
128         HandleOtherEvents(&event);
129         break;
130     }
131   }
132 }
133
134 void SleepWhileUnmapped()
135 {
136   boolean window_unmapped = TRUE;
137
138   KeyboardAutoRepeatOn();
139
140   while(window_unmapped)
141   {
142     Event event;
143
144     NextEvent(&event);
145
146     switch(event.type)
147     {
148       case EVENT_BUTTONRELEASE:
149         button_status = MB_RELEASED;
150         break;
151
152       case EVENT_KEYRELEASE:
153         key_joystick_mapping = 0;
154         break;
155
156       case EVENT_MAPNOTIFY:
157         window_unmapped = FALSE;
158         break;
159
160       case EVENT_UNMAPNOTIFY:
161         /* this is only to surely prevent the 'should not happen' case
162          * of recursively looping between 'SleepWhileUnmapped()' and
163          * 'HandleOtherEvents()' which usually calls this funtion.
164          */
165         break;
166
167       default:
168         HandleOtherEvents(&event);
169         break;
170     }
171   }
172
173   if (game_status == PLAYING)
174     KeyboardAutoRepeatOff();
175 }
176
177 void HandleExposeEvent(ExposeEvent *event)
178 {
179   int x = event->x, y = event->y;
180   int width = event->width, height = event->height;
181
182   if (setup.direct_draw && game_status==PLAYING)
183   {
184     int xx,yy;
185     int x1 = (x-SX)/TILEX, y1 = (y-SY)/TILEY;
186     int x2 = (x-SX+width)/TILEX, y2 = (y-SY+height)/TILEY;
187
188     SetDrawtoField(DRAW_BACKBUFFER);
189
190     for(xx=0; xx<SCR_FIELDX; xx++)
191       for(yy=0; yy<SCR_FIELDY; yy++)
192         if (xx>=x1 && xx<=x2 && yy>=y1 && yy<=y2)
193           DrawScreenField(xx,yy);
194     DrawAllPlayers();
195
196     SetDrawtoField(DRAW_DIRECT);
197   }
198
199   if (setup.soft_scrolling && game_status == PLAYING)
200   {
201     int fx = FX, fy = FY;
202
203     fx += (ScreenMovDir & (MV_LEFT|MV_RIGHT) ? ScreenGfxPos : 0);
204     fy += (ScreenMovDir & (MV_UP|MV_DOWN)    ? ScreenGfxPos : 0);
205
206     BlitBitmap(fieldbuffer, backbuffer, fx,fy, SXSIZE,SYSIZE, SX,SY);
207   }
208
209   BlitBitmap(drawto, window, x,y, width,height, x,y);
210
211   FlushDisplay();
212 }
213
214 void HandleButtonEvent(ButtonEvent *event)
215 {
216   motion_status = FALSE;
217
218   if (event->type == EVENT_BUTTONPRESS)
219     button_status = event->button;
220   else
221     button_status = MB_RELEASED;
222
223   HandleButton(event->x, event->y, button_status);
224 }
225
226 void HandleMotionEvent(MotionEvent *event)
227 {
228   int win_x, win_y;
229
230   if (!QueryPointer(window, &win_x, &win_y))
231     return;     /* window and pointer are on different screens */
232
233   if (!button_status && game_status != LEVELED)
234     return;
235
236   motion_status = TRUE;
237
238   HandleButton(win_x, win_y, button_status);
239 }
240
241 void HandleKeyEvent(KeyEvent *event)
242 {
243   int key_status = (event->type==EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
244   KeySym key;
245
246   if (game_status == PLAYING)
247   {
248     /* use '0' instead of 'event->state' to get the key without modifiers */
249     key = XLookupKeysym(event, 0);
250   }
251   else
252   {
253     /* get the key with all modifiers */
254     char buffer[10];
255     int buffer_size = 10;
256     XComposeStatus compose;
257     int char_count;
258
259     char_count = XLookupString(event, buffer, buffer_size, &key, &compose);
260     buffer[char_count] = '\0';
261   }
262
263   HandleKey(key, key_status);
264 }
265
266 void HandleFocusEvent(FocusChangeEvent *event)
267 {
268   static int old_joystick_status = -1;
269
270   if (event->type == EVENT_FOCUSOUT)
271   {
272     int i;
273
274     KeyboardAutoRepeatOn();
275     old_joystick_status = joystick_status;
276     joystick_status = JOYSTICK_OFF;
277
278     /* simulate key release events for still pressed keys */
279     key_joystick_mapping = 0;
280     for (i=0; i<MAX_PLAYERS; i++)
281       stored_player[i].action = 0;
282   }
283   else if (event->type == EVENT_FOCUSIN)
284   {
285     /* When there are two Rocks'n'Diamonds windows which overlap and
286        the player moves the pointer from one game window to the other,
287        a 'FocusOut' event is generated for the window the pointer is
288        leaving and a 'FocusIn' event is generated for the window the
289        pointer is entering. In some cases, it can happen that the
290        'FocusIn' event is handled by the one game process before the
291        'FocusOut' event by the other game process. In this case the
292        X11 environment would end up with activated keyboard auto repeat,
293        because unfortunately this is a global setting and not (which
294        would be far better) set for each X11 window individually.
295        The effect would be keyboard auto repeat while playing the game
296        (game_status == PLAYING), which is not desired.
297        To avoid this special case, we just wait 1/10 second before
298        processing the 'FocusIn' event.
299     */
300
301     if (game_status == PLAYING)
302     {
303       Delay(100);
304       KeyboardAutoRepeatOff();
305     }
306     if (old_joystick_status != -1)
307       joystick_status = old_joystick_status;
308   }
309 }
310
311 void HandleClientMessageEvent(ClientMessageEvent *event)
312 {
313 #ifdef USE_SDL_LIBRARY
314   CloseAllAndExit(0);   /* the only possible message here is SDL_QUIT */
315 #else
316 #ifndef MSDOS
317   if ((event->window == window) &&
318       (event->data.l[0] == XInternAtom(display, "WM_DELETE_WINDOW", FALSE)))
319     CloseAllAndExit(0);
320 #endif
321 #endif
322 }
323
324 void HandleButton(int mx, int my, int button)
325 {
326   static int old_mx = 0, old_my = 0;
327
328   if (button < 0)
329   {
330     mx = old_mx;
331     my = old_my;
332     button = -button;
333   }
334   else
335   {
336     old_mx = mx;
337     old_my = my;
338   }
339
340   HandleGadgets(mx, my, button);
341
342   switch(game_status)
343   {
344     case MAINMENU:
345       HandleMainMenu(mx,my, 0,0, button);
346       break;
347
348     case TYPENAME:
349       HandleTypeName(0, XK_Return);
350       break;
351
352     case CHOOSELEVEL:
353       HandleChooseLevel(mx,my, 0,0, button);
354       break;
355
356     case HALLOFFAME:
357       HandleHallOfFame(0,0, 0,0, button);
358       break;
359
360     case LEVELED:
361       break;
362
363     case HELPSCREEN:
364       HandleHelpScreen(button);
365       break;
366
367     case SETUP:
368       HandleSetupScreen(mx,my, 0,0, button);
369       break;
370
371     case SETUPINPUT:
372       HandleSetupInputScreen(mx,my, 0,0, button);
373       break;
374
375     case PLAYING:
376 #ifdef DEBUG
377       if (button == MB_RELEASED)
378       {
379         int sx = (mx - SX) / TILEX;
380         int sy = (my - SY) / TILEY;
381
382         if (IN_VIS_FIELD(sx,sy))
383         {
384           int x = LEVELX(sx);
385           int y = LEVELY(sy);
386
387           printf("INFO: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
388
389           if (!IN_LEV_FIELD(x, y))
390             break;
391
392           printf("      Feld[%d][%d] == %d\n", x,y, Feld[x][y]);
393           printf("      Store[%d][%d] == %d\n", x,y, Store[x][y]);
394           printf("      Store2[%d][%d] == %d\n", x,y, Store2[x][y]);
395           printf("      StorePlayer[%d][%d] == %d\n", x,y, StorePlayer[x][y]);
396           printf("      MovPos[%d][%d] == %d\n", x,y, MovPos[x][y]);
397           printf("      MovDir[%d][%d] == %d\n", x,y, MovDir[x][y]);
398           printf("      MovDelay[%d][%d] == %d\n", x,y, MovDelay[x][y]);
399           printf("\n");
400         }
401       }
402 #endif
403       break;
404
405     default:
406       break;
407   }
408 }
409
410 void HandleKey(KeySym key, int key_status)
411 {
412   int joy = 0;
413   static struct SetupKeyboardInfo custom_key;
414   static struct
415   {
416     KeySym *keysym_custom;
417     KeySym keysym_default;
418     byte action;
419   } key_info[] =
420   {
421     { &custom_key.left,  DEFAULT_KEY_LEFT,  JOY_LEFT     },
422     { &custom_key.right, DEFAULT_KEY_RIGHT, JOY_RIGHT    },
423     { &custom_key.up,    DEFAULT_KEY_UP,    JOY_UP       },
424     { &custom_key.down,  DEFAULT_KEY_DOWN,  JOY_DOWN     },
425     { &custom_key.snap,  DEFAULT_KEY_SNAP,  JOY_BUTTON_1 },
426     { &custom_key.bomb,  DEFAULT_KEY_BOMB,  JOY_BUTTON_2 }
427   };
428
429   if (game_status == PLAYING)
430   {
431     int pnr;
432
433     for (pnr=0; pnr<MAX_PLAYERS; pnr++)
434     {
435       int i;
436       byte key_action = 0;
437
438       if (setup.input[pnr].use_joystick)
439         continue;
440
441       custom_key = setup.input[pnr].key;
442
443       for (i=0; i<6; i++)
444         if (key == *key_info[i].keysym_custom)
445           key_action |= key_info[i].action;
446
447       if (key_status == KEY_PRESSED)
448         stored_player[pnr].action |= key_action;
449       else
450         stored_player[pnr].action &= ~key_action;
451     }
452   }
453   else
454   {
455     int i;
456
457     for (i=0; i<6; i++)
458       if (key == key_info[i].keysym_default)
459         joy |= key_info[i].action;
460   }
461
462   if (joy)
463   {
464     if (key_status == KEY_PRESSED)
465       key_joystick_mapping |= joy;
466     else
467       key_joystick_mapping &= ~joy;
468
469     HandleJoystick();
470   }
471
472   if (game_status != PLAYING)
473     key_joystick_mapping = 0;
474
475   if (key_status == KEY_RELEASED)
476     return;
477
478   if ((key == XK_Return || key == XK_space) &&
479       game_status == PLAYING && AllPlayersGone)
480   {
481     CloseDoor(DOOR_CLOSE_1);
482     game_status = MAINMENU;
483     DrawMainMenu();
484     return;
485   }
486
487   /* allow quick escape to the main menu with the Escape key */
488   if (key == XK_Escape && game_status != MAINMENU)
489   {
490     CloseDoor(DOOR_CLOSE_1 | DOOR_OPEN_2 | DOOR_NO_DELAY);
491     game_status = MAINMENU;
492     DrawMainMenu();
493     return;
494   }
495
496
497
498 #ifndef DEBUG
499
500   if (game_status == PLAYING && (tape.playing || tape.pausing))
501     return;
502
503 #endif
504
505
506
507   HandleGadgetsKeyInput(key);
508
509   switch(game_status)
510   {
511     case TYPENAME:
512       HandleTypeName(0, key);
513       break;
514
515     case MAINMENU:
516     case CHOOSELEVEL:
517     case SETUP:
518     case SETUPINPUT:
519       switch(key)
520       {
521         case XK_Return:
522         case XK_space:
523           if (game_status == MAINMENU)
524             HandleMainMenu(0,0, 0,0, MB_MENU_CHOICE);
525           else if (game_status == CHOOSELEVEL)
526             HandleChooseLevel(0,0, 0,0, MB_MENU_CHOICE);
527           else if (game_status == SETUP)
528             HandleSetupScreen(0,0, 0,0, MB_MENU_CHOICE);
529           else if (game_status == SETUPINPUT)
530             HandleSetupInputScreen(0,0, 0,0, MB_MENU_CHOICE);
531           break;
532
533         case XK_Page_Up:
534           if (game_status == CHOOSELEVEL)
535             HandleChooseLevel(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
536           break;
537
538         case XK_Page_Down:
539           if (game_status == CHOOSELEVEL)
540             HandleChooseLevel(0,0, 0,SCR_FIELDY, MB_MENU_MARK);
541           break;
542
543         default:
544           break;
545       }
546       break;
547
548     case HELPSCREEN:
549       HandleHelpScreen(MB_RELEASED);
550       break;
551
552     case HALLOFFAME:
553       switch(key)
554       {
555         case XK_Return:
556         case XK_space:
557           game_status = MAINMENU;
558           DrawMainMenu();
559           BackToFront();
560           break;
561
562         case XK_Page_Up:
563           HandleHallOfFame(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
564           break;
565
566         case XK_Page_Down:
567           HandleHallOfFame(0,0, 0,SCR_FIELDY, MB_MENU_MARK);
568           break;
569
570         default:
571           break;
572       }
573       break;
574
575     case LEVELED:
576       HandleLevelEditorKeyInput(key);
577       break;
578
579     case PLAYING:
580     {
581       switch(key)
582       {
583
584 #ifdef DEBUG
585         case XK_0:
586         case XK_1:
587         case XK_2:
588         case XK_3:
589         case XK_4:
590         case XK_5:
591         case XK_6:
592         case XK_7:
593         case XK_8:
594         case XK_9:
595           if (key == XK_0)
596           {
597             if (GameFrameDelay == 500)
598               GameFrameDelay = GAME_FRAME_DELAY;
599             else
600               GameFrameDelay = 500;
601           }
602           else
603             GameFrameDelay = (key - XK_0) * 10;
604           printf("Game speed == %d%% (%d ms delay between two frames)\n",
605                  GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
606           break;
607
608
609 #if 0
610         case XK_a:
611           if (ScrollStepSize == TILEX/8)
612             ScrollStepSize = TILEX/4;
613           else
614             ScrollStepSize = TILEX/8;
615           printf("ScrollStepSize == %d\n", ScrollStepSize);
616           break;
617 #endif
618
619 #if 0
620         case XK_m:
621           if (MoveSpeed == 8)
622           {
623             MoveSpeed = 4;
624             ScrollStepSize = TILEX/4;
625           }
626           else
627           {
628             MoveSpeed = 8;
629             ScrollStepSize = TILEX/8;
630           }
631           printf("MoveSpeed == %d\n", MoveSpeed);
632           break;
633 #endif
634
635         case XK_f:
636           ScrollStepSize = TILEX/8;
637           printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
638           break;
639
640         case XK_g:
641           ScrollStepSize = TILEX/4;
642           printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
643           break;
644
645         case XK_h:
646           ScrollStepSize = TILEX/2;
647           printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
648           break;
649
650         case XK_l:
651           ScrollStepSize = TILEX;
652           printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
653           break;
654
655 #ifndef MSDOS
656         case XK_Q:
657 #endif
658         case XK_q:
659           local_player->dynamite = 1000;
660           break;
661
662
663
664 #if 0
665
666         case XK_z:
667           {
668             int i;
669
670             for(i=0; i<MAX_PLAYERS; i++)
671             {
672               printf("Player %d:\n", i);
673               printf("  jx == %d, jy == %d\n",
674                      stored_player[i].jx, stored_player[i].jy);
675               printf("  last_jx == %d, last_jy == %d\n",
676                      stored_player[i].last_jx, stored_player[i].last_jy);
677             }
678             printf("\n");
679           }
680
681           break;
682 #endif
683 #endif
684
685         default:
686           break;
687       }
688       break;
689     }
690     default:
691       break;
692   }
693 }
694
695 void HandleNoXEvent()
696 {
697   if (button_status && game_status != PLAYING)
698   {
699     HandleButton(0, 0, -button_status);
700     return;
701   }
702
703 #ifndef MSDOS
704   if (options.network)
705     HandleNetworking();
706 #endif
707
708   HandleJoystick();
709
710   if (game_status == PLAYING)
711     HandleGameActions();
712 }
713
714 static int HandleJoystickForAllPlayers()
715 {
716   int i;
717   int result = 0;
718
719   for (i=0; i<MAX_PLAYERS; i++)
720   {
721     byte joy_action = 0;
722
723     /*
724     if (!setup.input[i].use_joystick)
725       continue;
726       */
727
728     joy_action = Joystick(i);
729     result |= joy_action;
730
731
732     if (!setup.input[i].use_joystick)
733       continue;
734
735
736     stored_player[i].action = joy_action;
737   }
738
739   return result;
740 }
741
742 void HandleJoystick()
743 {
744   int joystick  = HandleJoystickForAllPlayers();
745   int keyboard  = key_joystick_mapping;
746   int joy       = (joystick | keyboard);
747   int left      = joy & JOY_LEFT;
748   int right     = joy & JOY_RIGHT;
749   int up        = joy & JOY_UP;
750   int down      = joy & JOY_DOWN;
751   int button    = joy & JOY_BUTTON;
752   int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
753   int dx        = (left ? -1    : right ? 1     : 0);
754   int dy        = (up   ? -1    : down  ? 1     : 0);
755
756   switch(game_status)
757   {
758     case MAINMENU:
759     case CHOOSELEVEL:
760     case SETUP:
761     case SETUPINPUT:
762     {
763       static unsigned long joystickmove_delay = 0;
764
765       if (joystick && !button &&
766           !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
767         newbutton = dx = dy = 0;
768
769       if (game_status==MAINMENU)
770         HandleMainMenu(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
771       else if (game_status==CHOOSELEVEL)
772         HandleChooseLevel(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
773       else if (game_status==SETUP)
774         HandleSetupScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
775       else if (game_status==SETUPINPUT)
776         HandleSetupInputScreen(0,0,dx,dy,
777                                newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
778       break;
779     }
780
781     case HALLOFFAME:
782       HandleHallOfFame(0,0, dx,dy, !newbutton);
783       break;
784
785     case HELPSCREEN:
786       HandleHelpScreen(!newbutton);
787       break;
788
789     case PLAYING:
790       if (tape.playing || keyboard)
791         newbutton = ((joy & JOY_BUTTON) != 0);
792
793       if (AllPlayersGone && newbutton)
794       {
795         CloseDoor(DOOR_CLOSE_1);
796         game_status = MAINMENU;
797         DrawMainMenu();
798         return;
799       }
800
801       break;
802
803     default:
804       break;
805   }
806 }