rnd-20001205-3-src
[rocksndiamonds.git] / src / events.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2000 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 "tape.h"
23 #include "joystick.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 #if defined(TARGET_SDL)
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, KSYM_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 == KSYM_Return || key == KSYM_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 == KSYM_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 KSYM_Return:
532         case KSYM_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 KSYM_Page_Up:
544           if (game_status == CHOOSELEVEL)
545             HandleChooseLevel(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
546           break;
547
548         case KSYM_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 KSYM_Return:
566         case KSYM_space:
567           game_status = MAINMENU;
568           DrawMainMenu();
569           BackToFront();
570           break;
571
572         case KSYM_Page_Up:
573           HandleHallOfFame(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
574           break;
575
576         case KSYM_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 KSYM_0:
597         case KSYM_1:
598         case KSYM_2:
599         case KSYM_3:
600         case KSYM_4:
601         case KSYM_5:
602         case KSYM_6:
603         case KSYM_7:
604         case KSYM_8:
605         case KSYM_9:
606           if (key == KSYM_0)
607           {
608             if (GameFrameDelay == 500)
609               GameFrameDelay = GAME_FRAME_DELAY;
610             else
611               GameFrameDelay = 500;
612           }
613           else
614             GameFrameDelay = (key - KSYM_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 KSYM_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 KSYM_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 KSYM_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 KSYM_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 KSYM_f:
679           ScrollStepSize = TILEX/8;
680           printf("ScrollStepSize == %d (1/8)\n", ScrollStepSize);
681           break;
682
683         case KSYM_g:
684           ScrollStepSize = TILEX/4;
685           printf("ScrollStepSize == %d (1/4)\n", ScrollStepSize);
686           break;
687
688         case KSYM_h:
689           ScrollStepSize = TILEX/2;
690           printf("ScrollStepSize == %d (1/2)\n", ScrollStepSize);
691           break;
692
693         case KSYM_l:
694           ScrollStepSize = TILEX;
695           printf("ScrollStepSize == %d (1/1)\n", ScrollStepSize);
696           break;
697
698         case KSYM_Q:
699         case KSYM_q:
700           local_player->dynamite = 1000;
701           break;
702
703
704
705 #if 0
706
707         case KSYM_z:
708           {
709             int i;
710
711             for(i=0; i<MAX_PLAYERS; i++)
712             {
713               printf("Player %d:\n", i);
714               printf("  jx == %d, jy == %d\n",
715                      stored_player[i].jx, stored_player[i].jy);
716               printf("  last_jx == %d, last_jy == %d\n",
717                      stored_player[i].last_jx, stored_player[i].last_jy);
718             }
719             printf("\n");
720           }
721
722           break;
723 #endif
724 #endif
725
726         default:
727           break;
728       }
729       break;
730     }
731     default:
732       break;
733   }
734 }
735
736 void HandleNoEvent()
737 {
738   if (button_status && game_status != PLAYING)
739   {
740     HandleButton(0, 0, -button_status);
741     return;
742   }
743
744 #if defined(PLATFORM_UNIX)
745   if (options.network)
746     HandleNetworking();
747 #endif
748
749   HandleJoystick();
750 }
751
752 static int HandleJoystickForAllPlayers()
753 {
754   int i;
755   int result = 0;
756
757   for (i=0; i<MAX_PLAYERS; i++)
758   {
759     byte joy_action = 0;
760
761     /*
762     if (!setup.input[i].use_joystick)
763       continue;
764       */
765
766     joy_action = Joystick(i);
767     result |= joy_action;
768
769
770     if (!setup.input[i].use_joystick)
771       continue;
772
773
774     stored_player[i].action = joy_action;
775   }
776
777   return result;
778 }
779
780 void HandleJoystick()
781 {
782   int joystick  = HandleJoystickForAllPlayers();
783   int keyboard  = key_joystick_mapping;
784   int joy       = (joystick | keyboard);
785   int left      = joy & JOY_LEFT;
786   int right     = joy & JOY_RIGHT;
787   int up        = joy & JOY_UP;
788   int down      = joy & JOY_DOWN;
789   int button    = joy & JOY_BUTTON;
790   int newbutton = (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED);
791   int dx        = (left ? -1    : right ? 1     : 0);
792   int dy        = (up   ? -1    : down  ? 1     : 0);
793
794   switch(game_status)
795   {
796     case MAINMENU:
797     case CHOOSELEVEL:
798     case SETUP:
799     case SETUPINPUT:
800     {
801       static unsigned long joystickmove_delay = 0;
802
803       if (joystick && !button &&
804           !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
805         newbutton = dx = dy = 0;
806
807       if (game_status==MAINMENU)
808         HandleMainMenu(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
809       else if (game_status==CHOOSELEVEL)
810         HandleChooseLevel(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
811       else if (game_status==SETUP)
812         HandleSetupScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
813       else if (game_status==SETUPINPUT)
814         HandleSetupInputScreen(0,0,dx,dy,
815                                newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
816       break;
817     }
818
819     case HALLOFFAME:
820       HandleHallOfFame(0,0, dx,dy, !newbutton);
821       break;
822
823     case HELPSCREEN:
824       HandleHelpScreen(!newbutton);
825       break;
826
827     case PLAYING:
828       if (tape.playing || keyboard)
829         newbutton = ((joy & JOY_BUTTON) != 0);
830
831       if (AllPlayersGone && newbutton)
832       {
833         CloseDoor(DOOR_CLOSE_1);
834         game_status = MAINMENU;
835         DrawMainMenu();
836         return;
837       }
838
839       break;
840
841     default:
842       break;
843   }
844 }