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