rnd-20000927-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   boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
430   static struct SetupKeyboardInfo custom_key;
431   static struct
432   {
433     Key *key_custom;
434     Key key_default;
435     byte action;
436   } key_info[] =
437   {
438     { &custom_key.left,  DEFAULT_KEY_LEFT,  JOY_LEFT     },
439     { &custom_key.right, DEFAULT_KEY_RIGHT, JOY_RIGHT    },
440     { &custom_key.up,    DEFAULT_KEY_UP,    JOY_UP       },
441     { &custom_key.down,  DEFAULT_KEY_DOWN,  JOY_DOWN     },
442     { &custom_key.snap,  DEFAULT_KEY_SNAP,  JOY_BUTTON_1 },
443     { &custom_key.bomb,  DEFAULT_KEY_BOMB,  JOY_BUTTON_2 }
444   };
445
446   if (game_status == PLAYING)
447   {
448     int pnr;
449
450     for (pnr=0; pnr<MAX_PLAYERS; pnr++)
451     {
452       int i;
453       byte key_action = 0;
454
455       if (setup.input[pnr].use_joystick)
456         continue;
457
458       custom_key = setup.input[pnr].key;
459
460       for (i=0; i<6; i++)
461         if (key == *key_info[i].key_custom)
462           key_action |= key_info[i].action;
463
464       if (key_status == KEY_PRESSED)
465         stored_player[pnr].action |= key_action;
466       else
467         stored_player[pnr].action &= ~key_action;
468     }
469   }
470   else
471   {
472     int i;
473
474     for (i=0; i<6; i++)
475       if (key == key_info[i].key_default)
476         joy |= key_info[i].action;
477   }
478
479   if (joy)
480   {
481     if (key_status == KEY_PRESSED)
482       key_joystick_mapping |= joy;
483     else
484       key_joystick_mapping &= ~joy;
485
486     HandleJoystick();
487   }
488
489   if (game_status != PLAYING)
490     key_joystick_mapping = 0;
491
492   if (key_status == KEY_RELEASED)
493     return;
494
495   if ((key == KEY_Return || key == KEY_space) &&
496       game_status == PLAYING && AllPlayersGone)
497   {
498     CloseDoor(DOOR_CLOSE_1);
499     game_status = MAINMENU;
500     DrawMainMenu();
501     return;
502   }
503
504   /* allow quick escape to the main menu with the Escape key */
505   if (key == KEY_Escape && game_status != MAINMENU)
506   {
507     CloseDoor(DOOR_CLOSE_1 | DOOR_OPEN_2 | DOOR_NO_DELAY);
508     game_status = MAINMENU;
509     DrawMainMenu();
510     return;
511   }
512
513
514
515 #ifndef DEBUG
516
517   if (game_status == PLAYING && (tape.playing || tape.pausing))
518     return;
519
520 #endif
521
522
523
524   HandleGadgetsKeyInput(key);
525
526   switch(game_status)
527   {
528     case TYPENAME:
529       HandleTypeName(0, key);
530       break;
531
532     case MAINMENU:
533     case CHOOSELEVEL:
534     case SETUP:
535     case SETUPINPUT:
536       switch(key)
537       {
538         case KEY_Return:
539         case KEY_space:
540           if (game_status == MAINMENU)
541             HandleMainMenu(0,0, 0,0, MB_MENU_CHOICE);
542           else if (game_status == CHOOSELEVEL)
543             HandleChooseLevel(0,0, 0,0, MB_MENU_CHOICE);
544           else if (game_status == SETUP)
545             HandleSetupScreen(0,0, 0,0, MB_MENU_CHOICE);
546           else if (game_status == SETUPINPUT)
547             HandleSetupInputScreen(0,0, 0,0, MB_MENU_CHOICE);
548           break;
549
550         case KEY_Page_Up:
551           if (game_status == CHOOSELEVEL)
552             HandleChooseLevel(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
553           break;
554
555         case KEY_Page_Down:
556           if (game_status == CHOOSELEVEL)
557             HandleChooseLevel(0,0, 0,SCR_FIELDY, MB_MENU_MARK);
558           break;
559
560         default:
561           break;
562       }
563       break;
564
565     case HELPSCREEN:
566       HandleHelpScreen(MB_RELEASED);
567       break;
568
569     case HALLOFFAME:
570       switch(key)
571       {
572         case KEY_Return:
573         case KEY_space:
574           game_status = MAINMENU;
575           DrawMainMenu();
576           BackToFront();
577           break;
578
579         case KEY_Page_Up:
580           HandleHallOfFame(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
581           break;
582
583         case KEY_Page_Down:
584           HandleHallOfFame(0,0, 0,SCR_FIELDY, MB_MENU_MARK);
585           break;
586
587         default:
588           break;
589       }
590       break;
591
592     case LEVELED:
593       if (!anyTextGadgetActiveOrJustFinished)
594         HandleLevelEditorKeyInput(key);
595       break;
596
597     case PLAYING:
598     {
599       switch(key)
600       {
601
602 #ifdef DEBUG
603         case KEY_0:
604         case KEY_1:
605         case KEY_2:
606         case KEY_3:
607         case KEY_4:
608         case KEY_5:
609         case KEY_6:
610         case KEY_7:
611         case KEY_8:
612         case KEY_9:
613           if (key == KEY_0)
614           {
615             if (GameFrameDelay == 500)
616               GameFrameDelay = GAME_FRAME_DELAY;
617             else
618               GameFrameDelay = 500;
619           }
620           else
621             GameFrameDelay = (key - KEY_0) * 10;
622           printf("Game speed == %d%% (%d ms delay between two frames)\n",
623                  GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
624           break;
625
626         case KEY_d:
627           if (options.debug)
628           {
629             options.debug = FALSE;
630             printf("debug mode disabled\n");
631           }
632           else
633           {
634             options.debug = TRUE;
635             printf("debug mode enabled\n");
636           }
637           break;
638
639         case KEY_s:
640           if (!global.fps_slowdown)
641           {
642             global.fps_slowdown = TRUE;
643             global.fps_slowdown_factor = 2;
644             printf("fps slowdown enabled -- display only every 2nd frame\n");
645           }
646           else if (global.fps_slowdown_factor == 2)
647           {
648             global.fps_slowdown_factor = 4;
649             printf("fps slowdown enabled -- display only every 4th frame\n");
650           }
651           else
652           {
653             global.fps_slowdown = FALSE;
654             global.fps_slowdown_factor = 1;
655             printf("fps slowdown disabled\n");
656           }
657           break;
658
659 #if 0
660         case KEY_a:
661           if (ScrollStepSize == TILEX/8)
662             ScrollStepSize = TILEX/4;
663           else
664             ScrollStepSize = TILEX/8;
665           printf("ScrollStepSize == %d\n", ScrollStepSize);
666           break;
667 #endif
668
669 #if 0
670         case KEY_m:
671           if (MoveSpeed == 8)
672           {
673             MoveSpeed = 4;
674             ScrollStepSize = TILEX/4;
675           }
676           else
677           {
678             MoveSpeed = 8;
679             ScrollStepSize = TILEX/8;
680           }
681           printf("MoveSpeed == %d\n", MoveSpeed);
682           break;
683 #endif
684
685         case KEY_f:
686           ScrollStepSize = TILEX/8;
687           printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
688           break;
689
690         case KEY_g:
691           ScrollStepSize = TILEX/4;
692           printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
693           break;
694
695         case KEY_h:
696           ScrollStepSize = TILEX/2;
697           printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
698           break;
699
700         case KEY_l:
701           ScrollStepSize = TILEX;
702           printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
703           break;
704
705 #ifndef MSDOS
706         case KEY_Q:
707 #endif
708         case KEY_q:
709           local_player->dynamite = 1000;
710           break;
711
712
713
714 #if 0
715
716         case KEY_z:
717           {
718             int i;
719
720             for(i=0; i<MAX_PLAYERS; i++)
721             {
722               printf("Player %d:\n", i);
723               printf("  jx == %d, jy == %d\n",
724                      stored_player[i].jx, stored_player[i].jy);
725               printf("  last_jx == %d, last_jy == %d\n",
726                      stored_player[i].last_jx, stored_player[i].last_jy);
727             }
728             printf("\n");
729           }
730
731           break;
732 #endif
733 #endif
734
735         default:
736           break;
737       }
738       break;
739     }
740     default:
741       break;
742   }
743 }
744
745 void HandleNoEvent()
746 {
747   if (button_status && game_status != PLAYING)
748   {
749     HandleButton(0, 0, -button_status);
750     return;
751   }
752
753 #if !defined(MSDOS) && !defined(WIN32)
754   if (options.network)
755     HandleNetworking();
756 #endif
757
758   HandleJoystick();
759 }
760
761 static int HandleJoystickForAllPlayers()
762 {
763   int i;
764   int result = 0;
765
766   for (i=0; i<MAX_PLAYERS; i++)
767   {
768     byte joy_action = 0;
769
770     /*
771     if (!setup.input[i].use_joystick)
772       continue;
773       */
774
775     joy_action = Joystick(i);
776     result |= joy_action;
777
778
779     if (!setup.input[i].use_joystick)
780       continue;
781
782
783     stored_player[i].action = joy_action;
784   }
785
786   return result;
787 }
788
789 void HandleJoystick()
790 {
791   int joystick  = HandleJoystickForAllPlayers();
792   int keyboard  = key_joystick_mapping;
793   int joy       = (joystick | keyboard);
794   int left      = joy & JOY_LEFT;
795   int right     = joy & JOY_RIGHT;
796   int up        = joy & JOY_UP;
797   int down      = joy & JOY_DOWN;
798   int button    = joy & JOY_BUTTON;
799   int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
800   int dx        = (left ? -1    : right ? 1     : 0);
801   int dy        = (up   ? -1    : down  ? 1     : 0);
802
803   switch(game_status)
804   {
805     case MAINMENU:
806     case CHOOSELEVEL:
807     case SETUP:
808     case SETUPINPUT:
809     {
810       static unsigned long joystickmove_delay = 0;
811
812       if (joystick && !button &&
813           !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
814         newbutton = dx = dy = 0;
815
816       if (game_status==MAINMENU)
817         HandleMainMenu(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
818       else if (game_status==CHOOSELEVEL)
819         HandleChooseLevel(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
820       else if (game_status==SETUP)
821         HandleSetupScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
822       else if (game_status==SETUPINPUT)
823         HandleSetupInputScreen(0,0,dx,dy,
824                                newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
825       break;
826     }
827
828     case HALLOFFAME:
829       HandleHallOfFame(0,0, dx,dy, !newbutton);
830       break;
831
832     case HELPSCREEN:
833       HandleHelpScreen(!newbutton);
834       break;
835
836     case PLAYING:
837       if (tape.playing || keyboard)
838         newbutton = ((joy & JOY_BUTTON) != 0);
839
840       if (AllPlayersGone && newbutton)
841       {
842         CloseDoor(DOOR_CLOSE_1);
843         game_status = MAINMENU;
844         DrawMainMenu();
845         return;
846       }
847
848       break;
849
850     default:
851       break;
852   }
853 }