rnd-20000722-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 #ifndef USE_SDL_LIBRARY
180   int x = event->x, y = event->y;
181   int width = event->width, height = event->height;
182
183   if (setup.direct_draw && game_status==PLAYING)
184   {
185     int xx,yy;
186     int x1 = (x-SX)/TILEX, y1 = (y-SY)/TILEY;
187     int x2 = (x-SX+width)/TILEX, y2 = (y-SY+height)/TILEY;
188
189     SetDrawtoField(DRAW_BACKBUFFER);
190
191     for(xx=0; xx<SCR_FIELDX; xx++)
192       for(yy=0; yy<SCR_FIELDY; yy++)
193         if (xx>=x1 && xx<=x2 && yy>=y1 && yy<=y2)
194           DrawScreenField(xx,yy);
195     DrawAllPlayers();
196
197     SetDrawtoField(DRAW_DIRECT);
198   }
199
200   if (setup.soft_scrolling && game_status == PLAYING)
201   {
202     int fx = FX, fy = FY;
203
204     fx += (ScreenMovDir & (MV_LEFT|MV_RIGHT) ? ScreenGfxPos : 0);
205     fy += (ScreenMovDir & (MV_UP|MV_DOWN)    ? ScreenGfxPos : 0);
206
207     BlitBitmap(fieldbuffer, backbuffer, fx,fy, SXSIZE,SYSIZE, SX,SY);
208   }
209
210   BlitBitmap(drawto, window, x,y, width,height, x,y);
211
212   FlushDisplay();
213 #endif
214 }
215
216 void HandleButtonEvent(ButtonEvent *event)
217 {
218   motion_status = FALSE;
219
220   if (event->type == EVENT_BUTTONPRESS)
221     button_status = event->button;
222   else
223     button_status = MB_RELEASED;
224
225   HandleButton(event->x, event->y, button_status);
226 }
227
228 void HandleMotionEvent(MotionEvent *event)
229 {
230   int win_x, win_y;
231
232   if (!QueryPointer(window, &win_x, &win_y))
233     return;     /* window and pointer are on different screens */
234
235   if (!button_status && game_status != LEVELED)
236     return;
237
238   motion_status = TRUE;
239
240   HandleButton(win_x, win_y, button_status);
241 }
242
243 void HandleKeyEvent(KeyEvent *event)
244 {
245   int key_status = (event->type==EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
246   boolean with_modifiers = (game_status == PLAYING ? FALSE : TRUE);
247   Key key = GetEventKey(event, with_modifiers);
248
249   HandleKey(key, key_status);
250 }
251
252 void HandleFocusEvent(FocusChangeEvent *event)
253 {
254   static int old_joystick_status = -1;
255
256   if (event->type == EVENT_FOCUSOUT)
257   {
258     int i;
259
260     KeyboardAutoRepeatOn();
261     old_joystick_status = joystick_status;
262     joystick_status = JOYSTICK_OFF;
263
264     /* simulate key release events for still pressed keys */
265     key_joystick_mapping = 0;
266     for (i=0; i<MAX_PLAYERS; i++)
267       stored_player[i].action = 0;
268   }
269   else if (event->type == EVENT_FOCUSIN)
270   {
271     /* When there are two Rocks'n'Diamonds windows which overlap and
272        the player moves the pointer from one game window to the other,
273        a 'FocusOut' event is generated for the window the pointer is
274        leaving and a 'FocusIn' event is generated for the window the
275        pointer is entering. In some cases, it can happen that the
276        'FocusIn' event is handled by the one game process before the
277        'FocusOut' event by the other game process. In this case the
278        X11 environment would end up with activated keyboard auto repeat,
279        because unfortunately this is a global setting and not (which
280        would be far better) set for each X11 window individually.
281        The effect would be keyboard auto repeat while playing the game
282        (game_status == PLAYING), which is not desired.
283        To avoid this special case, we just wait 1/10 second before
284        processing the 'FocusIn' event.
285     */
286
287     if (game_status == PLAYING)
288     {
289       Delay(100);
290       KeyboardAutoRepeatOff();
291     }
292     if (old_joystick_status != -1)
293       joystick_status = old_joystick_status;
294   }
295 }
296
297 void HandleClientMessageEvent(ClientMessageEvent *event)
298 {
299 #ifdef USE_SDL_LIBRARY
300   CloseAllAndExit(0);   /* the only possible message here is SDL_QUIT */
301 #else
302 #ifndef MSDOS
303   if ((event->window == window) &&
304       (event->data.l[0] == XInternAtom(display, "WM_DELETE_WINDOW", FALSE)))
305     CloseAllAndExit(0);
306 #endif
307 #endif
308 }
309
310 void HandleButton(int mx, int my, int button)
311 {
312   static int old_mx = 0, old_my = 0;
313
314   if (button < 0)
315   {
316     mx = old_mx;
317     my = old_my;
318     button = -button;
319   }
320   else
321   {
322     old_mx = mx;
323     old_my = my;
324   }
325
326   HandleGadgets(mx, my, button);
327
328   switch(game_status)
329   {
330     case MAINMENU:
331       HandleMainMenu(mx,my, 0,0, button);
332       break;
333
334     case TYPENAME:
335       HandleTypeName(0, KEY_Return);
336       break;
337
338     case CHOOSELEVEL:
339       HandleChooseLevel(mx,my, 0,0, button);
340       break;
341
342     case HALLOFFAME:
343       HandleHallOfFame(0,0, 0,0, button);
344       break;
345
346     case LEVELED:
347       break;
348
349     case HELPSCREEN:
350       HandleHelpScreen(button);
351       break;
352
353     case SETUP:
354       HandleSetupScreen(mx,my, 0,0, button);
355       break;
356
357     case SETUPINPUT:
358       HandleSetupInputScreen(mx,my, 0,0, button);
359       break;
360
361     case PLAYING:
362 #ifdef DEBUG
363       if (button == MB_RELEASED)
364       {
365         int sx = (mx - SX) / TILEX;
366         int sy = (my - SY) / TILEY;
367
368         if (IN_VIS_FIELD(sx,sy))
369         {
370           int x = LEVELX(sx);
371           int y = LEVELY(sy);
372
373           printf("INFO: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
374
375           if (!IN_LEV_FIELD(x, y))
376             break;
377
378           printf("      Feld[%d][%d] == %d\n", x,y, Feld[x][y]);
379           printf("      Store[%d][%d] == %d\n", x,y, Store[x][y]);
380           printf("      Store2[%d][%d] == %d\n", x,y, Store2[x][y]);
381           printf("      StorePlayer[%d][%d] == %d\n", x,y, StorePlayer[x][y]);
382           printf("      MovPos[%d][%d] == %d\n", x,y, MovPos[x][y]);
383           printf("      MovDir[%d][%d] == %d\n", x,y, MovDir[x][y]);
384           printf("      MovDelay[%d][%d] == %d\n", x,y, MovDelay[x][y]);
385           printf("\n");
386         }
387       }
388 #endif
389       break;
390
391     default:
392       break;
393   }
394 }
395
396 void HandleKey(Key key, int key_status)
397 {
398   int joy = 0;
399   static struct SetupKeyboardInfo custom_key;
400   static struct
401   {
402     Key *key_custom;
403     Key key_default;
404     byte action;
405   } key_info[] =
406   {
407     { &custom_key.left,  DEFAULT_KEY_LEFT,  JOY_LEFT     },
408     { &custom_key.right, DEFAULT_KEY_RIGHT, JOY_RIGHT    },
409     { &custom_key.up,    DEFAULT_KEY_UP,    JOY_UP       },
410     { &custom_key.down,  DEFAULT_KEY_DOWN,  JOY_DOWN     },
411     { &custom_key.snap,  DEFAULT_KEY_SNAP,  JOY_BUTTON_1 },
412     { &custom_key.bomb,  DEFAULT_KEY_BOMB,  JOY_BUTTON_2 }
413   };
414
415   if (game_status == PLAYING)
416   {
417     int pnr;
418
419     for (pnr=0; pnr<MAX_PLAYERS; pnr++)
420     {
421       int i;
422       byte key_action = 0;
423
424       if (setup.input[pnr].use_joystick)
425         continue;
426
427       custom_key = setup.input[pnr].key;
428
429       for (i=0; i<6; i++)
430         if (key == *key_info[i].key_custom)
431           key_action |= key_info[i].action;
432
433       if (key_status == KEY_PRESSED)
434         stored_player[pnr].action |= key_action;
435       else
436         stored_player[pnr].action &= ~key_action;
437     }
438   }
439   else
440   {
441     int i;
442
443     for (i=0; i<6; i++)
444       if (key == key_info[i].key_default)
445         joy |= key_info[i].action;
446   }
447
448   if (joy)
449   {
450     if (key_status == KEY_PRESSED)
451       key_joystick_mapping |= joy;
452     else
453       key_joystick_mapping &= ~joy;
454
455     HandleJoystick();
456   }
457
458   if (game_status != PLAYING)
459     key_joystick_mapping = 0;
460
461   if (key_status == KEY_RELEASED)
462     return;
463
464   if ((key == KEY_Return || key == KEY_space) &&
465       game_status == PLAYING && AllPlayersGone)
466   {
467     CloseDoor(DOOR_CLOSE_1);
468     game_status = MAINMENU;
469     DrawMainMenu();
470     return;
471   }
472
473   /* allow quick escape to the main menu with the Escape key */
474   if (key == KEY_Escape && game_status != MAINMENU)
475   {
476     CloseDoor(DOOR_CLOSE_1 | DOOR_OPEN_2 | DOOR_NO_DELAY);
477     game_status = MAINMENU;
478     DrawMainMenu();
479     return;
480   }
481
482
483
484 #ifndef DEBUG
485
486   if (game_status == PLAYING && (tape.playing || tape.pausing))
487     return;
488
489 #endif
490
491
492
493   HandleGadgetsKeyInput(key);
494
495   switch(game_status)
496   {
497     case TYPENAME:
498       HandleTypeName(0, key);
499       break;
500
501     case MAINMENU:
502     case CHOOSELEVEL:
503     case SETUP:
504     case SETUPINPUT:
505       switch(key)
506       {
507         case KEY_Return:
508         case KEY_space:
509           if (game_status == MAINMENU)
510             HandleMainMenu(0,0, 0,0, MB_MENU_CHOICE);
511           else if (game_status == CHOOSELEVEL)
512             HandleChooseLevel(0,0, 0,0, MB_MENU_CHOICE);
513           else if (game_status == SETUP)
514             HandleSetupScreen(0,0, 0,0, MB_MENU_CHOICE);
515           else if (game_status == SETUPINPUT)
516             HandleSetupInputScreen(0,0, 0,0, MB_MENU_CHOICE);
517           break;
518
519         case KEY_Page_Up:
520           if (game_status == CHOOSELEVEL)
521             HandleChooseLevel(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
522           break;
523
524         case KEY_Page_Down:
525           if (game_status == CHOOSELEVEL)
526             HandleChooseLevel(0,0, 0,SCR_FIELDY, MB_MENU_MARK);
527           break;
528
529         default:
530           break;
531       }
532       break;
533
534     case HELPSCREEN:
535       HandleHelpScreen(MB_RELEASED);
536       break;
537
538     case HALLOFFAME:
539       switch(key)
540       {
541         case KEY_Return:
542         case KEY_space:
543           game_status = MAINMENU;
544           DrawMainMenu();
545           BackToFront();
546           break;
547
548         case KEY_Page_Up:
549           HandleHallOfFame(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
550           break;
551
552         case KEY_Page_Down:
553           HandleHallOfFame(0,0, 0,SCR_FIELDY, MB_MENU_MARK);
554           break;
555
556         default:
557           break;
558       }
559       break;
560
561     case LEVELED:
562       HandleLevelEditorKeyInput(key);
563       break;
564
565     case PLAYING:
566     {
567       switch(key)
568       {
569
570 #ifdef DEBUG
571         case KEY_0:
572         case KEY_1:
573         case KEY_2:
574         case KEY_3:
575         case KEY_4:
576         case KEY_5:
577         case KEY_6:
578         case KEY_7:
579         case KEY_8:
580         case KEY_9:
581           if (key == KEY_0)
582           {
583             if (GameFrameDelay == 500)
584               GameFrameDelay = GAME_FRAME_DELAY;
585             else
586               GameFrameDelay = 500;
587           }
588           else
589             GameFrameDelay = (key - KEY_0) * 10;
590           printf("Game speed == %d%% (%d ms delay between two frames)\n",
591                  GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
592           break;
593
594
595 #if 0
596         case KEY_a:
597           if (ScrollStepSize == TILEX/8)
598             ScrollStepSize = TILEX/4;
599           else
600             ScrollStepSize = TILEX/8;
601           printf("ScrollStepSize == %d\n", ScrollStepSize);
602           break;
603 #endif
604
605 #if 0
606         case KEY_m:
607           if (MoveSpeed == 8)
608           {
609             MoveSpeed = 4;
610             ScrollStepSize = TILEX/4;
611           }
612           else
613           {
614             MoveSpeed = 8;
615             ScrollStepSize = TILEX/8;
616           }
617           printf("MoveSpeed == %d\n", MoveSpeed);
618           break;
619 #endif
620
621         case KEY_f:
622           ScrollStepSize = TILEX/8;
623           printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
624           break;
625
626         case KEY_g:
627           ScrollStepSize = TILEX/4;
628           printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
629           break;
630
631         case KEY_h:
632           ScrollStepSize = TILEX/2;
633           printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
634           break;
635
636         case KEY_l:
637           ScrollStepSize = TILEX;
638           printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
639           break;
640
641 #ifndef MSDOS
642         case KEY_Q:
643 #endif
644         case KEY_q:
645           local_player->dynamite = 1000;
646           break;
647
648
649
650 #if 0
651
652         case KEY_z:
653           {
654             int i;
655
656             for(i=0; i<MAX_PLAYERS; i++)
657             {
658               printf("Player %d:\n", i);
659               printf("  jx == %d, jy == %d\n",
660                      stored_player[i].jx, stored_player[i].jy);
661               printf("  last_jx == %d, last_jy == %d\n",
662                      stored_player[i].last_jx, stored_player[i].last_jy);
663             }
664             printf("\n");
665           }
666
667           break;
668 #endif
669 #endif
670
671         default:
672           break;
673       }
674       break;
675     }
676     default:
677       break;
678   }
679 }
680
681 void HandleNoXEvent()
682 {
683   if (button_status && game_status != PLAYING)
684   {
685     HandleButton(0, 0, -button_status);
686     return;
687   }
688
689 #ifndef MSDOS
690   if (options.network)
691     HandleNetworking();
692 #endif
693
694   HandleJoystick();
695
696   if (game_status == PLAYING)
697     HandleGameActions();
698 }
699
700 static int HandleJoystickForAllPlayers()
701 {
702   int i;
703   int result = 0;
704
705   for (i=0; i<MAX_PLAYERS; i++)
706   {
707     byte joy_action = 0;
708
709     /*
710     if (!setup.input[i].use_joystick)
711       continue;
712       */
713
714     joy_action = Joystick(i);
715     result |= joy_action;
716
717
718     if (!setup.input[i].use_joystick)
719       continue;
720
721
722     stored_player[i].action = joy_action;
723   }
724
725   return result;
726 }
727
728 void HandleJoystick()
729 {
730   int joystick  = HandleJoystickForAllPlayers();
731   int keyboard  = key_joystick_mapping;
732   int joy       = (joystick | keyboard);
733   int left      = joy & JOY_LEFT;
734   int right     = joy & JOY_RIGHT;
735   int up        = joy & JOY_UP;
736   int down      = joy & JOY_DOWN;
737   int button    = joy & JOY_BUTTON;
738   int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
739   int dx        = (left ? -1    : right ? 1     : 0);
740   int dy        = (up   ? -1    : down  ? 1     : 0);
741
742   switch(game_status)
743   {
744     case MAINMENU:
745     case CHOOSELEVEL:
746     case SETUP:
747     case SETUPINPUT:
748     {
749       static unsigned long joystickmove_delay = 0;
750
751       if (joystick && !button &&
752           !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
753         newbutton = dx = dy = 0;
754
755       if (game_status==MAINMENU)
756         HandleMainMenu(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
757       else if (game_status==CHOOSELEVEL)
758         HandleChooseLevel(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
759       else if (game_status==SETUP)
760         HandleSetupScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
761       else if (game_status==SETUPINPUT)
762         HandleSetupInputScreen(0,0,dx,dy,
763                                newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
764       break;
765     }
766
767     case HALLOFFAME:
768       HandleHallOfFame(0,0, dx,dy, !newbutton);
769       break;
770
771     case HELPSCREEN:
772       HandleHelpScreen(!newbutton);
773       break;
774
775     case PLAYING:
776       if (tape.playing || keyboard)
777         newbutton = ((joy & JOY_BUTTON) != 0);
778
779       if (AllPlayersGone && newbutton)
780       {
781         CloseDoor(DOOR_CLOSE_1);
782         game_status = MAINMENU;
783         DrawMainMenu();
784         return;
785       }
786
787       break;
788
789     default:
790       break;
791   }
792 }