a4b60829e90a139f35aeff1bc19a9cbfab278b3e
[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 != PLAYING &&
481       game_status != LEVELED &&
482       game_status != CHOOSELEVEL &&
483       game_status != SETUP)
484   {
485     game_status = MAINMENU;
486     DrawMainMenu();
487     return;
488   }
489
490   /* special shortcuts for quick game tape saving and loading */
491   if (game_status == MAINMENU || game_status == PLAYING)
492   {
493     if (key == setup.shortcut.save_game)
494       TapeQuickSave();
495     else if (key == setup.shortcut.load_game)
496       TapeQuickLoad();
497   }
498
499
500 #ifndef DEBUG
501
502   if (game_status == PLAYING && (tape.playing || tape.pausing))
503     return;
504
505 #endif
506
507
508
509   HandleGadgetsKeyInput(key);
510
511   switch(game_status)
512   {
513     case TYPENAME:
514       HandleTypeName(0, key);
515       break;
516
517     case MAINMENU:
518     case CHOOSELEVEL:
519     case SETUP:
520       switch(key)
521       {
522         case KSYM_Return:
523         case KSYM_space:
524           if (game_status == MAINMENU)
525             HandleMainMenu(0,0, 0,0, MB_MENU_CHOICE);
526           else if (game_status == CHOOSELEVEL)
527             HandleChooseLevel(0,0, 0,0, MB_MENU_CHOICE);
528           else if (game_status == SETUP)
529             HandleSetupScreen(0,0, 0,0, MB_MENU_CHOICE);
530           break;
531
532         case KSYM_Escape:
533           if (game_status == CHOOSELEVEL)
534             HandleChooseLevel(0,0, 0,0, MB_MENU_LEAVE);
535           else if (game_status == SETUP)
536             HandleSetupScreen(0,0, 0,0, MB_MENU_LEAVE);
537           break;
538
539         case KSYM_Page_Up:
540           if (game_status == CHOOSELEVEL)
541             HandleChooseLevel(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
542           break;
543
544         case KSYM_Page_Down:
545           if (game_status == CHOOSELEVEL)
546             HandleChooseLevel(0,0, 0,SCR_FIELDY, MB_MENU_MARK);
547           break;
548
549 #ifdef DEBUG
550         case KSYM_t:
551           DumpTape(&tape);
552           break;
553 #endif
554
555         default:
556           break;
557       }
558       break;
559
560     case HELPSCREEN:
561       HandleHelpScreen(MB_RELEASED);
562       break;
563
564     case HALLOFFAME:
565       switch(key)
566       {
567         case KSYM_Return:
568         case KSYM_space:
569           game_status = MAINMENU;
570           DrawMainMenu();
571           BackToFront();
572           break;
573
574         case KSYM_Page_Up:
575           HandleHallOfFame(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
576           break;
577
578         case KSYM_Page_Down:
579           HandleHallOfFame(0,0, 0,SCR_FIELDY, MB_MENU_MARK);
580           break;
581
582         default:
583           break;
584       }
585       break;
586
587     case LEVELED:
588       if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
589         HandleLevelEditorKeyInput(key);
590       break;
591
592     case PLAYING:
593     {
594       switch(key)
595       {
596         case KSYM_Escape:
597           RequestQuitGame();
598           break;
599
600 #ifdef DEBUG
601         case KSYM_0:
602         case KSYM_1:
603         case KSYM_2:
604         case KSYM_3:
605         case KSYM_4:
606         case KSYM_5:
607         case KSYM_6:
608         case KSYM_7:
609         case KSYM_8:
610         case KSYM_9:
611           if (key == KSYM_0)
612           {
613             if (GameFrameDelay == 500)
614               GameFrameDelay = GAME_FRAME_DELAY;
615             else
616               GameFrameDelay = 500;
617           }
618           else
619             GameFrameDelay = (key - KSYM_0) * 10;
620           printf("Game speed == %d%% (%d ms delay between two frames)\n",
621                  GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
622           break;
623
624         case KSYM_d:
625           if (options.debug)
626           {
627             options.debug = FALSE;
628             printf("debug mode disabled\n");
629           }
630           else
631           {
632             options.debug = TRUE;
633             printf("debug mode enabled\n");
634           }
635           break;
636
637         case KSYM_s:
638           if (!global.fps_slowdown)
639           {
640             global.fps_slowdown = TRUE;
641             global.fps_slowdown_factor = 2;
642             printf("fps slowdown enabled -- display only every 2nd frame\n");
643           }
644           else if (global.fps_slowdown_factor == 2)
645           {
646             global.fps_slowdown_factor = 4;
647             printf("fps slowdown enabled -- display only every 4th frame\n");
648           }
649           else
650           {
651             global.fps_slowdown = FALSE;
652             global.fps_slowdown_factor = 1;
653             printf("fps slowdown disabled\n");
654           }
655           break;
656
657 #if 0
658         case KSYM_a:
659           if (ScrollStepSize == TILEX/8)
660             ScrollStepSize = TILEX/4;
661           else
662             ScrollStepSize = TILEX/8;
663           printf("ScrollStepSize == %d\n", ScrollStepSize);
664           break;
665 #endif
666
667 #if 0
668         case KSYM_m:
669           if (MoveSpeed == 8)
670           {
671             MoveSpeed = 4;
672             ScrollStepSize = TILEX/4;
673           }
674           else
675           {
676             MoveSpeed = 8;
677             ScrollStepSize = TILEX/8;
678           }
679           printf("MoveSpeed == %d\n", MoveSpeed);
680           break;
681 #endif
682
683         case KSYM_f:
684           ScrollStepSize = TILEX/8;
685           printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
686           break;
687
688         case KSYM_g:
689           ScrollStepSize = TILEX/4;
690           printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
691           break;
692
693         case KSYM_h:
694           ScrollStepSize = TILEX/2;
695           printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
696           break;
697
698         case KSYM_l:
699           ScrollStepSize = TILEX;
700           printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
701           break;
702
703         case KSYM_Q:
704         case KSYM_q:
705           local_player->dynamite = 1000;
706           break;
707
708
709
710 #if 0
711
712         case KSYM_z:
713           {
714             int i;
715
716             for(i=0; i<MAX_PLAYERS; i++)
717             {
718               printf("Player %d:\n", i);
719               printf("  jx == %d, jy == %d\n",
720                      stored_player[i].jx, stored_player[i].jy);
721               printf("  last_jx == %d, last_jy == %d\n",
722                      stored_player[i].last_jx, stored_player[i].last_jy);
723             }
724             printf("\n");
725           }
726
727           break;
728 #endif
729 #endif
730
731         default:
732           break;
733       }
734       break;
735     }
736     default:
737       break;
738   }
739 }
740
741 void HandleNoEvent()
742 {
743   if (button_status && game_status != PLAYING)
744   {
745     HandleButton(0, 0, -button_status);
746     return;
747   }
748
749 #if defined(PLATFORM_UNIX)
750   if (options.network)
751     HandleNetworking();
752 #endif
753
754   HandleJoystick();
755 }
756
757 static int HandleJoystickForAllPlayers()
758 {
759   int i;
760   int result = 0;
761
762   for (i=0; i<MAX_PLAYERS; i++)
763   {
764     byte joy_action = 0;
765
766     /*
767     if (!setup.input[i].use_joystick)
768       continue;
769       */
770
771     joy_action = Joystick(i);
772     result |= joy_action;
773
774
775     if (!setup.input[i].use_joystick)
776       continue;
777
778
779     stored_player[i].action = joy_action;
780   }
781
782   return result;
783 }
784
785 void HandleJoystick()
786 {
787   int joystick  = HandleJoystickForAllPlayers();
788   int keyboard  = key_joystick_mapping;
789   int joy       = (joystick | keyboard);
790   int left      = joy & JOY_LEFT;
791   int right     = joy & JOY_RIGHT;
792   int up        = joy & JOY_UP;
793   int down      = joy & JOY_DOWN;
794   int button    = joy & JOY_BUTTON;
795   int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
796   int dx        = (left ? -1    : right ? 1     : 0);
797   int dy        = (up   ? -1    : down  ? 1     : 0);
798
799   switch(game_status)
800   {
801     case MAINMENU:
802     case CHOOSELEVEL:
803     case SETUP:
804     {
805       static unsigned long joystickmove_delay = 0;
806
807       if (joystick && !button &&
808           !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
809         newbutton = dx = dy = 0;
810
811       if (game_status==MAINMENU)
812         HandleMainMenu(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
813       else if (game_status==CHOOSELEVEL)
814         HandleChooseLevel(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
815       else if (game_status==SETUP)
816         HandleSetupScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
817       break;
818     }
819
820     case HALLOFFAME:
821       HandleHallOfFame(0,0, dx,dy, !newbutton);
822       break;
823
824     case HELPSCREEN:
825       HandleHelpScreen(!newbutton);
826       break;
827
828     case PLAYING:
829       if (tape.playing || keyboard)
830         newbutton = ((joy & JOY_BUTTON) != 0);
831
832       if (AllPlayersGone && newbutton)
833       {
834         CloseDoor(DOOR_CLOSE_1);
835         game_status = MAINMENU;
836         DrawMainMenu();
837         return;
838       }
839
840       break;
841
842     default:
843       break;
844   }
845 }