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