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