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