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