c67561320bc43da6b4552d4ebac636bc904efc0f
[rocksndiamonds.git] / src / tools.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * tools.c                                                  *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "tools.h"
17 #include "game.h"
18 #include "events.h"
19 #include "cartoons.h"
20 #include "network.h"
21 #include "tape.h"
22
23 /* tool button identifiers */
24 #define TOOL_CTRL_ID_YES        0
25 #define TOOL_CTRL_ID_NO         1
26 #define TOOL_CTRL_ID_CONFIRM    2
27 #define TOOL_CTRL_ID_PLAYER_1   3
28 #define TOOL_CTRL_ID_PLAYER_2   4
29 #define TOOL_CTRL_ID_PLAYER_3   5
30 #define TOOL_CTRL_ID_PLAYER_4   6
31
32 #define NUM_TOOL_BUTTONS        7
33
34 /* forward declaration for internal use */
35 static void UnmapToolButtons();
36 static void HandleToolButtons(struct GadgetInfo *);
37
38 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
39 static int request_gadget_id = -1;
40
41 void SetDrawtoField(int mode)
42 {
43   if (mode == DRAW_BUFFERED && setup.soft_scrolling)
44   {
45     FX = TILEX;
46     FY = TILEY;
47     BX1 = -1;
48     BY1 = -1;
49     BX2 = SCR_FIELDX;
50     BY2 = SCR_FIELDY;
51     redraw_x1 = 1;
52     redraw_y1 = 1;
53
54     drawto_field = fieldbuffer;
55   }
56   else  /* DRAW_DIRECT, DRAW_BACKBUFFER */
57   {
58     FX = SX;
59     FY = SY;
60     BX1 = 0;
61     BY1 = 0;
62     BX2 = SCR_FIELDX - 1;
63     BY2 = SCR_FIELDY - 1;
64     redraw_x1 = 0;
65     redraw_y1 = 0;
66
67     drawto_field = (mode == DRAW_DIRECT ? window :  backbuffer);
68   }
69 }
70
71 void RedrawPlayfield(boolean force_redraw, int x, int y, int width, int height)
72 {
73   if (game_status == PLAYING)
74   {
75     if (force_redraw)
76     {
77       x = gfx.sx - TILEX;
78       y = gfx.sy - TILEY;
79       width = gfx.sxsize + 2 * TILEX;
80       height = gfx.sysize + 2 * TILEY;
81     }
82
83     if (force_redraw || setup.direct_draw)
84     {
85       int xx, yy;
86       int x1 = (x - SX) / TILEX, y1 = (y - SY) / TILEY;
87       int x2 = (x - SX + width) / TILEX, y2 = (y - SY + height) / TILEY;
88
89       if (setup.direct_draw)
90         SetDrawtoField(DRAW_BACKBUFFER);
91
92       for(xx=BX1; xx<=BX2; xx++)
93         for(yy=BY1; yy<=BY2; yy++)
94           if (xx >= x1 && xx <= x2 && yy >= y1 && yy <= y2)
95             DrawScreenField(xx, yy);
96       DrawAllPlayers();
97
98       if (setup.direct_draw)
99         SetDrawtoField(DRAW_DIRECT);
100     }
101
102     if (setup.soft_scrolling)
103     {
104       int fx = FX, fy = FY;
105
106       fx += (ScreenMovDir & (MV_LEFT|MV_RIGHT) ? ScreenGfxPos : 0);
107       fy += (ScreenMovDir & (MV_UP|MV_DOWN)    ? ScreenGfxPos : 0);
108
109       BlitBitmap(fieldbuffer, backbuffer, fx,fy, SXSIZE,SYSIZE, SX,SY);
110     }
111   }
112
113   BlitBitmap(drawto, window, x, y, width, height, x, y);
114 }
115
116 void BackToFront()
117 {
118   int x,y;
119   DrawBuffer *buffer = (drawto_field == window ? backbuffer : drawto_field);
120
121   if (setup.direct_draw && game_status == PLAYING)
122     redraw_mask &= ~REDRAW_MAIN;
123
124   if (redraw_mask & REDRAW_TILES && redraw_tiles > REDRAWTILES_THRESHOLD)
125     redraw_mask |= REDRAW_FIELD;
126
127   if (redraw_mask & REDRAW_FIELD)
128     redraw_mask &= ~REDRAW_TILES;
129
130   if (redraw_mask == REDRAW_NONE)
131     return;
132
133   if (global.fps_slowdown && game_status == PLAYING)
134   {
135     static boolean last_frame_skipped = FALSE;
136     boolean skip_even_when_not_scrolling = TRUE;
137     boolean just_scrolling = (ScreenMovDir != 0);
138     boolean verbose = FALSE;
139
140     if (global.fps_slowdown_factor > 1 &&
141         (FrameCounter % global.fps_slowdown_factor) &&
142         (just_scrolling || skip_even_when_not_scrolling))
143     {
144       redraw_mask &= ~REDRAW_MAIN;
145
146       last_frame_skipped = TRUE;
147
148       if (verbose)
149         printf("FRAME SKIPPED\n");
150     }
151     else
152     {
153       if (last_frame_skipped)
154         redraw_mask |= REDRAW_FIELD;
155
156       last_frame_skipped = FALSE;
157
158       if (verbose)
159         printf("frame not skipped\n");
160     }
161   }
162
163   /* synchronize X11 graphics at this point; if we would synchronize the
164      display immediately after the buffer switching (after the XFlush),
165      this could mean that we have to wait for the graphics to complete,
166      although we could go on doing calculations for the next frame */
167
168   SyncDisplay();
169
170   if (redraw_mask & REDRAW_ALL)
171   {
172     BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
173     redraw_mask = 0;
174   }
175
176   if (redraw_mask & REDRAW_FIELD)
177   {
178     if (game_status != PLAYING || redraw_mask & REDRAW_FROM_BACKBUFFER)
179     {
180       BlitBitmap(backbuffer, window,
181                  REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
182     }
183     else
184     {
185       int fx = FX, fy = FY;
186
187       if (setup.soft_scrolling)
188       {
189         fx += (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
190         fy += (ScreenMovDir & (MV_UP | MV_DOWN)    ? ScreenGfxPos : 0);
191       }
192
193       if (setup.soft_scrolling ||
194           ABS(ScreenMovPos) + ScrollStepSize == TILEX ||
195           ABS(ScreenMovPos) == ScrollStepSize ||
196           redraw_tiles > REDRAWTILES_THRESHOLD)
197       {
198         BlitBitmap(buffer, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
199
200 #ifdef DEBUG
201 #if 0
202         printf("redrawing all (ScreenGfxPos == %d) because %s\n",
203                ScreenGfxPos,
204                (setup.soft_scrolling ?
205                 "setup.soft_scrolling" :
206                 ABS(ScreenGfxPos) + ScrollStepSize == TILEX ?
207                 "ABS(ScreenGfxPos) + ScrollStepSize == TILEX" :
208                 ABS(ScreenGfxPos) == ScrollStepSize ?
209                 "ABS(ScreenGfxPos) == ScrollStepSize" :
210                 "redraw_tiles > REDRAWTILES_THRESHOLD"));
211 #endif
212 #endif
213       }
214     }
215
216     redraw_mask &= ~REDRAW_MAIN;
217   }
218
219   if (redraw_mask & REDRAW_DOORS)
220   {
221     if (redraw_mask & REDRAW_DOOR_1)
222       BlitBitmap(backbuffer, window, DX, DY, DXSIZE, DYSIZE, DX, DY);
223     if (redraw_mask & REDRAW_DOOR_2)
224     {
225       if ((redraw_mask & REDRAW_DOOR_2) == REDRAW_DOOR_2)
226         BlitBitmap(backbuffer, window, VX,VY, VXSIZE,VYSIZE, VX,VY);
227       else
228       {
229         if (redraw_mask & REDRAW_VIDEO_1)
230           BlitBitmap(backbuffer, window,
231                      VX+VIDEO_DISPLAY1_XPOS,VY+VIDEO_DISPLAY1_YPOS,
232                      VIDEO_DISPLAY_XSIZE,VIDEO_DISPLAY_YSIZE,
233                      VX+VIDEO_DISPLAY1_XPOS,VY+VIDEO_DISPLAY1_YPOS);
234         if (redraw_mask & REDRAW_VIDEO_2)
235           BlitBitmap(backbuffer, window,
236                      VX+VIDEO_DISPLAY2_XPOS,VY+VIDEO_DISPLAY2_YPOS,
237                      VIDEO_DISPLAY_XSIZE,VIDEO_DISPLAY_YSIZE,
238                      VX+VIDEO_DISPLAY2_XPOS,VY+VIDEO_DISPLAY2_YPOS);
239         if (redraw_mask & REDRAW_VIDEO_3)
240           BlitBitmap(backbuffer, window,
241                      VX+VIDEO_CONTROL_XPOS,VY+VIDEO_CONTROL_YPOS,
242                      VIDEO_CONTROL_XSIZE,VIDEO_CONTROL_YSIZE,
243                      VX+VIDEO_CONTROL_XPOS,VY+VIDEO_CONTROL_YPOS);
244       }
245     }
246     if (redraw_mask & REDRAW_DOOR_3)
247       BlitBitmap(backbuffer, window, EX, EY, EXSIZE, EYSIZE, EX, EY);
248     redraw_mask &= ~REDRAW_DOORS;
249   }
250
251   if (redraw_mask & REDRAW_MICROLEVEL)
252   {
253     BlitBitmap(backbuffer, window,
254                MICROLEV_XPOS, MICROLEV_YPOS, MICROLEV_XSIZE, MICROLEV_YSIZE,
255                MICROLEV_XPOS, MICROLEV_YPOS);
256     BlitBitmap(backbuffer, window,
257                SX, MICROLABEL_YPOS, SXSIZE, FONT4_YSIZE,
258                SX, MICROLABEL_YPOS);
259     redraw_mask &= ~REDRAW_MICROLEVEL;
260   }
261
262   if (redraw_mask & REDRAW_TILES)
263   {
264     for(x=0; x<SCR_FIELDX; x++)
265       for(y=0; y<SCR_FIELDY; y++)
266         if (redraw[redraw_x1 + x][redraw_y1 + y])
267           BlitBitmap(buffer, window,
268                      FX + x * TILEX, FX + y * TILEY, TILEX, TILEY,
269                      SX + x * TILEX, SY + y * TILEY);
270   }
271
272   if (redraw_mask & REDRAW_FPS)         /* display frames per second */
273   {
274     char text[100];
275     char info1[100];
276
277     sprintf(info1, " (only every %d. frame)", global.fps_slowdown_factor);
278     if (!global.fps_slowdown)
279       info1[0] = '\0';
280
281     sprintf(text, "%.1f fps%s", global.frames_per_second, info1);
282     DrawTextExt(window, SX, SY, text, FS_SMALL, FC_YELLOW, FONT_OPAQUE);
283   }
284
285   FlushDisplay();
286
287   for(x=0; x<MAX_BUF_XSIZE; x++)
288     for(y=0; y<MAX_BUF_YSIZE; y++)
289       redraw[x][y] = 0;
290   redraw_tiles = 0;
291   redraw_mask = REDRAW_NONE;
292 }
293
294 void FadeToFront()
295 {
296 #if 0
297   long fading_delay = 300;
298
299   if (setup.fading && (redraw_mask & REDRAW_FIELD))
300   {
301 #endif
302
303 #if 0
304     int x,y;
305
306     ClearRectangle(window, REAL_SX,REAL_SY,FULL_SXSIZE,FULL_SYSIZE);
307     FlushDisplay();
308
309     for(i=0;i<2*FULL_SYSIZE;i++)
310     {
311       for(y=0;y<FULL_SYSIZE;y++)
312       {
313         BlitBitmap(backbuffer, window,
314                    REAL_SX,REAL_SY+i, FULL_SXSIZE,1, REAL_SX,REAL_SY+i);
315       }
316       FlushDisplay();
317       Delay(10);
318     }
319 #endif
320
321 #if 0
322     for(i=1;i<FULL_SYSIZE;i+=2)
323       BlitBitmap(backbuffer, window,
324                  REAL_SX,REAL_SY+i, FULL_SXSIZE,1, REAL_SX,REAL_SY+i);
325     FlushDisplay();
326     Delay(fading_delay);
327 #endif
328
329 #if 0
330     SetClipOrigin(clip_gc[PIX_FADEMASK], 0, 0);
331     BlitBitmapMasked(backbuffer, window,
332                      REAL_SX,REAL_SY, FULL_SXSIZE,FULL_SYSIZE,
333                      REAL_SX,REAL_SY);
334     FlushDisplay();
335     Delay(fading_delay);
336
337     SetClipOrigin(clip_gc[PIX_FADEMASK], -1, -1);
338     BlitBitmapMasked(backbuffer, window,
339                      REAL_SX,REAL_SY, FULL_SXSIZE,FULL_SYSIZE,
340                      REAL_SX,REAL_SY);
341     FlushDisplay();
342     Delay(fading_delay);
343
344     SetClipOrigin(clip_gc[PIX_FADEMASK], 0, -1);
345     BlitBitmapMasked(backbuffer, window,
346                      REAL_SX,REAL_SY, FULL_SXSIZE,FULL_SYSIZE,
347                      REAL_SX,REAL_SY);
348     FlushDisplay();
349     Delay(fading_delay);
350
351     SetClipOrigin(clip_gc[PIX_FADEMASK], -1, 0);
352     BlitBitmapMasked(backbuffer, window,
353                      REAL_SX,REAL_SY, FULL_SXSIZE,FULL_SYSIZE,
354                      REAL_SX,REAL_SY);
355     FlushDisplay();
356     Delay(fading_delay);
357
358     redraw_mask &= ~REDRAW_MAIN;
359   }
360 #endif
361
362   BackToFront();
363 }
364
365 void SetMainBackgroundImage(int graphic)
366 {
367   SetMainBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
368                           graphic_info[graphic].bitmap ?
369                           graphic_info[graphic].bitmap :
370                           graphic_info[IMG_BACKGROUND_DEFAULT].bitmap);
371 }
372
373 void SetDoorBackgroundImage(int graphic)
374 {
375   SetDoorBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
376                           graphic_info[graphic].bitmap ?
377                           graphic_info[graphic].bitmap :
378                           graphic_info[IMG_BACKGROUND_DEFAULT].bitmap);
379 }
380
381 void DrawBackground(int dest_x, int dest_y, int width, int height)
382 {
383   ClearRectangleOnBackground(backbuffer, dest_x, dest_y, width, height);
384
385   redraw_mask |= REDRAW_FIELD;
386 }
387
388 void ClearWindow()
389 {
390   DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
391
392   if (setup.soft_scrolling && game_status == PLAYING)
393   {
394     ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
395     SetDrawtoField(DRAW_BUFFERED);
396   }
397   else
398     SetDrawtoField(DRAW_BACKBUFFER);
399
400   if (setup.direct_draw && game_status == PLAYING)
401   {
402     ClearRectangle(window, REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
403     SetDrawtoField(DRAW_DIRECT);
404   }
405 }
406
407 void MarkTileDirty(int x, int y)
408 {
409   int xx = redraw_x1 + x;
410   int yy = redraw_y1 + y;
411
412   if (!redraw[xx][yy])
413     redraw_tiles++;
414
415   redraw[xx][yy] = TRUE;
416   redraw_mask |= REDRAW_TILES;
417 }
418
419 void SetBorderElement()
420 {
421   int x, y;
422
423   BorderElement = EL_EMPTY;
424
425   for(y=0; y<lev_fieldy && BorderElement == EL_EMPTY; y++)
426   {
427     for(x=0; x<lev_fieldx; x++)
428     {
429       if (!IS_MASSIVE(Feld[x][y]))
430         BorderElement = EL_STEELWALL;
431
432       if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
433         x = lev_fieldx - 2;
434     }
435   }
436 }
437
438 static int getGraphicAnimationPhase(int frames, int delay, int mode)
439 {
440   int phase;
441
442   if (mode & ANIM_PINGPONG)
443   {
444     int max_anim_frames = 2 * frames - 2;
445
446     phase = (FrameCounter % (delay * max_anim_frames)) / delay;
447     phase = (phase < frames ? phase : max_anim_frames - phase);
448   }
449   else
450     phase = (FrameCounter % (delay * frames)) / delay;
451
452   if (mode & ANIM_REVERSE)
453     phase = -phase;
454
455   return phase;
456 }
457
458 inline int getGraphicAnimationFrame(int graphic, int sync_frame)
459 {
460   /* animation synchronized with global frame counter, not move position */
461   if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
462     sync_frame = FrameCounter;
463
464   return getAnimationFrame(graphic_info[graphic].anim_frames,
465                            graphic_info[graphic].anim_delay,
466                            graphic_info[graphic].anim_mode,
467                            graphic_info[graphic].anim_start_frame,
468                            sync_frame);
469 }
470
471 inline void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
472                                     int graphic, int sync_frame, int mask_mode)
473 {
474   int frame = getGraphicAnimationFrame(graphic, sync_frame);
475
476   if (mask_mode == USE_MASKING)
477     DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
478   else
479     DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
480 }
481
482 inline boolean checkDrawGraphicAnimation(int x, int y, int graphic)
483 {
484   int lx = LEVELX(x), ly = LEVELY(y);
485
486   return (IN_SCR_FIELD(x, y) &&
487           GfxFrame[lx][ly] % graphic_info[graphic].anim_delay == 0);
488 }
489
490 inline boolean checkDrawLevelGraphicAnimation(int x, int y, int graphic)
491 {
492   return (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
493           GfxFrame[x][y] % graphic_info[graphic].anim_delay == 0);
494 }
495
496 inline boolean DrawGraphicAnimation(int x, int y, int graphic)
497 {
498   int lx = LEVELX(x), ly = LEVELY(y);
499
500   if (!checkDrawGraphicAnimation(x, y, graphic))
501     return FALSE;
502
503   DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
504                           graphic, GfxFrame[lx][ly], NO_MASKING);
505   MarkTileDirty(x, y);
506
507   return TRUE;
508 }
509
510 boolean DrawLevelGraphicAnimation(int x, int y, int graphic)
511 {
512   return DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
513 }
514
515 boolean DrawLevelElementAnimation(int x, int y, int element)
516 {
517   return DrawGraphicAnimation(SCREENX(x), SCREENY(y), el2img(element));
518 }
519
520 void DrawAllPlayers()
521 {
522   int i;
523
524   for(i=0; i<MAX_PLAYERS; i++)
525     if (stored_player[i].active)
526       DrawPlayer(&stored_player[i]);
527 }
528
529 void DrawPlayerField(int x, int y)
530 {
531   if (!IS_PLAYER(x, y))
532     return;
533
534   DrawPlayer(PLAYERINFO(x, y));
535 }
536
537 void DrawPlayer(struct PlayerInfo *player)
538 {
539   int jx = player->jx, jy = player->jy;
540   int last_jx = player->last_jx, last_jy = player->last_jy;
541   int next_jx = jx + (jx - last_jx), next_jy = jy + (jy - last_jy);
542   int sx = SCREENX(jx), sy = SCREENY(jy);
543   int sxx = 0, syy = 0;
544   int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
545   int graphic;
546   int frame = 0;
547   boolean player_is_moving = (last_jx != jx || last_jy != jy ? TRUE : FALSE);
548
549   if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
550     return;
551
552 #if DEBUG
553   if (!IN_LEV_FIELD(jx,jy))
554   {
555     printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
556     printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
557     printf("DrawPlayerField(): This should never happen!\n");
558     return;
559   }
560 #endif
561
562   if (element == EL_EXPLOSION)
563     return;
564
565   /* draw things in the field the player is leaving, if needed */
566
567   if (player_is_moving)
568   {
569     if (Store[last_jx][last_jy] && IS_DRAWABLE(last_element))
570     {
571       DrawLevelElement(last_jx, last_jy, Store[last_jx][last_jy]);
572
573       if (last_element == EL_DYNAMITE_ACTIVE)
574         DrawDynamite(last_jx, last_jy);
575       else
576         DrawLevelFieldThruMask(last_jx, last_jy);
577     }
578     else if (last_element == EL_DYNAMITE_ACTIVE)
579       DrawDynamite(last_jx, last_jy);
580     else
581       DrawLevelField(last_jx, last_jy);
582
583     if (player->Pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
584     {
585       if (player->GfxPos)
586       {
587         if (Feld[next_jx][next_jy] == EL_SOKOBAN_FIELD_FULL)
588           DrawLevelElement(next_jx, next_jy, EL_SOKOBAN_FIELD_EMPTY);
589         else
590           DrawLevelElement(next_jx, next_jy, EL_EMPTY);
591       }
592       else
593         DrawLevelField(next_jx, next_jy);
594     }
595   }
596
597   if (!IN_SCR_FIELD(sx, sy))
598     return;
599
600   if (setup.direct_draw)
601     SetDrawtoField(DRAW_BUFFERED);
602
603   /* draw things behind the player, if needed */
604
605   if (Store[jx][jy])
606     DrawLevelElement(jx, jy, Store[jx][jy]);
607   else if (!IS_ACTIVE_BOMB(element))
608     DrawLevelField(jx, jy);
609   else
610     DrawLevelElement(jx, jy, EL_EMPTY);
611
612   /* draw player himself */
613
614   if (game.emulation == EMU_SUPAPLEX)
615   {
616     static int last_dir = MV_LEFT;
617     int action = (player->programmed_action ? player->programmed_action :
618                   player->action);
619     boolean action_moving =
620       (player_is_moving ||
621        ((action & (MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN)) &&
622         !(action & ~(MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN))));
623
624     graphic = IMG_SP_MURPHY;
625
626     if (player->Pushing)
627     {
628       if (player->MovDir == MV_LEFT)
629         graphic = IMG_SP_MURPHY_PUSHING_LEFT;
630       else if (player->MovDir == MV_RIGHT)
631         graphic = IMG_SP_MURPHY_PUSHING_RIGHT;
632       else if (player->MovDir & (MV_UP | MV_DOWN) && last_dir == MV_LEFT)
633         graphic = IMG_SP_MURPHY_PUSHING_LEFT;
634       else if (player->MovDir & (MV_UP | MV_DOWN) && last_dir == MV_RIGHT)
635         graphic = IMG_SP_MURPHY_PUSHING_RIGHT;
636     }
637     else if (player->snapped)
638     {
639       if (player->MovDir == MV_LEFT)
640         graphic = IMG_SP_MURPHY_SNAPPING_LEFT;
641       else if (player->MovDir == MV_RIGHT)
642         graphic = IMG_SP_MURPHY_SNAPPING_RIGHT;
643       else if (player->MovDir == MV_UP)
644         graphic = IMG_SP_MURPHY_SNAPPING_UP;
645       else if (player->MovDir == MV_DOWN)
646         graphic = IMG_SP_MURPHY_SNAPPING_DOWN;
647     }
648     else if (action_moving)
649     {
650       if (player->MovDir == MV_LEFT)
651         graphic = IMG_SP_MURPHY_MOVING_LEFT;
652       else if (player->MovDir == MV_RIGHT)
653         graphic = IMG_SP_MURPHY_MOVING_RIGHT;
654       else if (player->MovDir & (MV_UP | MV_DOWN) && last_dir == MV_LEFT)
655         graphic = IMG_SP_MURPHY_MOVING_LEFT;
656       else if (player->MovDir & (MV_UP | MV_DOWN) && last_dir == MV_RIGHT)
657         graphic = IMG_SP_MURPHY_MOVING_RIGHT;
658       else
659         graphic = IMG_SP_MURPHY_MOVING_LEFT;
660
661       frame = getGraphicAnimationFrame(graphic, -1);
662     }
663
664     if (player->MovDir == MV_LEFT || player->MovDir == MV_RIGHT)
665       last_dir = player->MovDir;
666   }
667   else
668   {
669     if (player->MovDir == MV_LEFT)
670       graphic = (player->Pushing ? IMG_PLAYER1_PUSHING_LEFT :
671                  player->is_moving ? IMG_PLAYER1_MOVING_LEFT :
672                  IMG_PLAYER1_LEFT);
673     else if (player->MovDir == MV_RIGHT)
674       graphic = (player->Pushing ? IMG_PLAYER1_PUSHING_RIGHT :
675                  player->is_moving ? IMG_PLAYER1_MOVING_RIGHT :
676                  IMG_PLAYER1_RIGHT);
677     else if (player->MovDir == MV_UP)
678       graphic = (player->Pushing ? IMG_PLAYER1_PUSHING_UP :
679                  player->is_moving ? IMG_PLAYER1_MOVING_UP :
680                  IMG_PLAYER1_UP);
681     else        /* MV_DOWN || MV_NO_MOVING */
682       graphic = (player->Pushing ? IMG_PLAYER1_PUSHING_DOWN :
683                  player->is_moving ? IMG_PLAYER1_MOVING_DOWN :
684                  IMG_PLAYER1_DOWN);
685
686     graphic = PLAYER_NR_GFX(graphic, player->index_nr);
687
688 #if 0
689     frame = player->Frame;
690 #else
691     frame = getGraphicAnimationFrame(graphic, player->Frame);
692 #endif
693   }
694
695   if (player->GfxPos)
696   {
697     if (player->MovDir == MV_LEFT || player->MovDir == MV_RIGHT)
698       sxx = player->GfxPos;
699     else
700       syy = player->GfxPos;
701   }
702
703   if (!setup.soft_scrolling && ScreenMovPos)
704     sxx = syy = 0;
705
706 #if 0
707   if (player->Frame)
708     printf("-> %d\n", player->Frame);
709 #endif
710
711   DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
712
713   if (SHIELD_ON(player))
714   {
715     int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
716                    IMG_SHIELD_NORMAL_ACTIVE);
717     int frame = getGraphicAnimationFrame(graphic, -1);
718
719     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
720   }
721
722 #if 0
723   if (player->Pushing && player->GfxPos)
724 #else
725   if (player->Pushing && player_is_moving)
726 #endif
727   {
728     int px = SCREENX(next_jx), py = SCREENY(next_jy);
729
730     if ((sxx || syy) &&
731         (element == EL_SOKOBAN_FIELD_EMPTY ||
732          Feld[next_jx][next_jy] == EL_SOKOBAN_FIELD_FULL))
733       DrawGraphicShiftedThruMask(px, py, sxx, syy, IMG_SOKOBAN_OBJECT, 0,
734                                  NO_CUTTING);
735     else
736     {
737       int element = Feld[next_jx][next_jy];
738       int graphic = el2img(element);
739 #if 1
740       int frame = 0;
741 #endif
742
743       if ((sxx || syy) && IS_PUSHABLE(element))
744       {
745         graphic = el_dir_act2img(element, player->MovDir, ACTION_MOVING);
746 #if 1
747         frame = getGraphicAnimationFrame(graphic, player->GfxPos);
748
749         frame = getGraphicAnimationFrame(graphic, player->Frame);
750 #endif
751
752 #if 0
753         printf("-> %d [%d]\n", player->Frame, player->GfxPos);
754 #endif
755
756 #if 0
757         /* !!! FIX !!! */
758         if (player->MovDir == MV_LEFT)
759           frame = 3 - frame;
760 #endif
761
762 #if 0
763         frame = (player->GfxPos / (TILEX / 4));
764
765         if (player->MovDir == MV_RIGHT)
766           frame = (frame + 4) % 4;
767 #endif
768       }
769
770       DrawGraphicShifted(px, py, sxx, syy, graphic, frame,
771                          NO_CUTTING, NO_MASKING);
772     }
773   }
774
775   /* draw things in front of player (active dynamite or dynabombs) */
776
777   if (IS_ACTIVE_BOMB(element))
778   {
779     graphic = el2img(element);
780
781 #if 0
782     if (element == EL_DYNAMITE_ACTIVE)
783     {
784       if ((frame = (96 - MovDelay[jx][jy]) / 12) > 6)
785         frame = 6;
786     }
787     else
788     {
789       if ((frame = ((96 - MovDelay[jx][jy]) / 6) % 8) > 3)
790         frame = 7 - frame;
791     }
792 #else
793
794 #if 0
795     frame = getGraphicAnimationFrame(graphic, 96 - MovDelay[jx][jy]);
796 #else
797     frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
798 #endif
799
800 #endif
801
802     if (game.emulation == EMU_SUPAPLEX)
803       DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
804     else
805       DrawGraphicThruMask(sx, sy, graphic, frame);
806   }
807
808   if (player_is_moving && last_element == EL_EXPLOSION)
809   {
810     int stored = Store[last_jx][last_jy];
811     int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
812                    stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
813                    IMG_SP_EXPLOSION);
814     int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
815     int phase = ExplodePhase[last_jx][last_jy] - 1;
816     int frame = getGraphicAnimationFrame(graphic, phase - delay);
817
818     if (phase >= delay)
819       DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
820   }
821
822   /* draw elements that stay over the player */
823   /* handle the field the player is leaving ... */
824   if (player_is_moving && IS_OVER_PLAYER(last_element))
825     DrawLevelField(last_jx, last_jy);
826
827   /* ... and the field the player is entering */
828   if (IS_OVER_PLAYER(element))
829     DrawLevelField(jx, jy);
830
831   if (setup.direct_draw)
832   {
833     int dest_x = SX + SCREENX(MIN(jx, last_jx)) * TILEX;
834     int dest_y = SY + SCREENY(MIN(jy, last_jy)) * TILEY;
835     int x_size = TILEX * (1 + ABS(jx - last_jx));
836     int y_size = TILEY * (1 + ABS(jy - last_jy));
837
838     BlitBitmap(drawto_field, window,
839                dest_x, dest_y, x_size, y_size, dest_x, dest_y);
840     SetDrawtoField(DRAW_DIRECT);
841   }
842
843   MarkTileDirty(sx,sy);
844 }
845
846 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
847 {
848   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
849   int offset_x = graphic_info[graphic].offset_x;
850   int offset_y = graphic_info[graphic].offset_y;
851   int src_x = graphic_info[graphic].src_x + frame * offset_x;
852   int src_y = graphic_info[graphic].src_y + frame * offset_y;
853
854   *bitmap = src_bitmap;
855   *x = src_x;
856   *y = src_y;
857 }
858
859 void DrawGraphic(int x, int y, int graphic, int frame)
860 {
861 #if DEBUG
862   if (!IN_SCR_FIELD(x, y))
863   {
864     printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
865     printf("DrawGraphic(): This should never happen!\n");
866     return;
867   }
868 #endif
869
870   DrawGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic, frame);
871   MarkTileDirty(x, y);
872 }
873
874 #if 0
875 void DrawOldGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic)
876 {
877   Bitmap *src_bitmap;
878   int src_x, src_y;
879
880   getOldGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
881   BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
882 }
883 #endif
884
885 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
886                     int frame)
887 {
888 #if 1
889   Bitmap *src_bitmap;
890   int src_x, src_y;
891
892   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
893 #else
894   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
895   int src_x = graphic_info[graphic].src_x;
896   int src_y = graphic_info[graphic].src_y;
897   int offset_x = graphic_info[graphic].offset_x;
898   int offset_y = graphic_info[graphic].offset_y;
899
900   src_x += frame * offset_x;
901   src_y += frame * offset_y;
902 #endif
903
904   BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
905 }
906
907 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
908 {
909 #if DEBUG
910   if (!IN_SCR_FIELD(x, y))
911   {
912     printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
913     printf("DrawGraphicThruMask(): This should never happen!\n");
914     return;
915   }
916 #endif
917
918   DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y *TILEY, graphic,
919                          frame);
920   MarkTileDirty(x, y);
921 }
922
923 void DrawGraphicThruMaskExt(DrawBuffer *d, int dest_x, int dest_y, int graphic,
924                             int frame)
925 {
926 #if 1
927   Bitmap *src_bitmap;
928   int src_x, src_y;
929   GC drawing_gc;
930
931   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
932   drawing_gc = src_bitmap->stored_clip_gc;
933 #else
934   GC drawing_gc = src_bitmap->stored_clip_gc;
935   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
936   int src_x = graphic_info[graphic].src_x;
937   int src_y = graphic_info[graphic].src_y;
938   int offset_x = graphic_info[graphic].offset_x;
939   int offset_y = graphic_info[graphic].offset_y;
940
941   src_x += frame * offset_x;
942   src_y += frame * offset_y;
943
944 #endif
945
946   SetClipOrigin(src_bitmap, drawing_gc, dest_x - src_x, dest_y - src_y);
947   BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY, dest_x, dest_y);
948 }
949
950 void DrawMiniGraphic(int x, int y, int graphic)
951 {
952   DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
953   MarkTileDirty(x / 2, y / 2);
954 }
955
956 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
957 {
958   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
959   int mini_startx = 0;
960   int mini_starty = src_bitmap->height * 2 / 3;
961   int src_x = mini_startx + graphic_info[graphic].src_x / 2;
962   int src_y = mini_starty + graphic_info[graphic].src_y / 2;
963
964   if (src_x + MINI_TILEX > src_bitmap->width ||
965       src_y + MINI_TILEY > src_bitmap->height)
966   {
967     /* graphic of desired size seems not to be contained in this image;
968        dirty workaround: get it from the middle of the normal sized image */
969
970     getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
971     src_x += (TILEX / 2 - MINI_TILEX / 2);
972     src_y += (TILEY / 2 - MINI_TILEY / 2);
973   }
974
975   *bitmap = src_bitmap;
976   *x = src_x;
977   *y = src_y;
978 }
979
980 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
981 {
982   Bitmap *src_bitmap;
983   int src_x, src_y;
984
985   getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
986   BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
987 }
988
989 void DrawGraphicShifted(int x,int y, int dx,int dy, int graphic, int frame,
990                         int cut_mode, int mask_mode)
991 {
992   Bitmap *src_bitmap;
993   GC drawing_gc;
994   int src_x;
995   int src_y;
996   int offset_x;
997   int offset_y;
998
999   int width = TILEX, height = TILEY;
1000   int cx = 0, cy = 0;
1001   int dest_x, dest_y;
1002
1003   if (graphic < 0)
1004   {
1005     DrawGraphic(x, y, graphic, frame);
1006     return;
1007   }
1008
1009   if (dx || dy)                 /* shifted graphic */
1010   {
1011     if (x < BX1)                /* object enters playfield from the left */
1012     {
1013       x = BX1;
1014       width = dx;
1015       cx = TILEX - dx;
1016       dx = 0;
1017     }
1018     else if (x > BX2)           /* object enters playfield from the right */
1019     {
1020       x = BX2;
1021       width = -dx;
1022       dx = TILEX + dx;
1023     }
1024     else if (x==BX1 && dx < 0)  /* object leaves playfield to the left */
1025     {
1026       width += dx;
1027       cx = -dx;
1028       dx = 0;
1029     }
1030     else if (x==BX2 && dx > 0)  /* object leaves playfield to the right */
1031       width -= dx;
1032     else if (dx)                /* general horizontal movement */
1033       MarkTileDirty(x + SIGN(dx), y);
1034
1035     if (y < BY1)                /* object enters playfield from the top */
1036     {
1037       if (cut_mode==CUT_BELOW)  /* object completely above top border */
1038         return;
1039
1040       y = BY1;
1041       height = dy;
1042       cy = TILEY - dy;
1043       dy = 0;
1044     }
1045     else if (y > BY2)           /* object enters playfield from the bottom */
1046     {
1047       y = BY2;
1048       height = -dy;
1049       dy = TILEY + dy;
1050     }
1051     else if (y==BY1 && dy < 0)  /* object leaves playfield to the top */
1052     {
1053       height += dy;
1054       cy = -dy;
1055       dy = 0;
1056     }
1057     else if (dy > 0 && cut_mode == CUT_ABOVE)
1058     {
1059       if (y == BY2)             /* object completely above bottom border */
1060         return;
1061
1062       height = dy;
1063       cy = TILEY - dy;
1064       dy = TILEY;
1065       MarkTileDirty(x, y + 1);
1066     }                           /* object leaves playfield to the bottom */
1067     else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1068       height -= dy;
1069     else if (dy)                /* general vertical movement */
1070       MarkTileDirty(x, y + SIGN(dy));
1071   }
1072
1073   src_bitmap = graphic_info[graphic].bitmap;
1074   src_x = graphic_info[graphic].src_x;
1075   src_y = graphic_info[graphic].src_y;
1076   offset_x = graphic_info[graphic].offset_x;
1077   offset_y = graphic_info[graphic].offset_y;
1078
1079   drawing_gc = src_bitmap->stored_clip_gc;
1080
1081   src_x += frame * offset_x;
1082   src_y += frame * offset_y;
1083
1084   src_x += cx;
1085   src_y += cy;
1086
1087   dest_x = FX + x * TILEX + dx;
1088   dest_y = FY + y * TILEY + dy;
1089
1090 #if DEBUG
1091   if (!IN_SCR_FIELD(x,y))
1092   {
1093     printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1094     printf("DrawGraphicShifted(): This should never happen!\n");
1095     return;
1096   }
1097 #endif
1098
1099   if (mask_mode == USE_MASKING)
1100   {
1101     SetClipOrigin(src_bitmap, drawing_gc, dest_x - src_x, dest_y - src_y);
1102     BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1103                      dest_x, dest_y);
1104   }
1105   else
1106     BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1107                dest_x, dest_y);
1108
1109   MarkTileDirty(x,y);
1110 }
1111
1112 void DrawGraphicShiftedThruMask(int x,int y, int dx,int dy, int graphic,
1113                                 int frame, int cut_mode)
1114 {
1115   DrawGraphicShifted(x,y, dx,dy, graphic, frame, cut_mode, USE_MASKING);
1116 }
1117
1118 inline static int getFramePosition(int x, int y)
1119 {
1120   int frame_pos = -1;           /* default: global synchronization */
1121 #if 0
1122   int element = Feld[x][y];
1123
1124   if (element == EL_QUICKSAND_FULL ||
1125       element == EL_MAGIC_WALL_FULL ||
1126       element == EL_BD_MAGIC_WALL_FULL)
1127     frame_pos = -1;
1128   else if (IS_MOVING(x, y) || CAN_MOVE(element) || CAN_FALL(element))
1129     frame_pos = ABS(MovPos[x][y]) / (TILEX / 8);
1130 #else
1131
1132   frame_pos = ABS(MovPos[x][y]) / (TILEX / 8);
1133
1134   frame_pos = GfxFrame[x][y];
1135
1136 #endif
1137
1138   return frame_pos;
1139 }
1140
1141 inline static int getGfxAction(int x, int y)
1142 {
1143   int gfx_action = ACTION_DEFAULT;
1144
1145 #if 0
1146   if (GfxAction[x][y] != ACTION_DEFAULT)
1147     gfx_action = GfxAction[x][y];
1148   else if (IS_MOVING(x, y))
1149     gfx_action = ACTION_MOVING;
1150 #else
1151   gfx_action = GfxAction[x][y];
1152 #endif
1153
1154 #if DEBUG
1155   if (gfx_action < 0)
1156     printf("getGfxAction: THIS SHOULD NEVER HAPPEN: GfxAction[%d][%d] == %d\n",
1157            x, y, gfx_action);
1158 #endif
1159
1160   return gfx_action;
1161 }
1162
1163 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1164                           int cut_mode, int mask_mode)
1165 {
1166   int ux = LEVELX(x), uy = LEVELY(y);
1167   int graphic;
1168   int frame;
1169
1170   if (IN_LEV_FIELD(ux, uy))
1171   {
1172     int move_dir = MovDir[ux][uy];
1173     int move_pos = getFramePosition(ux, uy);
1174     int gfx_action = getGfxAction(ux, uy);
1175
1176     graphic = el_dir_act2img(element, move_dir, gfx_action);
1177     frame = getGraphicAnimationFrame(graphic, move_pos);
1178   }
1179   else
1180   {
1181     graphic = el2img(element);
1182     frame = getGraphicAnimationFrame(graphic, 0);
1183   }
1184
1185   if (element == EL_WALL_GROWING)
1186   {
1187     boolean left_stopped = FALSE, right_stopped = FALSE;
1188
1189     if (!IN_LEV_FIELD(ux - 1, uy) || IS_MAUER(Feld[ux - 1][uy]))
1190       left_stopped = TRUE;
1191     if (!IN_LEV_FIELD(ux + 1, uy) || IS_MAUER(Feld[ux + 1][uy]))
1192       right_stopped = TRUE;
1193
1194     if (left_stopped && right_stopped)
1195       graphic = IMG_WALL;
1196     else if (left_stopped)
1197     {
1198       graphic = IMG_WALL_GROWING_ACTIVE_RIGHT;
1199       frame = graphic_info[graphic].anim_frames - 1;
1200     }
1201     else if (right_stopped)
1202     {
1203       graphic = IMG_WALL_GROWING_ACTIVE_LEFT;
1204       frame = graphic_info[graphic].anim_frames - 1;
1205     }
1206   }
1207   else if (IS_AMOEBOID(element) || element == EL_AMOEBA_DRIPPING)
1208   {
1209     graphic = (element == EL_BD_AMOEBA ? IMG_BD_AMOEBA_PART1 :
1210                element == EL_AMOEBA_WET ? IMG_AMOEBA_WET_PART1 :
1211                element == EL_AMOEBA_DRY ? IMG_AMOEBA_DRY_PART1 :
1212                element == EL_AMOEBA_FULL ? IMG_AMOEBA_FULL_PART1 :
1213                IMG_AMOEBA_DEAD_PART1);
1214
1215     graphic += (x + 2 * y + 4) % 4;
1216   }
1217
1218   if (dx || dy)
1219     DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1220   else if (mask_mode == USE_MASKING)
1221     DrawGraphicThruMask(x, y, graphic, frame);
1222   else
1223     DrawGraphic(x, y, graphic, frame);
1224 }
1225
1226 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1227                          int cut_mode, int mask_mode)
1228 {
1229   if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1230     DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1231                          cut_mode, mask_mode);
1232 }
1233
1234 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1235                               int cut_mode)
1236 {
1237   DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1238 }
1239
1240 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1241                              int cut_mode)
1242 {
1243   DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1244 }
1245
1246 #if 0
1247 void DrawOldScreenElementThruMask(int x, int y, int element)
1248 {
1249   DrawOldScreenElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1250 }
1251
1252 void DrawScreenElementThruMask(int x, int y, int element)
1253 {
1254   DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1255 }
1256 #endif
1257
1258 void DrawLevelElementThruMask(int x, int y, int element)
1259 {
1260   DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1261 }
1262
1263 void DrawLevelFieldThruMask(int x, int y)
1264 {
1265   DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1266 }
1267
1268 void DrawCrumbledSand(int x, int y)
1269 {
1270   Bitmap *src_bitmap;
1271   int src_x, src_y;
1272   int i, width, height, cx,cy;
1273   int ux = LEVELX(x), uy = LEVELY(y);
1274   int element, graphic;
1275   int snip = 4;
1276   static int xy[4][2] =
1277   {
1278     { 0, -1 },
1279     { -1, 0 },
1280     { +1, 0 },
1281     { 0, +1 }
1282   };
1283
1284   if (!IN_LEV_FIELD(ux, uy))
1285     return;
1286
1287   element = Feld[ux][uy];
1288
1289   if (element == EL_SAND ||
1290       element == EL_LANDMINE ||
1291       element == EL_TRAP ||
1292       element == EL_TRAP_ACTIVE)
1293   {
1294     if (!IN_SCR_FIELD(x, y))
1295       return;
1296
1297     graphic = IMG_SAND_CRUMBLED;
1298
1299     src_bitmap = graphic_info[graphic].bitmap;
1300     src_x = graphic_info[graphic].src_x;
1301     src_y = graphic_info[graphic].src_y;
1302
1303     for(i=0; i<4; i++)
1304     {
1305       int uxx, uyy;
1306
1307       uxx = ux + xy[i][0];
1308       uyy = uy + xy[i][1];
1309       if (!IN_LEV_FIELD(uxx, uyy))
1310         element = EL_STEELWALL;
1311       else
1312         element = Feld[uxx][uyy];
1313
1314       if (element == EL_SAND ||
1315           element == EL_LANDMINE ||
1316           element == EL_TRAP ||
1317           element == EL_TRAP_ACTIVE)
1318         continue;
1319
1320       if (i == 1 || i == 2)
1321       {
1322         width = snip;
1323         height = TILEY;
1324         cx = (i == 2 ? TILEX - snip : 0);
1325         cy = 0;
1326       }
1327       else
1328       {
1329         width = TILEX;
1330         height = snip;
1331         cx = 0;
1332         cy = (i == 3 ? TILEY - snip : 0);
1333       }
1334
1335       BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1336                  width, height, FX + x * TILEX + cx, FY + y * TILEY + cy);
1337     }
1338
1339     MarkTileDirty(x, y);
1340   }
1341   else
1342   {
1343     graphic = IMG_SAND_CRUMBLED;
1344
1345     src_bitmap = graphic_info[graphic].bitmap;
1346     src_x = graphic_info[graphic].src_x;
1347     src_y = graphic_info[graphic].src_y;
1348
1349     for(i=0; i<4; i++)
1350     {
1351       int xx, yy, uxx, uyy;
1352
1353       xx = x + xy[i][0];
1354       yy = y + xy[i][1];
1355       uxx = ux + xy[i][0];
1356       uyy = uy + xy[i][1];
1357
1358       if (!IN_LEV_FIELD(uxx, uyy) ||
1359           (Feld[uxx][uyy] != EL_SAND &&
1360            Feld[uxx][uyy] != EL_LANDMINE &&
1361            Feld[uxx][uyy] != EL_TRAP &&
1362            Feld[uxx][uyy] != EL_TRAP_ACTIVE) ||
1363           !IN_SCR_FIELD(xx, yy))
1364         continue;
1365
1366       if (i == 1 || i == 2)
1367       {
1368         width = snip;
1369         height = TILEY;
1370         cx = (i == 1 ? TILEX - snip : 0);
1371         cy = 0;
1372       }
1373       else
1374       {
1375         width = TILEX;
1376         height = snip;
1377         cx = 0;
1378         cy = (i==0 ? TILEY-snip : 0);
1379       }
1380
1381       BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1382                  width, height, FX + xx * TILEX + cx, FY + yy * TILEY + cy);
1383
1384       MarkTileDirty(xx, yy);
1385     }
1386   }
1387 }
1388
1389 void DrawScreenElement(int x, int y, int element)
1390 {
1391   DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
1392   DrawCrumbledSand(x, y);
1393 }
1394
1395 void DrawLevelElement(int x, int y, int element)
1396 {
1397   if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1398     DrawScreenElement(SCREENX(x), SCREENY(y), element);
1399 }
1400
1401 void DrawScreenField(int x, int y)
1402 {
1403   int ux = LEVELX(x), uy = LEVELY(y);
1404   int element, content;
1405
1406   if (!IN_LEV_FIELD(ux, uy))
1407   {
1408     if (ux < -1 || ux > lev_fieldx || uy < -1 || uy > lev_fieldy)
1409       element = EL_EMPTY;
1410     else
1411       element = BorderElement;
1412
1413     DrawScreenElement(x, y, element);
1414     return;
1415   }
1416
1417   element = Feld[ux][uy];
1418   content = Store[ux][uy];
1419
1420   if (IS_MOVING(ux, uy))
1421   {
1422     int horiz_move = (MovDir[ux][uy] == MV_LEFT || MovDir[ux][uy] == MV_RIGHT);
1423     boolean cut_mode = NO_CUTTING;
1424
1425     if (element == EL_QUICKSAND_EMPTYING ||
1426         element == EL_MAGIC_WALL_EMPTYING ||
1427         element == EL_BD_MAGIC_WALL_EMPTYING ||
1428         element == EL_AMOEBA_DRIPPING)
1429       cut_mode = CUT_ABOVE;
1430     else if (element == EL_QUICKSAND_FILLING ||
1431              element == EL_MAGIC_WALL_FILLING ||
1432              element == EL_BD_MAGIC_WALL_FILLING)
1433       cut_mode = CUT_BELOW;
1434
1435     if (cut_mode == CUT_ABOVE)
1436       DrawScreenElementShifted(x, y, 0, 0, element, NO_CUTTING);
1437     else
1438       DrawScreenElement(x, y, EL_EMPTY);
1439
1440     if (horiz_move)
1441       DrawScreenElementShifted(x, y, MovPos[ux][uy], 0, element, NO_CUTTING);
1442     else if (cut_mode == NO_CUTTING)
1443       DrawScreenElementShifted(x, y, 0, MovPos[ux][uy], element, cut_mode);
1444     else
1445       DrawScreenElementShifted(x, y, 0, MovPos[ux][uy], content, cut_mode);
1446
1447     if (content == EL_ACID)
1448       DrawLevelElementThruMask(ux, uy + 1, EL_ACID);
1449   }
1450   else if (IS_BLOCKED(ux, uy))
1451   {
1452     int oldx, oldy;
1453     int sx, sy;
1454     int horiz_move;
1455     boolean cut_mode = NO_CUTTING;
1456     int element_old, content_old;
1457
1458     Blocked2Moving(ux, uy, &oldx, &oldy);
1459     sx = SCREENX(oldx);
1460     sy = SCREENY(oldy);
1461     horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
1462                   MovDir[oldx][oldy] == MV_RIGHT);
1463
1464     element_old = Feld[oldx][oldy];
1465     content_old = Store[oldx][oldy];
1466
1467     if (element_old == EL_QUICKSAND_EMPTYING ||
1468         element_old == EL_MAGIC_WALL_EMPTYING ||
1469         element_old == EL_BD_MAGIC_WALL_EMPTYING ||
1470         element_old == EL_AMOEBA_DRIPPING)
1471       cut_mode = CUT_ABOVE;
1472
1473     DrawScreenElement(x, y, EL_EMPTY);
1474
1475     if (horiz_move)
1476       DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
1477                                NO_CUTTING);
1478     else if (cut_mode == NO_CUTTING)
1479       DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
1480                                cut_mode);
1481     else
1482       DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
1483                                cut_mode);
1484   }
1485   else if (IS_DRAWABLE(element))
1486     DrawScreenElement(x, y, element);
1487   else
1488     DrawScreenElement(x, y, EL_EMPTY);
1489 }
1490
1491 void DrawLevelField(int x, int y)
1492 {
1493   if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1494     DrawScreenField(SCREENX(x), SCREENY(y));
1495   else if (IS_MOVING(x, y))
1496   {
1497     int newx,newy;
1498
1499     Moving2Blocked(x, y, &newx, &newy);
1500     if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
1501       DrawScreenField(SCREENX(newx), SCREENY(newy));
1502   }
1503   else if (IS_BLOCKED(x, y))
1504   {
1505     int oldx, oldy;
1506
1507     Blocked2Moving(x, y, &oldx, &oldy);
1508     if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
1509       DrawScreenField(SCREENX(oldx), SCREENY(oldy));
1510   }
1511 }
1512
1513 void DrawMiniElement(int x, int y, int element)
1514 {
1515   int graphic;
1516
1517   graphic = el2img(element);
1518   DrawMiniGraphic(x, y, graphic);
1519 }
1520
1521 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
1522 {
1523   int x = sx + scroll_x, y = sy + scroll_y;
1524
1525   if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
1526     DrawMiniElement(sx, sy, EL_EMPTY);
1527   else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
1528     DrawMiniElement(sx, sy, Feld[x][y]);
1529   else
1530   {
1531     int steel_type, steel_position;
1532     int border[6][2] =
1533     {
1534       { IMG_STEELWALL_TOPLEFT,          IMG_INVISIBLE_STEELWALL_TOPLEFT     },
1535       { IMG_STEELWALL_TOPRIGHT,         IMG_INVISIBLE_STEELWALL_TOPRIGHT    },
1536       { IMG_STEELWALL_BOTTOMLEFT,       IMG_INVISIBLE_STEELWALL_BOTTOMLEFT  },
1537       { IMG_STEELWALL_BOTTOMRIGHT,      IMG_INVISIBLE_STEELWALL_BOTTOMRIGHT },
1538       { IMG_STEELWALL_VERTICAL,         IMG_INVISIBLE_STEELWALL_VERTICAL    },
1539       { IMG_STEELWALL_HORIZONTAL,       IMG_INVISIBLE_STEELWALL_HORIZONTAL  }
1540     };
1541
1542     steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
1543     steel_position = (x == -1 && y == -1                        ? 0 :
1544                       x == lev_fieldx && y == -1                ? 1 :
1545                       x == -1 && y == lev_fieldy                ? 2 :
1546                       x == lev_fieldx && y == lev_fieldy        ? 3 :
1547                       x == -1 || x == lev_fieldx                ? 4 :
1548                       y == -1 || y == lev_fieldy                ? 5 : -1);
1549
1550     if (steel_position != -1)
1551       DrawMiniGraphic(sx, sy, border[steel_position][steel_type]);
1552   }
1553 }
1554
1555 void getMicroGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1556 {
1557   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
1558   int mini_startx = src_bitmap->width * 3 / 4;
1559   int mini_starty = src_bitmap->height * 2 / 3;
1560   int src_x = mini_startx + graphic_info[graphic].src_x / 8;
1561   int src_y = mini_starty + graphic_info[graphic].src_y / 8;
1562
1563   if (src_x + MICRO_TILEX > src_bitmap->width ||
1564       src_y + MICRO_TILEY > src_bitmap->height)
1565   {
1566     /* graphic of desired size seems not to be contained in this image;
1567        dirty workaround: get it from the middle of the normal sized image */
1568
1569     getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1570     src_x += (TILEX / 2 - MICRO_TILEX / 2);
1571     src_y += (TILEY / 2 - MICRO_TILEY / 2);
1572   }
1573
1574   *bitmap = src_bitmap;
1575   *x = src_x;
1576   *y = src_y;
1577 }
1578
1579 void DrawMicroElement(int xpos, int ypos, int element)
1580 {
1581   Bitmap *src_bitmap;
1582   int src_x, src_y;
1583   int graphic = el2img(element);
1584
1585   getMicroGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1586   BlitBitmap(src_bitmap, drawto, src_x, src_y, MICRO_TILEX, MICRO_TILEY,
1587              xpos, ypos);
1588 }
1589
1590 void DrawLevel()
1591 {
1592   int x,y;
1593
1594   SetDrawBackgroundMask(REDRAW_NONE);
1595   ClearWindow();
1596
1597   for(x=BX1; x<=BX2; x++)
1598     for(y=BY1; y<=BY2; y++)
1599       DrawScreenField(x, y);
1600
1601   redraw_mask |= REDRAW_FIELD;
1602 }
1603
1604 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
1605 {
1606   int x,y;
1607
1608   for(x=0; x<size_x; x++)
1609     for(y=0; y<size_y; y++)
1610       DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
1611
1612   redraw_mask |= REDRAW_FIELD;
1613 }
1614
1615 static void DrawMicroLevelExt(int xpos, int ypos, int from_x, int from_y)
1616 {
1617   int x, y;
1618
1619   DrawBackground(xpos, ypos, MICROLEV_XSIZE, MICROLEV_YSIZE);
1620
1621   if (lev_fieldx < STD_LEV_FIELDX)
1622     xpos += (STD_LEV_FIELDX - lev_fieldx) / 2 * MICRO_TILEX;
1623   if (lev_fieldy < STD_LEV_FIELDY)
1624     ypos += (STD_LEV_FIELDY - lev_fieldy) / 2 * MICRO_TILEY;
1625
1626   xpos += MICRO_TILEX;
1627   ypos += MICRO_TILEY;
1628
1629   for(x=-1; x<=STD_LEV_FIELDX; x++)
1630   {
1631     for(y=-1; y<=STD_LEV_FIELDY; y++)
1632     {
1633       int lx = from_x + x, ly = from_y + y;
1634
1635       if (lx >= 0 && lx < lev_fieldx && ly >= 0 && ly < lev_fieldy)
1636         DrawMicroElement(xpos + x * MICRO_TILEX, ypos + y * MICRO_TILEY,
1637                          Ur[lx][ly]);
1638       else if (lx >= -1 && lx < lev_fieldx+1 && ly >= -1 && ly < lev_fieldy+1
1639                && BorderElement != EL_EMPTY)
1640         DrawMicroElement(xpos + x * MICRO_TILEX, ypos + y * MICRO_TILEY,
1641                          BorderElement);
1642     }
1643   }
1644
1645   redraw_mask |= REDRAW_MICROLEVEL;
1646 }
1647
1648 #define MICROLABEL_EMPTY                0
1649 #define MICROLABEL_LEVEL_NAME           1
1650 #define MICROLABEL_CREATED_BY           2
1651 #define MICROLABEL_LEVEL_AUTHOR         3
1652 #define MICROLABEL_IMPORTED_FROM        4
1653 #define MICROLABEL_LEVEL_IMPORT_INFO    5
1654
1655 #define MAX_MICROLABEL_SIZE             (SXSIZE / FONT4_XSIZE)
1656
1657 static void DrawMicroLevelLabelExt(int mode)
1658 {
1659   char label_text[MAX_MICROLABEL_SIZE + 1];
1660
1661   DrawBackground(SX, MICROLABEL_YPOS, SXSIZE, FONT4_YSIZE);
1662
1663   strncpy(label_text, (mode == MICROLABEL_LEVEL_NAME ? level.name :
1664                        mode == MICROLABEL_CREATED_BY ? "created by" :
1665                        mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
1666                        mode == MICROLABEL_IMPORTED_FROM ? "imported from" :
1667                        mode == MICROLABEL_LEVEL_IMPORT_INFO ?
1668                        leveldir_current->imported_from : ""),
1669           MAX_MICROLABEL_SIZE);
1670   label_text[MAX_MICROLABEL_SIZE] = '\0';
1671
1672   if (strlen(label_text) > 0)
1673   {
1674     int lxpos = SX + (SXSIZE - strlen(label_text) * FONT4_XSIZE) / 2;
1675     int lypos = MICROLABEL_YPOS;
1676
1677     DrawText(lxpos, lypos, label_text, FS_SMALL, FC_SPECIAL2);
1678   }
1679
1680   redraw_mask |= REDRAW_MICROLEVEL;
1681 }
1682
1683 void DrawMicroLevel(int xpos, int ypos, boolean restart)
1684 {
1685   static unsigned long scroll_delay = 0;
1686   static unsigned long label_delay = 0;
1687   static int from_x, from_y, scroll_direction;
1688   static int label_state, label_counter;
1689
1690   if (restart)
1691   {
1692     from_x = from_y = 0;
1693     scroll_direction = MV_RIGHT;
1694     label_state = 1;
1695     label_counter = 0;
1696
1697     DrawMicroLevelExt(xpos, ypos, from_x, from_y);
1698     DrawMicroLevelLabelExt(label_state);
1699
1700     /* initialize delay counters */
1701     DelayReached(&scroll_delay, 0);
1702     DelayReached(&label_delay, 0);
1703
1704     return;
1705   }
1706
1707   /* scroll micro level, if needed */
1708   if ((lev_fieldx > STD_LEV_FIELDX || lev_fieldy > STD_LEV_FIELDY) &&
1709       DelayReached(&scroll_delay, MICROLEVEL_SCROLL_DELAY))
1710   {
1711     switch (scroll_direction)
1712     {
1713       case MV_LEFT:
1714         if (from_x > 0)
1715           from_x--;
1716         else
1717           scroll_direction = MV_UP;
1718         break;
1719
1720       case MV_RIGHT:
1721         if (from_x < lev_fieldx - STD_LEV_FIELDX)
1722           from_x++;
1723         else
1724           scroll_direction = MV_DOWN;
1725         break;
1726
1727       case MV_UP:
1728         if (from_y > 0)
1729           from_y--;
1730         else
1731           scroll_direction = MV_RIGHT;
1732         break;
1733
1734       case MV_DOWN:
1735         if (from_y < lev_fieldy - STD_LEV_FIELDY)
1736           from_y++;
1737         else
1738           scroll_direction = MV_LEFT;
1739         break;
1740
1741       default:
1742         break;
1743     }
1744
1745     DrawMicroLevelExt(xpos, ypos, from_x, from_y);
1746   }
1747
1748   /* redraw micro level label, if needed */
1749   if (strcmp(level.name, NAMELESS_LEVEL_NAME) != 0 &&
1750       strcmp(level.author, ANONYMOUS_NAME) != 0 &&
1751       strcmp(level.author, leveldir_current->name) != 0 &&
1752       DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
1753   {
1754     int max_label_counter = 23;
1755
1756     if (leveldir_current->imported_from != NULL)
1757       max_label_counter += 14;
1758
1759     label_counter = (label_counter + 1) % max_label_counter;
1760     label_state = (label_counter >= 0 && label_counter <= 7 ?
1761                    MICROLABEL_LEVEL_NAME :
1762                    label_counter >= 9 && label_counter <= 12 ?
1763                    MICROLABEL_CREATED_BY :
1764                    label_counter >= 14 && label_counter <= 21 ?
1765                    MICROLABEL_LEVEL_AUTHOR :
1766                    label_counter >= 23 && label_counter <= 26 ?
1767                    MICROLABEL_IMPORTED_FROM :
1768                    label_counter >= 28 && label_counter <= 35 ?
1769                    MICROLABEL_LEVEL_IMPORT_INFO : MICROLABEL_EMPTY);
1770     DrawMicroLevelLabelExt(label_state);
1771   }
1772 }
1773
1774 int REQ_in_range(int x, int y)
1775 {
1776   if (y > DY+249 && y < DY+278)
1777   {
1778     if (x > DX+1 && x < DX+48)
1779       return 1;
1780     else if (x > DX+51 && x < DX+98) 
1781       return 2;
1782   }
1783   return 0;
1784 }
1785
1786 #define MAX_REQUEST_LINES               13
1787 #define MAX_REQUEST_LINE_LEN            7
1788
1789 boolean Request(char *text, unsigned int req_state)
1790 {
1791   int mx, my, ty, result = -1;
1792   unsigned int old_door_state;
1793
1794 #if defined(PLATFORM_UNIX)
1795   /* pause network game while waiting for request to answer */
1796   if (options.network &&
1797       game_status == PLAYING &&
1798       req_state & REQUEST_WAIT_FOR)
1799     SendToServer_PausePlaying();
1800 #endif
1801
1802   old_door_state = GetDoorState();
1803
1804   UnmapAllGadgets();
1805
1806   CloseDoor(DOOR_CLOSE_1);
1807
1808   /* save old door content */
1809   BlitBitmap(bitmap_db_door, bitmap_db_door,
1810              DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE,
1811              DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY1);
1812
1813   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
1814
1815   /* clear door drawing field */
1816   DrawBackground(DX, DY, DXSIZE, DYSIZE);
1817
1818   /* write text for request */
1819   for(ty=0; ty < MAX_REQUEST_LINES; ty++)
1820   {
1821     char text_line[MAX_REQUEST_LINE_LEN + 1];
1822     int tx, tl, tc;
1823
1824     if (!*text)
1825       break;
1826
1827     for(tl=0,tx=0; tx < MAX_REQUEST_LINE_LEN; tl++,tx++)
1828     {
1829       tc = *(text + tx);
1830       if (!tc || tc == ' ')
1831         break;
1832     }
1833
1834     if (!tl)
1835     { 
1836       text++; 
1837       ty--; 
1838       continue; 
1839     }
1840
1841     strncpy(text_line, text, tl);
1842     text_line[tl] = 0;
1843
1844     DrawText(DX + 50 - (tl * 14)/2, DY + 8 + ty * 16,
1845              text_line, FS_SMALL, FC_YELLOW);
1846
1847     text += tl + (tc == ' ' ? 1 : 0);
1848   }
1849
1850   if (req_state & REQ_ASK)
1851   {
1852     MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
1853     MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
1854   }
1855   else if (req_state & REQ_CONFIRM)
1856   {
1857     MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
1858   }
1859   else if (req_state & REQ_PLAYER)
1860   {
1861     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
1862     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
1863     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
1864     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
1865   }
1866
1867   /* copy request gadgets to door backbuffer */
1868   BlitBitmap(drawto, bitmap_db_door,
1869              DX, DY, DXSIZE, DYSIZE,
1870              DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1871
1872   OpenDoor(DOOR_OPEN_1);
1873
1874 #if 0
1875   ClearEventQueue();
1876 #endif
1877
1878   if (!(req_state & REQUEST_WAIT_FOR))
1879   {
1880     SetDrawBackgroundMask(REDRAW_FIELD);
1881
1882     return FALSE;
1883   }
1884
1885   if (game_status != MAINMENU)
1886     InitAnimation();
1887
1888   button_status = MB_RELEASED;
1889
1890   request_gadget_id = -1;
1891
1892   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
1893
1894   while(result < 0)
1895   {
1896     if (PendingEvent())
1897     {
1898       Event event;
1899
1900       NextEvent(&event);
1901
1902       switch(event.type)
1903       {
1904         case EVENT_BUTTONPRESS:
1905         case EVENT_BUTTONRELEASE:
1906         case EVENT_MOTIONNOTIFY:
1907         {
1908           if (event.type == EVENT_MOTIONNOTIFY)
1909           {
1910             if (!PointerInWindow(window))
1911               continue; /* window and pointer are on different screens */
1912
1913             if (!button_status)
1914               continue;
1915
1916             motion_status = TRUE;
1917             mx = ((MotionEvent *) &event)->x;
1918             my = ((MotionEvent *) &event)->y;
1919           }
1920           else
1921           {
1922             motion_status = FALSE;
1923             mx = ((ButtonEvent *) &event)->x;
1924             my = ((ButtonEvent *) &event)->y;
1925             if (event.type == EVENT_BUTTONPRESS)
1926               button_status = ((ButtonEvent *) &event)->button;
1927             else
1928               button_status = MB_RELEASED;
1929           }
1930
1931           /* this sets 'request_gadget_id' */
1932           HandleGadgets(mx, my, button_status);
1933
1934           switch(request_gadget_id)
1935           {
1936             case TOOL_CTRL_ID_YES:
1937               result = TRUE;
1938               break;
1939             case TOOL_CTRL_ID_NO:
1940               result = FALSE;
1941               break;
1942             case TOOL_CTRL_ID_CONFIRM:
1943               result = TRUE | FALSE;
1944               break;
1945
1946             case TOOL_CTRL_ID_PLAYER_1:
1947               result = 1;
1948               break;
1949             case TOOL_CTRL_ID_PLAYER_2:
1950               result = 2;
1951               break;
1952             case TOOL_CTRL_ID_PLAYER_3:
1953               result = 3;
1954               break;
1955             case TOOL_CTRL_ID_PLAYER_4:
1956               result = 4;
1957               break;
1958
1959             default:
1960               break;
1961           }
1962
1963           break;
1964         }
1965
1966         case EVENT_KEYPRESS:
1967           switch(GetEventKey((KeyEvent *)&event, TRUE))
1968           {
1969             case KSYM_Return:
1970               result = 1;
1971               break;
1972
1973             case KSYM_Escape:
1974               result = 0;
1975               break;
1976
1977             default:
1978               break;
1979           }
1980           if (req_state & REQ_PLAYER)
1981             result = 0;
1982           break;
1983
1984         case EVENT_KEYRELEASE:
1985           ClearPlayerAction();
1986           break;
1987
1988         default:
1989           HandleOtherEvents(&event);
1990           break;
1991       }
1992     }
1993     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
1994     {
1995       int joy = AnyJoystick();
1996
1997       if (joy & JOY_BUTTON_1)
1998         result = 1;
1999       else if (joy & JOY_BUTTON_2)
2000         result = 0;
2001     }
2002
2003     DoAnimation();
2004
2005     /* don't eat all CPU time */
2006     Delay(10);
2007   }
2008
2009   if (game_status != MAINMENU)
2010     StopAnimation();
2011
2012   UnmapToolButtons();
2013
2014   if (!(req_state & REQ_STAY_OPEN))
2015   {
2016     CloseDoor(DOOR_CLOSE_1);
2017
2018     if (!(req_state & REQ_STAY_CLOSED) && (old_door_state & DOOR_OPEN_1))
2019     {
2020       BlitBitmap(bitmap_db_door, bitmap_db_door,
2021                  DOOR_GFX_PAGEX2,DOOR_GFX_PAGEY1, DXSIZE,DYSIZE,
2022                  DOOR_GFX_PAGEX1,DOOR_GFX_PAGEY1);
2023       OpenDoor(DOOR_OPEN_1);
2024     }
2025   }
2026
2027   RemapAllGadgets();
2028
2029   SetDrawBackgroundMask(REDRAW_FIELD);
2030
2031 #if defined(PLATFORM_UNIX)
2032   /* continue network game after request */
2033   if (options.network &&
2034       game_status == PLAYING &&
2035       req_state & REQUEST_WAIT_FOR)
2036     SendToServer_ContinuePlaying();
2037 #endif
2038
2039   return result;
2040 }
2041
2042 unsigned int OpenDoor(unsigned int door_state)
2043 {
2044   unsigned int new_door_state;
2045
2046   if (door_state & DOOR_COPY_BACK)
2047   {
2048     BlitBitmap(bitmap_db_door, bitmap_db_door,
2049                DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE + VYSIZE,
2050                DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2051     door_state &= ~DOOR_COPY_BACK;
2052   }
2053
2054   new_door_state = MoveDoor(door_state);
2055
2056   return(new_door_state);
2057 }
2058
2059 unsigned int CloseDoor(unsigned int door_state)
2060 {
2061   unsigned int new_door_state;
2062
2063   BlitBitmap(backbuffer, bitmap_db_door,
2064              DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2065   BlitBitmap(backbuffer, bitmap_db_door,
2066              VX, VY, VXSIZE, VYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY2);
2067
2068   new_door_state = MoveDoor(door_state);
2069
2070   return(new_door_state);
2071 }
2072
2073 unsigned int GetDoorState()
2074 {
2075   return MoveDoor(DOOR_GET_STATE);
2076 }
2077
2078 unsigned int SetDoorState(unsigned int door_state)
2079 {
2080   return MoveDoor(door_state | DOOR_SET_STATE);
2081 }
2082
2083 unsigned int MoveDoor(unsigned int door_state)
2084 {
2085   static int door1 = DOOR_OPEN_1;
2086   static int door2 = DOOR_CLOSE_2;
2087   static unsigned long door_delay = 0;
2088   int x, start, stepsize = 2;
2089   unsigned long door_delay_value = stepsize * 5;
2090
2091   if (door_state == DOOR_GET_STATE)
2092     return(door1 | door2);
2093
2094   if (door_state & DOOR_SET_STATE)
2095   {
2096     if (door_state & DOOR_ACTION_1)
2097       door1 = door_state & DOOR_ACTION_1;
2098     if (door_state & DOOR_ACTION_2)
2099       door2 = door_state & DOOR_ACTION_2;
2100
2101     return(door1 | door2);
2102   }
2103
2104   if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
2105     door_state &= ~DOOR_OPEN_1;
2106   else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
2107     door_state &= ~DOOR_CLOSE_1;
2108   if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
2109     door_state &= ~DOOR_OPEN_2;
2110   else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
2111     door_state &= ~DOOR_CLOSE_2;
2112
2113   if (setup.quick_doors)
2114   {
2115     stepsize = 20;
2116     door_delay_value = 0;
2117
2118     StopSound(SND_MENU_DOOR_OPENING);
2119     StopSound(SND_MENU_DOOR_CLOSING);
2120   }
2121
2122   if (global.autoplay_leveldir)
2123   {
2124     door_state |= DOOR_NO_DELAY;
2125     door_state &= ~DOOR_CLOSE_ALL;
2126   }
2127
2128   if (door_state & DOOR_ACTION)
2129   {
2130     if (!(door_state & DOOR_NO_DELAY))
2131     {
2132       /* opening door sound has priority over simultaneously closing door */
2133       if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
2134         PlaySoundStereo(SND_MENU_DOOR_OPENING, SOUND_MAX_RIGHT);
2135       else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
2136         PlaySoundStereo(SND_MENU_DOOR_CLOSING, SOUND_MAX_RIGHT);
2137     }
2138
2139     start = ((door_state & DOOR_NO_DELAY) ? DXSIZE : 0);
2140
2141     for(x=start; x<=DXSIZE; x+=stepsize)
2142     {
2143       Bitmap *bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2144       GC gc = bitmap->stored_clip_gc;
2145
2146       if (!(door_state & DOOR_NO_DELAY))
2147         WaitUntilDelayReached(&door_delay, door_delay_value);
2148
2149       if (door_state & DOOR_ACTION_1)
2150       {
2151         int i = (door_state & DOOR_OPEN_1 ? DXSIZE-x : x);
2152         int j = (DXSIZE - i) / 3;
2153
2154         BlitBitmap(bitmap_db_door, drawto,
2155                    DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1 + i/2,
2156                    DXSIZE,DYSIZE - i/2, DX, DY);
2157
2158         ClearRectangle(drawto, DX, DY + DYSIZE - i/2, DXSIZE,i/2);
2159
2160         SetClipOrigin(bitmap, gc, DX - i, (DY + j) - DOOR_GFX_PAGEY1);
2161         BlitBitmapMasked(bitmap, drawto,
2162                          DXSIZE, DOOR_GFX_PAGEY1, i, 77,
2163                          DX + DXSIZE - i, DY + j);
2164         BlitBitmapMasked(bitmap, drawto,
2165                          DXSIZE, DOOR_GFX_PAGEY1 + 140, i, 63,
2166                          DX + DXSIZE - i, DY + 140 + j);
2167         SetClipOrigin(bitmap, gc, DX - DXSIZE + i, DY - (DOOR_GFX_PAGEY1 + j));
2168         BlitBitmapMasked(bitmap, drawto,
2169                          DXSIZE - i, DOOR_GFX_PAGEY1 + j, i, 77 - j,
2170                          DX, DY);
2171         BlitBitmapMasked(bitmap, drawto,
2172                          DXSIZE-i, DOOR_GFX_PAGEY1 + 140, i, 63,
2173                          DX, DY + 140 - j);
2174
2175         BlitBitmapMasked(bitmap, drawto,
2176                          DXSIZE - i, DOOR_GFX_PAGEY1 + 77, i, 63,
2177                          DX, DY + 77 - j);
2178         BlitBitmapMasked(bitmap, drawto,
2179                          DXSIZE - i, DOOR_GFX_PAGEY1 + 203, i, 77,
2180                          DX, DY + 203 - j);
2181         SetClipOrigin(bitmap, gc, DX - i, (DY + j) - DOOR_GFX_PAGEY1);
2182         BlitBitmapMasked(bitmap, drawto,
2183                          DXSIZE, DOOR_GFX_PAGEY1 + 77, i, 63,
2184                          DX + DXSIZE - i, DY + 77 + j);
2185         BlitBitmapMasked(bitmap, drawto,
2186                          DXSIZE, DOOR_GFX_PAGEY1 + 203, i, 77 - j,
2187                          DX + DXSIZE - i, DY + 203 + j);
2188
2189         redraw_mask |= REDRAW_DOOR_1;
2190       }
2191
2192       if (door_state & DOOR_ACTION_2)
2193       {
2194         int i = (door_state & DOOR_OPEN_2 ? VXSIZE - x : x);
2195         int j = (VXSIZE - i) / 3;
2196
2197         BlitBitmap(bitmap_db_door, drawto,
2198                    DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY2 + i/2,
2199                    VXSIZE, VYSIZE - i/2, VX, VY);
2200
2201         ClearRectangle(drawto, VX, VY + VYSIZE-i/2, VXSIZE, i/2);
2202
2203         SetClipOrigin(bitmap, gc, VX - i, (VY + j) - DOOR_GFX_PAGEY2);
2204         BlitBitmapMasked(bitmap, drawto,
2205                          VXSIZE, DOOR_GFX_PAGEY2, i, VYSIZE / 2,
2206                          VX + VXSIZE-i, VY+j);
2207         SetClipOrigin(bitmap, gc,
2208                       VX - VXSIZE + i, VY - (DOOR_GFX_PAGEY2 + j));
2209         BlitBitmapMasked(bitmap, drawto,
2210                          VXSIZE - i, DOOR_GFX_PAGEY2 + j, i, VYSIZE / 2 - j,
2211                          VX, VY);
2212
2213         BlitBitmapMasked(bitmap, drawto,
2214                          VXSIZE - i, DOOR_GFX_PAGEY2 + VYSIZE / 2,
2215                          i, VYSIZE / 2, VX, VY + VYSIZE / 2 - j);
2216         SetClipOrigin(bitmap, gc, VX - i, (VY + j) - DOOR_GFX_PAGEY2);
2217         BlitBitmapMasked(bitmap, drawto,
2218                          VXSIZE, DOOR_GFX_PAGEY2 + VYSIZE / 2,
2219                          i, VYSIZE / 2 - j,
2220                          VX + VXSIZE - i, VY + VYSIZE / 2 + j);
2221
2222         redraw_mask |= REDRAW_DOOR_2;
2223       }
2224
2225       BackToFront();
2226
2227       if (game_status == MAINMENU)
2228         DoAnimation();
2229     }
2230   }
2231
2232   if (setup.quick_doors)
2233   {
2234     StopSound(SND_MENU_DOOR_OPENING);
2235     StopSound(SND_MENU_DOOR_CLOSING);
2236   }
2237
2238   if (door_state & DOOR_ACTION_1)
2239     door1 = door_state & DOOR_ACTION_1;
2240   if (door_state & DOOR_ACTION_2)
2241     door2 = door_state & DOOR_ACTION_2;
2242
2243   return (door1 | door2);
2244 }
2245
2246 void DrawSpecialEditorDoor()
2247 {
2248   /* draw bigger toolbox window */
2249   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2250              DOOR_GFX_PAGEX7, 0, EXSIZE + 8, 8,
2251              EX - 4, EY - 12);
2252   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
2253              EX - 4, VY - 4, EXSIZE + 8, EYSIZE - VYSIZE + 4,
2254              EX - 4, EY - 4);
2255
2256   redraw_mask |= REDRAW_ALL;
2257 }
2258
2259 void UndrawSpecialEditorDoor()
2260 {
2261   /* draw normal tape recorder window */
2262   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
2263              EX - 4, EY - 12, EXSIZE + 8, EYSIZE - VYSIZE + 12,
2264              EX - 4, EY - 12);
2265
2266   redraw_mask |= REDRAW_ALL;
2267 }
2268
2269 #ifndef TARGET_SDL
2270 int ReadPixel(DrawBuffer *bitmap, int x, int y)
2271 {
2272   XImage *pixel_image;
2273   unsigned long pixel_value;
2274
2275   pixel_image = XGetImage(display, bitmap->drawable,
2276                           x, y, 1, 1, AllPlanes, ZPixmap);
2277   pixel_value = XGetPixel(pixel_image, 0, 0);
2278
2279   XDestroyImage(pixel_image);
2280
2281   return pixel_value;
2282 }
2283 #endif
2284
2285 /* ---------- new tool button stuff ---------------------------------------- */
2286
2287 /* graphic position values for tool buttons */
2288 #define TOOL_BUTTON_YES_XPOS            2
2289 #define TOOL_BUTTON_YES_YPOS            250
2290 #define TOOL_BUTTON_YES_GFX_YPOS        0
2291 #define TOOL_BUTTON_YES_XSIZE           46
2292 #define TOOL_BUTTON_YES_YSIZE           28
2293 #define TOOL_BUTTON_NO_XPOS             52
2294 #define TOOL_BUTTON_NO_YPOS             TOOL_BUTTON_YES_YPOS
2295 #define TOOL_BUTTON_NO_GFX_YPOS         TOOL_BUTTON_YES_GFX_YPOS
2296 #define TOOL_BUTTON_NO_XSIZE            TOOL_BUTTON_YES_XSIZE
2297 #define TOOL_BUTTON_NO_YSIZE            TOOL_BUTTON_YES_YSIZE
2298 #define TOOL_BUTTON_CONFIRM_XPOS        TOOL_BUTTON_YES_XPOS
2299 #define TOOL_BUTTON_CONFIRM_YPOS        TOOL_BUTTON_YES_YPOS
2300 #define TOOL_BUTTON_CONFIRM_GFX_YPOS    30
2301 #define TOOL_BUTTON_CONFIRM_XSIZE       96
2302 #define TOOL_BUTTON_CONFIRM_YSIZE       TOOL_BUTTON_YES_YSIZE
2303 #define TOOL_BUTTON_PLAYER_XSIZE        30
2304 #define TOOL_BUTTON_PLAYER_YSIZE        30
2305 #define TOOL_BUTTON_PLAYER_GFX_XPOS     5
2306 #define TOOL_BUTTON_PLAYER_GFX_YPOS     185
2307 #define TOOL_BUTTON_PLAYER_XPOS         (5 + TOOL_BUTTON_PLAYER_XSIZE / 2)
2308 #define TOOL_BUTTON_PLAYER_YPOS         (215 - TOOL_BUTTON_PLAYER_YSIZE / 2)
2309 #define TOOL_BUTTON_PLAYER1_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
2310                                          + 0 * TOOL_BUTTON_PLAYER_XSIZE)
2311 #define TOOL_BUTTON_PLAYER2_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
2312                                          + 1 * TOOL_BUTTON_PLAYER_XSIZE)
2313 #define TOOL_BUTTON_PLAYER3_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
2314                                          + 0 * TOOL_BUTTON_PLAYER_XSIZE)
2315 #define TOOL_BUTTON_PLAYER4_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
2316                                          + 1 * TOOL_BUTTON_PLAYER_XSIZE)
2317 #define TOOL_BUTTON_PLAYER1_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
2318                                          + 0 * TOOL_BUTTON_PLAYER_YSIZE)
2319 #define TOOL_BUTTON_PLAYER2_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
2320                                          + 0 * TOOL_BUTTON_PLAYER_YSIZE)
2321 #define TOOL_BUTTON_PLAYER3_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
2322                                          + 1 * TOOL_BUTTON_PLAYER_YSIZE)
2323 #define TOOL_BUTTON_PLAYER4_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
2324                                          + 1 * TOOL_BUTTON_PLAYER_YSIZE)
2325
2326 static struct
2327 {
2328   int xpos, ypos;
2329   int x, y;
2330   int width, height;
2331   int gadget_id;
2332   char *infotext;
2333 } toolbutton_info[NUM_TOOL_BUTTONS] =
2334 {
2335   {
2336     TOOL_BUTTON_YES_XPOS,       TOOL_BUTTON_YES_GFX_YPOS,
2337     TOOL_BUTTON_YES_XPOS,       TOOL_BUTTON_YES_YPOS,
2338     TOOL_BUTTON_YES_XSIZE,      TOOL_BUTTON_YES_YSIZE,
2339     TOOL_CTRL_ID_YES,
2340     "yes"
2341   },
2342   {
2343     TOOL_BUTTON_NO_XPOS,        TOOL_BUTTON_NO_GFX_YPOS,
2344     TOOL_BUTTON_NO_XPOS,        TOOL_BUTTON_NO_YPOS,
2345     TOOL_BUTTON_NO_XSIZE,       TOOL_BUTTON_NO_YSIZE,
2346     TOOL_CTRL_ID_NO,
2347     "no"
2348   },
2349   {
2350     TOOL_BUTTON_CONFIRM_XPOS,   TOOL_BUTTON_CONFIRM_GFX_YPOS,
2351     TOOL_BUTTON_CONFIRM_XPOS,   TOOL_BUTTON_CONFIRM_YPOS,
2352     TOOL_BUTTON_CONFIRM_XSIZE,  TOOL_BUTTON_CONFIRM_YSIZE,
2353     TOOL_CTRL_ID_CONFIRM,
2354     "confirm"
2355   },
2356   {
2357     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
2358     TOOL_BUTTON_PLAYER1_XPOS,   TOOL_BUTTON_PLAYER1_YPOS,
2359     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
2360     TOOL_CTRL_ID_PLAYER_1,
2361     "player 1"
2362   },
2363   {
2364     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
2365     TOOL_BUTTON_PLAYER2_XPOS,   TOOL_BUTTON_PLAYER2_YPOS,
2366     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
2367     TOOL_CTRL_ID_PLAYER_2,
2368     "player 2"
2369   },
2370   {
2371     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
2372     TOOL_BUTTON_PLAYER3_XPOS,   TOOL_BUTTON_PLAYER3_YPOS,
2373     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
2374     TOOL_CTRL_ID_PLAYER_3,
2375     "player 3"
2376   },
2377   {
2378     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
2379     TOOL_BUTTON_PLAYER4_XPOS,   TOOL_BUTTON_PLAYER4_YPOS,
2380     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
2381     TOOL_CTRL_ID_PLAYER_4,
2382     "player 4"
2383   }
2384 };
2385
2386 void CreateToolButtons()
2387 {
2388   int i;
2389
2390   for (i=0; i<NUM_TOOL_BUTTONS; i++)
2391   {
2392     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2393     Bitmap *deco_bitmap = None;
2394     int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
2395     struct GadgetInfo *gi;
2396     unsigned long event_mask;
2397     int gd_xoffset, gd_yoffset;
2398     int gd_x1, gd_x2, gd_y;
2399     int id = i;
2400
2401     event_mask = GD_EVENT_RELEASED;
2402
2403     gd_xoffset = toolbutton_info[i].xpos;
2404     gd_yoffset = toolbutton_info[i].ypos;
2405     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
2406     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
2407     gd_y = DOOR_GFX_PAGEY1 + gd_yoffset;
2408
2409     if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
2410     {
2411       int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
2412
2413       getMiniGraphicSource(PLAYER_NR_GFX(IMG_PLAYER1, player_nr),
2414                            &deco_bitmap, &deco_x, &deco_y);
2415       deco_xpos = (toolbutton_info[i].width - MINI_TILEX) / 2;
2416       deco_ypos = (toolbutton_info[i].height - MINI_TILEY) / 2;
2417     }
2418
2419     gi = CreateGadget(GDI_CUSTOM_ID, id,
2420                       GDI_INFO_TEXT, toolbutton_info[i].infotext,
2421                       GDI_X, DX + toolbutton_info[i].x,
2422                       GDI_Y, DY + toolbutton_info[i].y,
2423                       GDI_WIDTH, toolbutton_info[i].width,
2424                       GDI_HEIGHT, toolbutton_info[i].height,
2425                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
2426                       GDI_STATE, GD_BUTTON_UNPRESSED,
2427                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y,
2428                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y,
2429                       GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
2430                       GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
2431                       GDI_DECORATION_SIZE, MINI_TILEX, MINI_TILEY,
2432                       GDI_DECORATION_SHIFTING, 1, 1,
2433                       GDI_EVENT_MASK, event_mask,
2434                       GDI_CALLBACK_ACTION, HandleToolButtons,
2435                       GDI_END);
2436
2437     if (gi == NULL)
2438       Error(ERR_EXIT, "cannot create gadget");
2439
2440     tool_gadget[id] = gi;
2441   }
2442 }
2443
2444 void FreeToolButtons()
2445 {
2446   int i;
2447
2448   for (i=0; i<NUM_TOOL_BUTTONS; i++)
2449     FreeGadget(tool_gadget[i]);
2450 }
2451
2452 static void UnmapToolButtons()
2453 {
2454   int i;
2455
2456   for (i=0; i<NUM_TOOL_BUTTONS; i++)
2457     UnmapGadget(tool_gadget[i]);
2458 }
2459
2460 static void HandleToolButtons(struct GadgetInfo *gi)
2461 {
2462   request_gadget_id = gi->custom_id;
2463 }
2464
2465 int get_next_element(int element)
2466 {
2467   switch(element)
2468   {
2469     case EL_QUICKSAND_FILLING:          return EL_QUICKSAND_FULL;
2470     case EL_QUICKSAND_EMPTYING:         return EL_QUICKSAND_EMPTY;
2471     case EL_MAGIC_WALL_FILLING:         return EL_MAGIC_WALL_FULL;
2472     case EL_MAGIC_WALL_EMPTYING:        return EL_MAGIC_WALL_ACTIVE;
2473     case EL_BD_MAGIC_WALL_FILLING:      return EL_BD_MAGIC_WALL_FULL;
2474     case EL_BD_MAGIC_WALL_EMPTYING:     return EL_BD_MAGIC_WALL_ACTIVE;
2475     case EL_AMOEBA_DRIPPING:            return EL_AMOEBA_WET;
2476
2477     default:                            return element;
2478   }
2479 }
2480
2481 int el2img(int element)
2482 {
2483   int graphic = element_info[element].graphic[ACTION_DEFAULT];
2484
2485 #if DEBUG
2486   if (graphic < 0)
2487     Error(ERR_WARN, "element %d -> graphic %d -- probably crashing now...",
2488           element, graphic);
2489 #endif
2490
2491   return graphic;
2492 }
2493
2494 int el_dir2img(int element, int direction)
2495 {
2496   return el_dir_act2img(element, direction, ACTION_DEFAULT);
2497 }
2498
2499 int el_dir_act2img(int element, int direction, int action)
2500 {
2501 #if DEBUG
2502   if (element < 0)
2503   {    
2504     printf("el_dir_act2img: THIS SHOULD NEVER HAPPEN: element == %d\n",
2505            element);
2506
2507     return IMG_EMPTY;
2508   }
2509
2510   if (action < 0)
2511   {    
2512     printf("el_dir_act2img: THIS SHOULD NEVER HAPPEN: action == %d\n",
2513            action);
2514
2515     return IMG_EMPTY;
2516   }
2517 #endif
2518
2519   direction = MV_DIR_BIT(direction);
2520
2521   return element_info[element].direction_graphic[action][direction];
2522 }