rnd-20000806-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
623 #if 0
624         case KEY_a:
625           if (ScrollStepSize == TILEX/8)
626             ScrollStepSize = TILEX/4;
627           else
628             ScrollStepSize = TILEX/8;
629           printf("ScrollStepSize == %d\n", ScrollStepSize);
630           break;
631 #endif
632
633 #if 0
634         case KEY_m:
635           if (MoveSpeed == 8)
636           {
637             MoveSpeed = 4;
638             ScrollStepSize = TILEX/4;
639           }
640           else
641           {
642             MoveSpeed = 8;
643             ScrollStepSize = TILEX/8;
644           }
645           printf("MoveSpeed == %d\n", MoveSpeed);
646           break;
647 #endif
648
649         case KEY_f:
650           ScrollStepSize = TILEX/8;
651           printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
652           break;
653
654         case KEY_g:
655           ScrollStepSize = TILEX/4;
656           printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
657           break;
658
659         case KEY_h:
660           ScrollStepSize = TILEX/2;
661           printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
662           break;
663
664         case KEY_l:
665           ScrollStepSize = TILEX;
666           printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
667           break;
668
669 #ifndef MSDOS
670         case KEY_Q:
671 #endif
672         case KEY_q:
673           local_player->dynamite = 1000;
674           break;
675
676
677
678 #if 0
679
680         case KEY_z:
681           {
682             int i;
683
684             for(i=0; i<MAX_PLAYERS; i++)
685             {
686               printf("Player %d:\n", i);
687               printf("  jx == %d, jy == %d\n",
688                      stored_player[i].jx, stored_player[i].jy);
689               printf("  last_jx == %d, last_jy == %d\n",
690                      stored_player[i].last_jx, stored_player[i].last_jy);
691             }
692             printf("\n");
693           }
694
695           break;
696 #endif
697 #endif
698
699         default:
700           break;
701       }
702       break;
703     }
704     default:
705       break;
706   }
707 }
708
709 void HandleNoXEvent()
710 {
711   if (button_status && game_status != PLAYING)
712   {
713     HandleButton(0, 0, -button_status);
714     return;
715   }
716
717 #ifndef MSDOS
718   if (options.network)
719     HandleNetworking();
720 #endif
721
722   HandleJoystick();
723
724   if (game_status == PLAYING)
725     HandleGameActions();
726 }
727
728 static int HandleJoystickForAllPlayers()
729 {
730   int i;
731   int result = 0;
732
733   for (i=0; i<MAX_PLAYERS; i++)
734   {
735     byte joy_action = 0;
736
737     /*
738     if (!setup.input[i].use_joystick)
739       continue;
740       */
741
742     joy_action = Joystick(i);
743     result |= joy_action;
744
745
746     if (!setup.input[i].use_joystick)
747       continue;
748
749
750     stored_player[i].action = joy_action;
751   }
752
753   return result;
754 }
755
756 void HandleJoystick()
757 {
758   int joystick  = HandleJoystickForAllPlayers();
759   int keyboard  = key_joystick_mapping;
760   int joy       = (joystick | keyboard);
761   int left      = joy & JOY_LEFT;
762   int right     = joy & JOY_RIGHT;
763   int up        = joy & JOY_UP;
764   int down      = joy & JOY_DOWN;
765   int button    = joy & JOY_BUTTON;
766   int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
767   int dx        = (left ? -1    : right ? 1     : 0);
768   int dy        = (up   ? -1    : down  ? 1     : 0);
769
770   switch(game_status)
771   {
772     case MAINMENU:
773     case CHOOSELEVEL:
774     case SETUP:
775     case SETUPINPUT:
776     {
777       static unsigned long joystickmove_delay = 0;
778
779       if (joystick && !button &&
780           !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
781         newbutton = dx = dy = 0;
782
783       if (game_status==MAINMENU)
784         HandleMainMenu(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
785       else if (game_status==CHOOSELEVEL)
786         HandleChooseLevel(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
787       else if (game_status==SETUP)
788         HandleSetupScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
789       else if (game_status==SETUPINPUT)
790         HandleSetupInputScreen(0,0,dx,dy,
791                                newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
792       break;
793     }
794
795     case HALLOFFAME:
796       HandleHallOfFame(0,0, dx,dy, !newbutton);
797       break;
798
799     case HELPSCREEN:
800       HandleHelpScreen(!newbutton);
801       break;
802
803     case PLAYING:
804       if (tape.playing || keyboard)
805         newbutton = ((joy & JOY_BUTTON) != 0);
806
807       if (AllPlayersGone && newbutton)
808       {
809         CloseDoor(DOOR_CLOSE_1);
810         game_status = MAINMENU;
811         DrawMainMenu();
812         return;
813       }
814
815       break;
816
817     default:
818       break;
819   }
820 }