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