rnd-20020401-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 "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   RedrawPlayfield(FALSE, event->x, event->y, event->width, event->height);
226   FlushDisplay();
227 #endif
228 }
229
230 void HandleButtonEvent(ButtonEvent *event)
231 {
232   motion_status = FALSE;
233
234   if (event->type == EVENT_BUTTONPRESS)
235     button_status = event->button;
236   else
237     button_status = MB_RELEASED;
238
239   HandleButton(event->x, event->y, button_status);
240 }
241
242 void HandleMotionEvent(MotionEvent *event)
243 {
244   if (!PointerInWindow(window))
245     return;     /* window and pointer are on different screens */
246
247 #if 1
248   if (button_status == MB_RELEASED && game_status != LEVELED)
249     return;
250 #endif
251
252   motion_status = TRUE;
253
254   HandleButton(event->x, event->y, button_status);
255 }
256
257 void HandleKeyEvent(KeyEvent *event)
258 {
259   int key_status = (event->type==EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
260   boolean with_modifiers = (game_status == PLAYING ? FALSE : TRUE);
261   Key key = GetEventKey(event, with_modifiers);
262
263   HandleKey(key, key_status);
264 }
265
266 void HandleFocusEvent(FocusChangeEvent *event)
267 {
268   static int old_joystick_status = -1;
269
270   if (event->type == EVENT_FOCUSOUT)
271   {
272     int i;
273
274     KeyboardAutoRepeatOn();
275     old_joystick_status = joystick.status;
276     joystick.status = JOYSTICK_NOT_AVAILABLE;
277
278     /* simulate key release events for still pressed keys */
279     key_joystick_mapping = 0;
280     for (i=0; i<MAX_PLAYERS; i++)
281       stored_player[i].action = 0;
282   }
283   else if (event->type == EVENT_FOCUSIN)
284   {
285     /* When there are two Rocks'n'Diamonds windows which overlap and
286        the player moves the pointer from one game window to the other,
287        a 'FocusOut' event is generated for the window the pointer is
288        leaving and a 'FocusIn' event is generated for the window the
289        pointer is entering. In some cases, it can happen that the
290        'FocusIn' event is handled by the one game process before the
291        'FocusOut' event by the other game process. In this case the
292        X11 environment would end up with activated keyboard auto repeat,
293        because unfortunately this is a global setting and not (which
294        would be far better) set for each X11 window individually.
295        The effect would be keyboard auto repeat while playing the game
296        (game_status == PLAYING), which is not desired.
297        To avoid this special case, we just wait 1/10 second before
298        processing the 'FocusIn' event.
299     */
300
301     if (game_status == PLAYING)
302     {
303       Delay(100);
304       KeyboardAutoRepeatOff();
305     }
306     if (old_joystick_status != -1)
307       joystick.status = old_joystick_status;
308   }
309 }
310
311 void HandleClientMessageEvent(ClientMessageEvent *event)
312 {
313   if (CheckCloseWindowEvent(event))
314     CloseAllAndExit(0);
315 }
316
317 void HandleButton(int mx, int my, int button)
318 {
319   static int old_mx = 0, old_my = 0;
320
321   if (button < 0)
322   {
323     mx = old_mx;
324     my = old_my;
325     button = -button;
326   }
327   else
328   {
329     old_mx = mx;
330     old_my = my;
331   }
332
333   HandleGadgets(mx, my, button);
334
335   switch(game_status)
336   {
337     case MAINMENU:
338       HandleMainMenu(mx,my, 0,0, button);
339       break;
340
341     case TYPENAME:
342       HandleTypeName(0, KSYM_Return);
343       break;
344
345     case CHOOSELEVEL:
346       HandleChooseLevel(mx,my, 0,0, button);
347       break;
348
349     case HALLOFFAME:
350       HandleHallOfFame(0,0, 0,0, button);
351       break;
352
353     case LEVELED:
354       break;
355
356     case HELPSCREEN:
357       HandleHelpScreen(button);
358       break;
359
360     case SETUP:
361       HandleSetupScreen(mx,my, 0,0, button);
362       break;
363
364     case PLAYING:
365 #ifdef DEBUG
366       if (button == MB_RELEASED)
367       {
368         int sx = (mx - SX) / TILEX;
369         int sy = (my - SY) / TILEY;
370
371         if (IN_VIS_FIELD(sx,sy))
372         {
373           int x = LEVELX(sx);
374           int y = LEVELY(sy);
375
376           printf("INFO: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
377
378           if (!IN_LEV_FIELD(x, y))
379             break;
380
381           printf("      Feld[%d][%d] == %d\n", x,y, Feld[x][y]);
382           printf("      Store[%d][%d] == %d\n", x,y, Store[x][y]);
383           printf("      Store2[%d][%d] == %d\n", x,y, Store2[x][y]);
384           printf("      StorePlayer[%d][%d] == %d\n", x,y, StorePlayer[x][y]);
385           printf("      MovPos[%d][%d] == %d\n", x,y, MovPos[x][y]);
386           printf("      MovDir[%d][%d] == %d\n", x,y, MovDir[x][y]);
387           printf("      MovDelay[%d][%d] == %d\n", x,y, MovDelay[x][y]);
388           printf("\n");
389         }
390       }
391 #endif
392       break;
393
394     default:
395       break;
396   }
397 }
398
399 void HandleKey(Key key, int key_status)
400 {
401   int joy = 0;
402   boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
403   static struct SetupKeyboardInfo custom_key;
404   static struct
405   {
406     Key *key_custom;
407     Key key_default;
408     byte action;
409   } key_info[] =
410   {
411     { &custom_key.left,  DEFAULT_KEY_LEFT,  JOY_LEFT     },
412     { &custom_key.right, DEFAULT_KEY_RIGHT, JOY_RIGHT    },
413     { &custom_key.up,    DEFAULT_KEY_UP,    JOY_UP       },
414     { &custom_key.down,  DEFAULT_KEY_DOWN,  JOY_DOWN     },
415     { &custom_key.snap,  DEFAULT_KEY_SNAP,  JOY_BUTTON_1 },
416     { &custom_key.bomb,  DEFAULT_KEY_BOMB,  JOY_BUTTON_2 }
417   };
418
419   if (game_status == PLAYING)
420   {
421     int pnr;
422
423     for (pnr=0; pnr<MAX_PLAYERS; pnr++)
424     {
425       int i;
426       byte key_action = 0;
427
428       if (setup.input[pnr].use_joystick)
429         continue;
430
431       custom_key = setup.input[pnr].key;
432
433       for (i=0; i<6; i++)
434         if (key == *key_info[i].key_custom)
435           key_action |= key_info[i].action;
436
437       if (key_status == KEY_PRESSED)
438         stored_player[pnr].action |= key_action;
439       else
440         stored_player[pnr].action &= ~key_action;
441     }
442   }
443   else
444   {
445     int i;
446
447     for (i=0; i<6; i++)
448       if (key == key_info[i].key_default)
449         joy |= key_info[i].action;
450   }
451
452   if (joy)
453   {
454     if (key_status == KEY_PRESSED)
455       key_joystick_mapping |= joy;
456     else
457       key_joystick_mapping &= ~joy;
458
459     HandleJoystick();
460   }
461
462   if (game_status != PLAYING)
463     key_joystick_mapping = 0;
464
465   if (key_status == KEY_RELEASED)
466     return;
467
468   if ((key == KSYM_Return || key == KSYM_space) &&
469       game_status == PLAYING && AllPlayersGone)
470   {
471     CloseDoor(DOOR_CLOSE_1);
472     game_status = MAINMENU;
473     DrawMainMenu();
474     return;
475   }
476
477   /* allow quick escape to the main menu with the Escape key */
478   if (key == KSYM_Escape &&
479       game_status != MAINMENU &&
480       game_status != LEVELED &&
481       game_status != CHOOSELEVEL &&
482       game_status != SETUP)
483   {
484     game_status = MAINMENU;
485     DrawMainMenu();
486     return;
487   }
488
489   /* special shortcuts for quick game tape saving and loading */
490   if (game_status == MAINMENU || game_status == PLAYING)
491   {
492     if (key == KSYM_F1)         /* save game */
493       TapeQuickSave();
494     else if (key == KSYM_F2)    /* load game */
495       TapeQuickLoad();
496   }
497
498
499 #ifndef DEBUG
500
501   if (game_status == PLAYING && (tape.playing || tape.pausing))
502     return;
503
504 #endif
505
506
507
508   HandleGadgetsKeyInput(key);
509
510   switch(game_status)
511   {
512     case TYPENAME:
513       HandleTypeName(0, key);
514       break;
515
516     case MAINMENU:
517     case CHOOSELEVEL:
518     case SETUP:
519       switch(key)
520       {
521         case KSYM_Return:
522         case KSYM_space:
523           if (game_status == MAINMENU)
524             HandleMainMenu(0,0, 0,0, MB_MENU_CHOICE);
525           else if (game_status == CHOOSELEVEL)
526             HandleChooseLevel(0,0, 0,0, MB_MENU_CHOICE);
527           else if (game_status == SETUP)
528             HandleSetupScreen(0,0, 0,0, MB_MENU_CHOICE);
529           break;
530
531         case KSYM_Escape:
532           if (game_status == CHOOSELEVEL)
533             HandleChooseLevel(0,0, 0,0, MB_MENU_LEAVE);
534           else if (game_status == SETUP)
535             HandleSetupScreen(0,0, 0,0, MB_MENU_LEAVE);
536           break;
537
538         case KSYM_Page_Up:
539           if (game_status == CHOOSELEVEL)
540             HandleChooseLevel(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
541           break;
542
543         case KSYM_Page_Down:
544           if (game_status == CHOOSELEVEL)
545             HandleChooseLevel(0,0, 0,SCR_FIELDY, MB_MENU_MARK);
546           break;
547
548 #ifdef DEBUG
549         case KSYM_t:
550           DumpTape(&tape);
551           break;
552 #endif
553
554         default:
555           break;
556       }
557       break;
558
559     case HELPSCREEN:
560       HandleHelpScreen(MB_RELEASED);
561       break;
562
563     case HALLOFFAME:
564       switch(key)
565       {
566         case KSYM_Return:
567         case KSYM_space:
568           game_status = MAINMENU;
569           DrawMainMenu();
570           BackToFront();
571           break;
572
573         case KSYM_Page_Up:
574           HandleHallOfFame(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
575           break;
576
577         case KSYM_Page_Down:
578           HandleHallOfFame(0,0, 0,SCR_FIELDY, MB_MENU_MARK);
579           break;
580
581         default:
582           break;
583       }
584       break;
585
586     case LEVELED:
587       if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
588         HandleLevelEditorKeyInput(key);
589       break;
590
591     case PLAYING:
592     {
593       switch(key)
594       {
595
596 #ifdef DEBUG
597         case KSYM_0:
598         case KSYM_1:
599         case KSYM_2:
600         case KSYM_3:
601         case KSYM_4:
602         case KSYM_5:
603         case KSYM_6:
604         case KSYM_7:
605         case KSYM_8:
606         case KSYM_9:
607           if (key == KSYM_0)
608           {
609             if (GameFrameDelay == 500)
610               GameFrameDelay = GAME_FRAME_DELAY;
611             else
612               GameFrameDelay = 500;
613           }
614           else
615             GameFrameDelay = (key - KSYM_0) * 10;
616           printf("Game speed == %d%% (%d ms delay between two frames)\n",
617                  GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
618           break;
619
620         case KSYM_d:
621           if (options.debug)
622           {
623             options.debug = FALSE;
624             printf("debug mode disabled\n");
625           }
626           else
627           {
628             options.debug = TRUE;
629             printf("debug mode enabled\n");
630           }
631           break;
632
633         case KSYM_s:
634           if (!global.fps_slowdown)
635           {
636             global.fps_slowdown = TRUE;
637             global.fps_slowdown_factor = 2;
638             printf("fps slowdown enabled -- display only every 2nd frame\n");
639           }
640           else if (global.fps_slowdown_factor == 2)
641           {
642             global.fps_slowdown_factor = 4;
643             printf("fps slowdown enabled -- display only every 4th frame\n");
644           }
645           else
646           {
647             global.fps_slowdown = FALSE;
648             global.fps_slowdown_factor = 1;
649             printf("fps slowdown disabled\n");
650           }
651           break;
652
653 #if 0
654         case KSYM_a:
655           if (ScrollStepSize == TILEX/8)
656             ScrollStepSize = TILEX/4;
657           else
658             ScrollStepSize = TILEX/8;
659           printf("ScrollStepSize == %d\n", ScrollStepSize);
660           break;
661 #endif
662
663 #if 0
664         case KSYM_m:
665           if (MoveSpeed == 8)
666           {
667             MoveSpeed = 4;
668             ScrollStepSize = TILEX/4;
669           }
670           else
671           {
672             MoveSpeed = 8;
673             ScrollStepSize = TILEX/8;
674           }
675           printf("MoveSpeed == %d\n", MoveSpeed);
676           break;
677 #endif
678
679         case KSYM_f:
680           ScrollStepSize = TILEX/8;
681           printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
682           break;
683
684         case KSYM_g:
685           ScrollStepSize = TILEX/4;
686           printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
687           break;
688
689         case KSYM_h:
690           ScrollStepSize = TILEX/2;
691           printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
692           break;
693
694         case KSYM_l:
695           ScrollStepSize = TILEX;
696           printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
697           break;
698
699         case KSYM_Q:
700         case KSYM_q:
701           local_player->dynamite = 1000;
702           break;
703
704
705
706 #if 0
707
708         case KSYM_z:
709           {
710             int i;
711
712             for(i=0; i<MAX_PLAYERS; i++)
713             {
714               printf("Player %d:\n", i);
715               printf("  jx == %d, jy == %d\n",
716                      stored_player[i].jx, stored_player[i].jy);
717               printf("  last_jx == %d, last_jy == %d\n",
718                      stored_player[i].last_jx, stored_player[i].last_jy);
719             }
720             printf("\n");
721           }
722
723           break;
724 #endif
725 #endif
726
727         default:
728           break;
729       }
730       break;
731     }
732     default:
733       break;
734   }
735 }
736
737 void HandleNoEvent()
738 {
739   if (button_status && game_status != PLAYING)
740   {
741     HandleButton(0, 0, -button_status);
742     return;
743   }
744
745 #if defined(PLATFORM_UNIX)
746   if (options.network)
747     HandleNetworking();
748 #endif
749
750   HandleJoystick();
751 }
752
753 static int HandleJoystickForAllPlayers()
754 {
755   int i;
756   int result = 0;
757
758   for (i=0; i<MAX_PLAYERS; i++)
759   {
760     byte joy_action = 0;
761
762     /*
763     if (!setup.input[i].use_joystick)
764       continue;
765       */
766
767     joy_action = Joystick(i);
768     result |= joy_action;
769
770
771     if (!setup.input[i].use_joystick)
772       continue;
773
774
775     stored_player[i].action = joy_action;
776   }
777
778   return result;
779 }
780
781 void HandleJoystick()
782 {
783   int joystick  = HandleJoystickForAllPlayers();
784   int keyboard  = key_joystick_mapping;
785   int joy       = (joystick | keyboard);
786   int left      = joy & JOY_LEFT;
787   int right     = joy & JOY_RIGHT;
788   int up        = joy & JOY_UP;
789   int down      = joy & JOY_DOWN;
790   int button    = joy & JOY_BUTTON;
791   int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
792   int dx        = (left ? -1    : right ? 1     : 0);
793   int dy        = (up   ? -1    : down  ? 1     : 0);
794
795   switch(game_status)
796   {
797     case MAINMENU:
798     case CHOOSELEVEL:
799     case SETUP:
800     {
801       static unsigned long joystickmove_delay = 0;
802
803       if (joystick && !button &&
804           !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
805         newbutton = dx = dy = 0;
806
807       if (game_status==MAINMENU)
808         HandleMainMenu(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
809       else if (game_status==CHOOSELEVEL)
810         HandleChooseLevel(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
811       else if (game_status==SETUP)
812         HandleSetupScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
813       break;
814     }
815
816     case HALLOFFAME:
817       HandleHallOfFame(0,0, dx,dy, !newbutton);
818       break;
819
820     case HELPSCREEN:
821       HandleHelpScreen(!newbutton);
822       break;
823
824     case PLAYING:
825       if (tape.playing || keyboard)
826         newbutton = ((joy & JOY_BUTTON) != 0);
827
828       if (AllPlayersGone && newbutton)
829       {
830         CloseDoor(DOOR_CLOSE_1);
831         game_status = MAINMENU;
832         DrawMainMenu();
833         return;
834       }
835
836       break;
837
838     default:
839       break;
840   }
841 }