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