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