rnd-20010115-1-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 "tape.h"
23 #include "joystick.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_OFF;
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 && game_status != MAINMENU)
514   {
515     CloseDoor(DOOR_CLOSE_1 | DOOR_OPEN_2 | DOOR_NO_DELAY);
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         default:
569           break;
570       }
571       break;
572
573     case HELPSCREEN:
574       HandleHelpScreen(MB_RELEASED);
575       break;
576
577     case HALLOFFAME:
578       switch(key)
579       {
580         case KSYM_Return:
581         case KSYM_space:
582           game_status = MAINMENU;
583           DrawMainMenu();
584           BackToFront();
585           break;
586
587         case KSYM_Page_Up:
588           HandleHallOfFame(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
589           break;
590
591         case KSYM_Page_Down:
592           HandleHallOfFame(0,0, 0,SCR_FIELDY, MB_MENU_MARK);
593           break;
594
595         default:
596           break;
597       }
598       break;
599
600     case LEVELED:
601       if (!anyTextGadgetActiveOrJustFinished)
602         HandleLevelEditorKeyInput(key);
603       break;
604
605     case PLAYING:
606     {
607       switch(key)
608       {
609
610 #ifdef DEBUG
611         case KSYM_0:
612         case KSYM_1:
613         case KSYM_2:
614         case KSYM_3:
615         case KSYM_4:
616         case KSYM_5:
617         case KSYM_6:
618         case KSYM_7:
619         case KSYM_8:
620         case KSYM_9:
621           if (key == KSYM_0)
622           {
623             if (GameFrameDelay == 500)
624               GameFrameDelay = GAME_FRAME_DELAY;
625             else
626               GameFrameDelay = 500;
627           }
628           else
629             GameFrameDelay = (key - KSYM_0) * 10;
630           printf("Game speed == %d%% (%d ms delay between two frames)\n",
631                  GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
632           break;
633
634         case KSYM_d:
635           if (options.debug)
636           {
637             options.debug = FALSE;
638             printf("debug mode disabled\n");
639           }
640           else
641           {
642             options.debug = TRUE;
643             printf("debug mode enabled\n");
644           }
645           break;
646
647         case KSYM_s:
648           if (!global.fps_slowdown)
649           {
650             global.fps_slowdown = TRUE;
651             global.fps_slowdown_factor = 2;
652             printf("fps slowdown enabled -- display only every 2nd frame\n");
653           }
654           else if (global.fps_slowdown_factor == 2)
655           {
656             global.fps_slowdown_factor = 4;
657             printf("fps slowdown enabled -- display only every 4th frame\n");
658           }
659           else
660           {
661             global.fps_slowdown = FALSE;
662             global.fps_slowdown_factor = 1;
663             printf("fps slowdown disabled\n");
664           }
665           break;
666
667 #if 0
668         case KSYM_a:
669           if (ScrollStepSize == TILEX/8)
670             ScrollStepSize = TILEX/4;
671           else
672             ScrollStepSize = TILEX/8;
673           printf("ScrollStepSize == %d\n", ScrollStepSize);
674           break;
675 #endif
676
677 #if 0
678         case KSYM_m:
679           if (MoveSpeed == 8)
680           {
681             MoveSpeed = 4;
682             ScrollStepSize = TILEX/4;
683           }
684           else
685           {
686             MoveSpeed = 8;
687             ScrollStepSize = TILEX/8;
688           }
689           printf("MoveSpeed == %d\n", MoveSpeed);
690           break;
691 #endif
692
693         case KSYM_f:
694           ScrollStepSize = TILEX/8;
695           printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
696           break;
697
698         case KSYM_g:
699           ScrollStepSize = TILEX/4;
700           printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
701           break;
702
703         case KSYM_h:
704           ScrollStepSize = TILEX/2;
705           printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
706           break;
707
708         case KSYM_l:
709           ScrollStepSize = TILEX;
710           printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
711           break;
712
713         case KSYM_Q:
714         case KSYM_q:
715           local_player->dynamite = 1000;
716           break;
717
718
719
720 #if 0
721
722         case KSYM_z:
723           {
724             int i;
725
726             for(i=0; i<MAX_PLAYERS; i++)
727             {
728               printf("Player %d:\n", i);
729               printf("  jx == %d, jy == %d\n",
730                      stored_player[i].jx, stored_player[i].jy);
731               printf("  last_jx == %d, last_jy == %d\n",
732                      stored_player[i].last_jx, stored_player[i].last_jy);
733             }
734             printf("\n");
735           }
736
737           break;
738 #endif
739 #endif
740
741         default:
742           break;
743       }
744       break;
745     }
746     default:
747       break;
748   }
749 }
750
751 void HandleNoEvent()
752 {
753   if (button_status && game_status != PLAYING)
754   {
755     HandleButton(0, 0, -button_status);
756     return;
757   }
758
759 #if defined(PLATFORM_UNIX)
760   if (options.network)
761     HandleNetworking();
762 #endif
763
764   HandleJoystick();
765 }
766
767 static int HandleJoystickForAllPlayers()
768 {
769   int i;
770   int result = 0;
771
772   for (i=0; i<MAX_PLAYERS; i++)
773   {
774     byte joy_action = 0;
775
776     /*
777     if (!setup.input[i].use_joystick)
778       continue;
779       */
780
781     joy_action = Joystick(i);
782     result |= joy_action;
783
784
785     if (!setup.input[i].use_joystick)
786       continue;
787
788
789     stored_player[i].action = joy_action;
790   }
791
792   return result;
793 }
794
795 void HandleJoystick()
796 {
797   int joystick  = HandleJoystickForAllPlayers();
798   int keyboard  = key_joystick_mapping;
799   int joy       = (joystick | keyboard);
800   int left      = joy & JOY_LEFT;
801   int right     = joy & JOY_RIGHT;
802   int up        = joy & JOY_UP;
803   int down      = joy & JOY_DOWN;
804   int button    = joy & JOY_BUTTON;
805   int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
806   int dx        = (left ? -1    : right ? 1     : 0);
807   int dy        = (up   ? -1    : down  ? 1     : 0);
808
809   switch(game_status)
810   {
811     case MAINMENU:
812     case CHOOSELEVEL:
813     case SETUP:
814     case SETUPINPUT:
815     {
816       static unsigned long joystickmove_delay = 0;
817
818       if (joystick && !button &&
819           !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
820         newbutton = dx = dy = 0;
821
822       if (game_status==MAINMENU)
823         HandleMainMenu(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
824       else if (game_status==CHOOSELEVEL)
825         HandleChooseLevel(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
826       else if (game_status==SETUP)
827         HandleSetupScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
828       else if (game_status==SETUPINPUT)
829         HandleSetupInputScreen(0,0,dx,dy,
830                                newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
831       break;
832     }
833
834     case HALLOFFAME:
835       HandleHallOfFame(0,0, dx,dy, !newbutton);
836       break;
837
838     case HELPSCREEN:
839       HandleHelpScreen(!newbutton);
840       break;
841
842     case PLAYING:
843       if (tape.playing || keyboard)
844         newbutton = ((joy & JOY_BUTTON) != 0);
845
846       if (AllPlayersGone && newbutton)
847       {
848         CloseDoor(DOOR_CLOSE_1);
849         game_status = MAINMENU;
850         DrawMainMenu();
851         return;
852       }
853
854       break;
855
856     default:
857       break;
858   }
859 }