rnd-20001125-4-src
[rocksndiamonds.git] / src / events.c
1 /***********************************************************
2 *  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
3 *----------------------------------------------------------*
4 *  (c) 1995-98 Artsoft Entertainment                       *
5 *              Holger Schemel                              *
6 *              Oststrasse 11a                              *
7 *              33604 Bielefeld                             *
8 *              phone: ++49 +521 290471                     *
9 *              email: aeglos@valinor.owl.de                *
10 *----------------------------------------------------------*
11 *  events.c                                                *
12 ***********************************************************/
13
14 #include "events.h"
15 #include "init.h"
16 #include "screens.h"
17 #include "tools.h"
18 #include "game.h"
19 #include "editor.h"
20 #include "misc.h"
21 #include "tape.h"
22 #include "joystick.h"
23 #include "buttons.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 void EventLoop(void)
49 {
50   while(1)
51   {
52     if (PendingEvent())         /* got event */
53     {
54       Event event;
55
56       NextEvent(&event);
57
58       if (FilterMouseMotionEvents(&event))
59       {
60         switch(event.type)
61         {
62           case EVENT_BUTTONPRESS:
63           case EVENT_BUTTONRELEASE:
64             HandleButtonEvent((ButtonEvent *) &event);
65             break;
66   
67           case EVENT_MOTIONNOTIFY:
68             HandleMotionEvent((MotionEvent *) &event);
69             break;
70   
71           case EVENT_KEYPRESS:
72           case EVENT_KEYRELEASE:
73             HandleKeyEvent((KeyEvent *) &event);
74             break;
75   
76           default:
77             HandleOtherEvents(&event);
78             break;
79         }
80       }
81     }
82     else
83       HandleNoEvent();
84
85     /* don't use all CPU time when idle; the main loop while playing
86        has its own synchronization and is CPU friendly, too */
87
88     if (game_status == PLAYING)
89       HandleGameActions();
90     else
91     {
92       SyncDisplay();
93       if (!PendingEvent())      /* delay only if no pending events */
94         Delay(10);
95     }
96
97     /* refresh window contents from drawing buffer, if needed */
98     BackToFront();
99
100     if (game_status == EXITGAME)
101       return;
102   }
103 }
104
105 void HandleOtherEvents(Event *event)
106 {
107   switch(event->type)
108   {
109     case EVENT_EXPOSE:
110       HandleExposeEvent((ExposeEvent *) event);
111       break;
112
113     case EVENT_UNMAPNOTIFY:
114       SleepWhileUnmapped();
115       break;
116
117     case EVENT_FOCUSIN:
118     case EVENT_FOCUSOUT:
119       HandleFocusEvent((FocusChangeEvent *) event);
120       break;
121
122     case EVENT_CLIENTMESSAGE:
123       HandleClientMessageEvent((ClientMessageEvent *) event);
124       break;
125
126 #ifdef USE_SDL_JOYSTICK
127     case SDL_JOYAXISMOTION:
128     case SDL_JOYBUTTONDOWN:
129     case SDL_JOYBUTTONUP:
130       HandleJoystickEvent(event);
131       break;
132 #endif
133
134     default:
135       break;
136   }
137 }
138
139 void ClearEventQueue()
140 {
141   while (PendingEvent())
142   {
143     Event event;
144
145     NextEvent(&event);
146
147     switch(event.type)
148     {
149       case EVENT_BUTTONRELEASE:
150         button_status = MB_RELEASED;
151         break;
152
153       case EVENT_KEYRELEASE:
154         key_joystick_mapping = 0;
155         break;
156
157       default:
158         HandleOtherEvents(&event);
159         break;
160     }
161   }
162 }
163
164 void SleepWhileUnmapped()
165 {
166   boolean window_unmapped = TRUE;
167
168   KeyboardAutoRepeatOn();
169
170   while(window_unmapped)
171   {
172     Event event;
173
174     NextEvent(&event);
175
176     switch(event.type)
177     {
178       case EVENT_BUTTONRELEASE:
179         button_status = MB_RELEASED;
180         break;
181
182       case EVENT_KEYRELEASE:
183         key_joystick_mapping = 0;
184         break;
185
186       case EVENT_MAPNOTIFY:
187         window_unmapped = FALSE;
188         break;
189
190       case EVENT_UNMAPNOTIFY:
191         /* this is only to surely prevent the 'should not happen' case
192          * of recursively looping between 'SleepWhileUnmapped()' and
193          * 'HandleOtherEvents()' which usually calls this funtion.
194          */
195         break;
196
197       default:
198         HandleOtherEvents(&event);
199         break;
200     }
201   }
202
203   if (game_status == PLAYING)
204     KeyboardAutoRepeatOff();
205 }
206
207 void HandleExposeEvent(ExposeEvent *event)
208 {
209 #ifndef TARGET_SDL
210   int x = event->x, y = event->y;
211   int width = event->width, height = event->height;
212
213   if (setup.direct_draw && game_status==PLAYING)
214   {
215     int xx,yy;
216     int x1 = (x-SX)/TILEX, y1 = (y-SY)/TILEY;
217     int x2 = (x-SX+width)/TILEX, y2 = (y-SY+height)/TILEY;
218
219     SetDrawtoField(DRAW_BACKBUFFER);
220
221     for(xx=0; xx<SCR_FIELDX; xx++)
222       for(yy=0; yy<SCR_FIELDY; yy++)
223         if (xx>=x1 && xx<=x2 && yy>=y1 && yy<=y2)
224           DrawScreenField(xx,yy);
225     DrawAllPlayers();
226
227     SetDrawtoField(DRAW_DIRECT);
228   }
229
230   if (setup.soft_scrolling && game_status == PLAYING)
231   {
232     int fx = FX, fy = FY;
233
234     fx += (ScreenMovDir & (MV_LEFT|MV_RIGHT) ? ScreenGfxPos : 0);
235     fy += (ScreenMovDir & (MV_UP|MV_DOWN)    ? ScreenGfxPos : 0);
236
237     BlitBitmap(fieldbuffer, backbuffer, fx,fy, SXSIZE,SYSIZE, SX,SY);
238   }
239
240   BlitBitmap(drawto, window, x,y, width,height, x,y);
241
242   FlushDisplay();
243 #endif
244 }
245
246 void HandleButtonEvent(ButtonEvent *event)
247 {
248   motion_status = FALSE;
249
250   if (event->type == EVENT_BUTTONPRESS)
251     button_status = event->button;
252   else
253     button_status = MB_RELEASED;
254
255   HandleButton(event->x, event->y, button_status);
256 }
257
258 void HandleMotionEvent(MotionEvent *event)
259 {
260   if (!PointerInWindow(window))
261     return;     /* window and pointer are on different screens */
262
263 #if 1
264   if (button_status == MB_RELEASED && game_status != LEVELED)
265     return;
266 #endif
267
268   motion_status = TRUE;
269
270   HandleButton(event->x, event->y, button_status);
271 }
272
273 void HandleKeyEvent(KeyEvent *event)
274 {
275   int key_status = (event->type==EVENT_KEYPRESS ? KEY_PRESSED : KEY_RELEASED);
276   boolean with_modifiers = (game_status == PLAYING ? FALSE : TRUE);
277   Key key = GetEventKey(event, with_modifiers);
278
279   HandleKey(key, key_status);
280 }
281
282 void HandleFocusEvent(FocusChangeEvent *event)
283 {
284   static int old_joystick_status = -1;
285
286   if (event->type == EVENT_FOCUSOUT)
287   {
288     int i;
289
290     KeyboardAutoRepeatOn();
291     old_joystick_status = joystick_status;
292     joystick_status = JOYSTICK_OFF;
293
294     /* simulate key release events for still pressed keys */
295     key_joystick_mapping = 0;
296     for (i=0; i<MAX_PLAYERS; i++)
297       stored_player[i].action = 0;
298   }
299   else if (event->type == EVENT_FOCUSIN)
300   {
301     /* When there are two Rocks'n'Diamonds windows which overlap and
302        the player moves the pointer from one game window to the other,
303        a 'FocusOut' event is generated for the window the pointer is
304        leaving and a 'FocusIn' event is generated for the window the
305        pointer is entering. In some cases, it can happen that the
306        'FocusIn' event is handled by the one game process before the
307        'FocusOut' event by the other game process. In this case the
308        X11 environment would end up with activated keyboard auto repeat,
309        because unfortunately this is a global setting and not (which
310        would be far better) set for each X11 window individually.
311        The effect would be keyboard auto repeat while playing the game
312        (game_status == PLAYING), which is not desired.
313        To avoid this special case, we just wait 1/10 second before
314        processing the 'FocusIn' event.
315     */
316
317     if (game_status == PLAYING)
318     {
319       Delay(100);
320       KeyboardAutoRepeatOff();
321     }
322     if (old_joystick_status != -1)
323       joystick_status = old_joystick_status;
324   }
325 }
326
327 void HandleClientMessageEvent(ClientMessageEvent *event)
328 {
329   if (CheckCloseWindowEvent(event))
330     CloseAllAndExit(0);
331 }
332
333 void HandleButton(int mx, int my, int button)
334 {
335   static int old_mx = 0, old_my = 0;
336
337   if (button < 0)
338   {
339     mx = old_mx;
340     my = old_my;
341     button = -button;
342   }
343   else
344   {
345     old_mx = mx;
346     old_my = my;
347   }
348
349   HandleGadgets(mx, my, button);
350
351   switch(game_status)
352   {
353     case MAINMENU:
354       HandleMainMenu(mx,my, 0,0, button);
355       break;
356
357     case TYPENAME:
358       HandleTypeName(0, KEY_Return);
359       break;
360
361     case CHOOSELEVEL:
362       HandleChooseLevel(mx,my, 0,0, button);
363       break;
364
365     case HALLOFFAME:
366       HandleHallOfFame(0,0, 0,0, button);
367       break;
368
369     case LEVELED:
370       break;
371
372     case HELPSCREEN:
373       HandleHelpScreen(button);
374       break;
375
376     case SETUP:
377       HandleSetupScreen(mx,my, 0,0, button);
378       break;
379
380     case SETUPINPUT:
381       HandleSetupInputScreen(mx,my, 0,0, button);
382       break;
383
384     case PLAYING:
385 #ifdef DEBUG
386       if (button == MB_RELEASED)
387       {
388         int sx = (mx - SX) / TILEX;
389         int sy = (my - SY) / TILEY;
390
391         if (IN_VIS_FIELD(sx,sy))
392         {
393           int x = LEVELX(sx);
394           int y = LEVELY(sy);
395
396           printf("INFO: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
397
398           if (!IN_LEV_FIELD(x, y))
399             break;
400
401           printf("      Feld[%d][%d] == %d\n", x,y, Feld[x][y]);
402           printf("      Store[%d][%d] == %d\n", x,y, Store[x][y]);
403           printf("      Store2[%d][%d] == %d\n", x,y, Store2[x][y]);
404           printf("      StorePlayer[%d][%d] == %d\n", x,y, StorePlayer[x][y]);
405           printf("      MovPos[%d][%d] == %d\n", x,y, MovPos[x][y]);
406           printf("      MovDir[%d][%d] == %d\n", x,y, MovDir[x][y]);
407           printf("      MovDelay[%d][%d] == %d\n", x,y, MovDelay[x][y]);
408           printf("\n");
409         }
410       }
411 #endif
412       break;
413
414     default:
415       break;
416   }
417 }
418
419 void HandleKey(Key key, int key_status)
420 {
421   int joy = 0;
422   boolean anyTextGadgetActiveOrJustFinished = anyTextGadgetActive();
423   static struct SetupKeyboardInfo custom_key;
424   static struct
425   {
426     Key *key_custom;
427     Key key_default;
428     byte action;
429   } key_info[] =
430   {
431     { &custom_key.left,  DEFAULT_KEY_LEFT,  JOY_LEFT     },
432     { &custom_key.right, DEFAULT_KEY_RIGHT, JOY_RIGHT    },
433     { &custom_key.up,    DEFAULT_KEY_UP,    JOY_UP       },
434     { &custom_key.down,  DEFAULT_KEY_DOWN,  JOY_DOWN     },
435     { &custom_key.snap,  DEFAULT_KEY_SNAP,  JOY_BUTTON_1 },
436     { &custom_key.bomb,  DEFAULT_KEY_BOMB,  JOY_BUTTON_2 }
437   };
438
439   if (game_status == PLAYING)
440   {
441     int pnr;
442
443     for (pnr=0; pnr<MAX_PLAYERS; pnr++)
444     {
445       int i;
446       byte key_action = 0;
447
448       if (setup.input[pnr].use_joystick)
449         continue;
450
451       custom_key = setup.input[pnr].key;
452
453       for (i=0; i<6; i++)
454         if (key == *key_info[i].key_custom)
455           key_action |= key_info[i].action;
456
457       if (key_status == KEY_PRESSED)
458         stored_player[pnr].action |= key_action;
459       else
460         stored_player[pnr].action &= ~key_action;
461     }
462   }
463   else
464   {
465     int i;
466
467     for (i=0; i<6; i++)
468       if (key == key_info[i].key_default)
469         joy |= key_info[i].action;
470   }
471
472   if (joy)
473   {
474     if (key_status == KEY_PRESSED)
475       key_joystick_mapping |= joy;
476     else
477       key_joystick_mapping &= ~joy;
478
479     HandleJoystick();
480   }
481
482   if (game_status != PLAYING)
483     key_joystick_mapping = 0;
484
485   if (key_status == KEY_RELEASED)
486     return;
487
488   if ((key == KEY_Return || key == KEY_space) &&
489       game_status == PLAYING && AllPlayersGone)
490   {
491     CloseDoor(DOOR_CLOSE_1);
492     game_status = MAINMENU;
493     DrawMainMenu();
494     return;
495   }
496
497   /* allow quick escape to the main menu with the Escape key */
498   if (key == KEY_Escape && game_status != MAINMENU)
499   {
500     CloseDoor(DOOR_CLOSE_1 | DOOR_OPEN_2 | DOOR_NO_DELAY);
501     game_status = MAINMENU;
502     DrawMainMenu();
503     return;
504   }
505
506
507
508 #ifndef DEBUG
509
510   if (game_status == PLAYING && (tape.playing || tape.pausing))
511     return;
512
513 #endif
514
515
516
517   HandleGadgetsKeyInput(key);
518
519   switch(game_status)
520   {
521     case TYPENAME:
522       HandleTypeName(0, key);
523       break;
524
525     case MAINMENU:
526     case CHOOSELEVEL:
527     case SETUP:
528     case SETUPINPUT:
529       switch(key)
530       {
531         case KEY_Return:
532         case KEY_space:
533           if (game_status == MAINMENU)
534             HandleMainMenu(0,0, 0,0, MB_MENU_CHOICE);
535           else if (game_status == CHOOSELEVEL)
536             HandleChooseLevel(0,0, 0,0, MB_MENU_CHOICE);
537           else if (game_status == SETUP)
538             HandleSetupScreen(0,0, 0,0, MB_MENU_CHOICE);
539           else if (game_status == SETUPINPUT)
540             HandleSetupInputScreen(0,0, 0,0, MB_MENU_CHOICE);
541           break;
542
543         case KEY_Page_Up:
544           if (game_status == CHOOSELEVEL)
545             HandleChooseLevel(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
546           break;
547
548         case KEY_Page_Down:
549           if (game_status == CHOOSELEVEL)
550             HandleChooseLevel(0,0, 0,SCR_FIELDY, MB_MENU_MARK);
551           break;
552
553         default:
554           break;
555       }
556       break;
557
558     case HELPSCREEN:
559       HandleHelpScreen(MB_RELEASED);
560       break;
561
562     case HALLOFFAME:
563       switch(key)
564       {
565         case KEY_Return:
566         case KEY_space:
567           game_status = MAINMENU;
568           DrawMainMenu();
569           BackToFront();
570           break;
571
572         case KEY_Page_Up:
573           HandleHallOfFame(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
574           break;
575
576         case KEY_Page_Down:
577           HandleHallOfFame(0,0, 0,SCR_FIELDY, MB_MENU_MARK);
578           break;
579
580         default:
581           break;
582       }
583       break;
584
585     case LEVELED:
586       if (!anyTextGadgetActiveOrJustFinished)
587         HandleLevelEditorKeyInput(key);
588       break;
589
590     case PLAYING:
591     {
592       switch(key)
593       {
594
595 #ifdef DEBUG
596         case KEY_0:
597         case KEY_1:
598         case KEY_2:
599         case KEY_3:
600         case KEY_4:
601         case KEY_5:
602         case KEY_6:
603         case KEY_7:
604         case KEY_8:
605         case KEY_9:
606           if (key == KEY_0)
607           {
608             if (GameFrameDelay == 500)
609               GameFrameDelay = GAME_FRAME_DELAY;
610             else
611               GameFrameDelay = 500;
612           }
613           else
614             GameFrameDelay = (key - KEY_0) * 10;
615           printf("Game speed == %d%% (%d ms delay between two frames)\n",
616                  GAME_FRAME_DELAY * 100 / GameFrameDelay, GameFrameDelay);
617           break;
618
619         case KEY_d:
620           if (options.debug)
621           {
622             options.debug = FALSE;
623             printf("debug mode disabled\n");
624           }
625           else
626           {
627             options.debug = TRUE;
628             printf("debug mode enabled\n");
629           }
630           break;
631
632         case KEY_s:
633           if (!global.fps_slowdown)
634           {
635             global.fps_slowdown = TRUE;
636             global.fps_slowdown_factor = 2;
637             printf("fps slowdown enabled -- display only every 2nd frame\n");
638           }
639           else if (global.fps_slowdown_factor == 2)
640           {
641             global.fps_slowdown_factor = 4;
642             printf("fps slowdown enabled -- display only every 4th frame\n");
643           }
644           else
645           {
646             global.fps_slowdown = FALSE;
647             global.fps_slowdown_factor = 1;
648             printf("fps slowdown disabled\n");
649           }
650           break;
651
652 #if 0
653         case KEY_a:
654           if (ScrollStepSize == TILEX/8)
655             ScrollStepSize = TILEX/4;
656           else
657             ScrollStepSize = TILEX/8;
658           printf("ScrollStepSize == %d\n", ScrollStepSize);
659           break;
660 #endif
661
662 #if 0
663         case KEY_m:
664           if (MoveSpeed == 8)
665           {
666             MoveSpeed = 4;
667             ScrollStepSize = TILEX/4;
668           }
669           else
670           {
671             MoveSpeed = 8;
672             ScrollStepSize = TILEX/8;
673           }
674           printf("MoveSpeed == %d\n", MoveSpeed);
675           break;
676 #endif
677
678         case KEY_f:
679           ScrollStepSize = TILEX/8;
680           printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
681           break;
682
683         case KEY_g:
684           ScrollStepSize = TILEX/4;
685           printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
686           break;
687
688         case KEY_h:
689           ScrollStepSize = TILEX/2;
690           printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
691           break;
692
693         case KEY_l:
694           ScrollStepSize = TILEX;
695           printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
696           break;
697
698         case KEY_Q:
699           local_player->dynamite = 1000;
700           break;
701
702
703
704 #if 0
705
706         case KEY_z:
707           {
708             int i;
709
710             for(i=0; i<MAX_PLAYERS; i++)
711             {
712               printf("Player %d:\n", i);
713               printf("  jx == %d, jy == %d\n",
714                      stored_player[i].jx, stored_player[i].jy);
715               printf("  last_jx == %d, last_jy == %d\n",
716                      stored_player[i].last_jx, stored_player[i].last_jy);
717             }
718             printf("\n");
719           }
720
721           break;
722 #endif
723 #endif
724
725         default:
726           break;
727       }
728       break;
729     }
730     default:
731       break;
732   }
733 }
734
735 void HandleNoEvent()
736 {
737   if (button_status && game_status != PLAYING)
738   {
739     HandleButton(0, 0, -button_status);
740     return;
741   }
742
743 #if !defined(MSDOS) && !defined(WIN32)
744   if (options.network)
745     HandleNetworking();
746 #endif
747
748   HandleJoystick();
749 }
750
751 static int HandleJoystickForAllPlayers()
752 {
753   int i;
754   int result = 0;
755
756   for (i=0; i<MAX_PLAYERS; i++)
757   {
758     byte joy_action = 0;
759
760     /*
761     if (!setup.input[i].use_joystick)
762       continue;
763       */
764
765     joy_action = Joystick(i);
766     result |= joy_action;
767
768
769     if (!setup.input[i].use_joystick)
770       continue;
771
772
773     stored_player[i].action = joy_action;
774   }
775
776   return result;
777 }
778
779 void HandleJoystick()
780 {
781   int joystick  = HandleJoystickForAllPlayers();
782   int keyboard  = key_joystick_mapping;
783   int joy       = (joystick | keyboard);
784   int left      = joy & JOY_LEFT;
785   int right     = joy & JOY_RIGHT;
786   int up        = joy & JOY_UP;
787   int down      = joy & JOY_DOWN;
788   int button    = joy & JOY_BUTTON;
789   int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
790   int dx        = (left ? -1    : right ? 1     : 0);
791   int dy        = (up   ? -1    : down  ? 1     : 0);
792
793   switch(game_status)
794   {
795     case MAINMENU:
796     case CHOOSELEVEL:
797     case SETUP:
798     case SETUPINPUT:
799     {
800       static unsigned long joystickmove_delay = 0;
801
802       if (joystick && !button &&
803           !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
804         newbutton = dx = dy = 0;
805
806       if (game_status==MAINMENU)
807         HandleMainMenu(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
808       else if (game_status==CHOOSELEVEL)
809         HandleChooseLevel(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
810       else if (game_status==SETUP)
811         HandleSetupScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
812       else if (game_status==SETUPINPUT)
813         HandleSetupInputScreen(0,0,dx,dy,
814                                newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
815       break;
816     }
817
818     case HALLOFFAME:
819       HandleHallOfFame(0,0, dx,dy, !newbutton);
820       break;
821
822     case HELPSCREEN:
823       HandleHelpScreen(!newbutton);
824       break;
825
826     case PLAYING:
827       if (tape.playing || keyboard)
828         newbutton = ((joy & JOY_BUTTON) != 0);
829
830       if (AllPlayersGone && newbutton)
831       {
832         CloseDoor(DOOR_CLOSE_1);
833         game_status = MAINMENU;
834         DrawMainMenu();
835         return;
836       }
837
838       break;
839
840     default:
841       break;
842   }
843 }