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