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