3f4cff156333e254be9e205a7b1c7aa01c7fc8fb
[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 static int el_act_dir2crm(int, int, int);
38 static int el_act2crm(int, int);
39
40 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
41 static int request_gadget_id = -1;
42
43 void SetDrawtoField(int mode)
44 {
45   if (mode == DRAW_BUFFERED && setup.soft_scrolling)
46   {
47     FX = TILEX;
48     FY = TILEY;
49     BX1 = -1;
50     BY1 = -1;
51     BX2 = SCR_FIELDX;
52     BY2 = SCR_FIELDY;
53     redraw_x1 = 1;
54     redraw_y1 = 1;
55
56     drawto_field = fieldbuffer;
57   }
58   else  /* DRAW_DIRECT, DRAW_BACKBUFFER */
59   {
60     FX = SX;
61     FY = SY;
62     BX1 = 0;
63     BY1 = 0;
64     BX2 = SCR_FIELDX - 1;
65     BY2 = SCR_FIELDY - 1;
66     redraw_x1 = 0;
67     redraw_y1 = 0;
68
69     drawto_field = (mode == DRAW_DIRECT ? window :  backbuffer);
70   }
71 }
72
73 void RedrawPlayfield(boolean force_redraw, int x, int y, int width, int height)
74 {
75   if (game_status == GAME_MODE_PLAYING && !game.envelope_active)
76   {
77     if (force_redraw)
78     {
79       x = gfx.sx - TILEX;
80       y = gfx.sy - TILEY;
81       width = gfx.sxsize + 2 * TILEX;
82       height = gfx.sysize + 2 * TILEY;
83     }
84
85     if (force_redraw || setup.direct_draw)
86     {
87       int xx, yy;
88       int x1 = (x - SX) / TILEX, y1 = (y - SY) / TILEY;
89       int x2 = (x - SX + width) / TILEX, y2 = (y - SY + height) / TILEY;
90
91       if (setup.direct_draw)
92         SetDrawtoField(DRAW_BACKBUFFER);
93
94       for(xx=BX1; xx<=BX2; xx++)
95         for(yy=BY1; yy<=BY2; yy++)
96           if (xx >= x1 && xx <= x2 && yy >= y1 && yy <= y2)
97             DrawScreenField(xx, yy);
98       DrawAllPlayers();
99
100       if (setup.direct_draw)
101         SetDrawtoField(DRAW_DIRECT);
102     }
103
104     if (setup.soft_scrolling)
105     {
106       int fx = FX, fy = FY;
107
108       fx += (ScreenMovDir & (MV_LEFT|MV_RIGHT) ? ScreenGfxPos : 0);
109       fy += (ScreenMovDir & (MV_UP|MV_DOWN)    ? ScreenGfxPos : 0);
110
111       BlitBitmap(fieldbuffer, backbuffer, fx,fy, SXSIZE,SYSIZE, SX,SY);
112     }
113   }
114
115   BlitBitmap(drawto, window, x, y, width, height, x, y);
116 }
117
118 void BackToFront()
119 {
120   int x,y;
121   DrawBuffer *buffer = (drawto_field == window ? backbuffer : drawto_field);
122
123   if (setup.direct_draw && game_status == GAME_MODE_PLAYING)
124     redraw_mask &= ~REDRAW_MAIN;
125
126   if (redraw_mask & REDRAW_TILES && redraw_tiles > REDRAWTILES_THRESHOLD)
127     redraw_mask |= REDRAW_FIELD;
128
129   if (redraw_mask & REDRAW_FIELD)
130     redraw_mask &= ~REDRAW_TILES;
131
132   if (redraw_mask == REDRAW_NONE)
133     return;
134
135   if (global.fps_slowdown && game_status == GAME_MODE_PLAYING)
136   {
137     static boolean last_frame_skipped = FALSE;
138     boolean skip_even_when_not_scrolling = TRUE;
139     boolean just_scrolling = (ScreenMovDir != 0);
140     boolean verbose = FALSE;
141
142     if (global.fps_slowdown_factor > 1 &&
143         (FrameCounter % global.fps_slowdown_factor) &&
144         (just_scrolling || skip_even_when_not_scrolling))
145     {
146       redraw_mask &= ~REDRAW_MAIN;
147
148       last_frame_skipped = TRUE;
149
150       if (verbose)
151         printf("FRAME SKIPPED\n");
152     }
153     else
154     {
155       if (last_frame_skipped)
156         redraw_mask |= REDRAW_FIELD;
157
158       last_frame_skipped = FALSE;
159
160       if (verbose)
161         printf("frame not skipped\n");
162     }
163   }
164
165   /* synchronize X11 graphics at this point; if we would synchronize the
166      display immediately after the buffer switching (after the XFlush),
167      this could mean that we have to wait for the graphics to complete,
168      although we could go on doing calculations for the next frame */
169
170   SyncDisplay();
171
172   if (redraw_mask & REDRAW_ALL)
173   {
174     BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
175     redraw_mask = 0;
176   }
177
178   if (redraw_mask & REDRAW_FIELD)
179   {
180     if (game_status != GAME_MODE_PLAYING ||
181         redraw_mask & REDRAW_FROM_BACKBUFFER)
182     {
183       BlitBitmap(backbuffer, window,
184                  REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
185     }
186     else
187     {
188       int fx = FX, fy = FY;
189
190       if (setup.soft_scrolling)
191       {
192         fx += (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
193         fy += (ScreenMovDir & (MV_UP | MV_DOWN)    ? ScreenGfxPos : 0);
194       }
195
196       if (setup.soft_scrolling ||
197           ABS(ScreenMovPos) + ScrollStepSize == TILEX ||
198           ABS(ScreenMovPos) == ScrollStepSize ||
199           redraw_tiles > REDRAWTILES_THRESHOLD)
200       {
201         BlitBitmap(buffer, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
202
203 #ifdef DEBUG
204 #if 0
205         printf("redrawing all (ScreenGfxPos == %d) because %s\n",
206                ScreenGfxPos,
207                (setup.soft_scrolling ?
208                 "setup.soft_scrolling" :
209                 ABS(ScreenGfxPos) + ScrollStepSize == TILEX ?
210                 "ABS(ScreenGfxPos) + ScrollStepSize == TILEX" :
211                 ABS(ScreenGfxPos) == ScrollStepSize ?
212                 "ABS(ScreenGfxPos) == ScrollStepSize" :
213                 "redraw_tiles > REDRAWTILES_THRESHOLD"));
214 #endif
215 #endif
216       }
217     }
218
219     redraw_mask &= ~REDRAW_MAIN;
220   }
221
222   if (redraw_mask & REDRAW_DOORS)
223   {
224     if (redraw_mask & REDRAW_DOOR_1)
225       BlitBitmap(backbuffer, window, DX, DY, DXSIZE, DYSIZE, DX, DY);
226     if (redraw_mask & REDRAW_DOOR_2)
227     {
228       if ((redraw_mask & REDRAW_DOOR_2) == REDRAW_DOOR_2)
229         BlitBitmap(backbuffer, window, VX,VY, VXSIZE,VYSIZE, VX,VY);
230       else
231       {
232         if (redraw_mask & REDRAW_VIDEO_1)
233           BlitBitmap(backbuffer, window,
234                      VX+VIDEO_DISPLAY1_XPOS,VY+VIDEO_DISPLAY1_YPOS,
235                      VIDEO_DISPLAY_XSIZE,VIDEO_DISPLAY_YSIZE,
236                      VX+VIDEO_DISPLAY1_XPOS,VY+VIDEO_DISPLAY1_YPOS);
237         if (redraw_mask & REDRAW_VIDEO_2)
238           BlitBitmap(backbuffer, window,
239                      VX+VIDEO_DISPLAY2_XPOS,VY+VIDEO_DISPLAY2_YPOS,
240                      VIDEO_DISPLAY_XSIZE,VIDEO_DISPLAY_YSIZE,
241                      VX+VIDEO_DISPLAY2_XPOS,VY+VIDEO_DISPLAY2_YPOS);
242         if (redraw_mask & REDRAW_VIDEO_3)
243           BlitBitmap(backbuffer, window,
244                      VX+VIDEO_CONTROL_XPOS,VY+VIDEO_CONTROL_YPOS,
245                      VIDEO_CONTROL_XSIZE,VIDEO_CONTROL_YSIZE,
246                      VX+VIDEO_CONTROL_XPOS,VY+VIDEO_CONTROL_YPOS);
247       }
248     }
249
250     if (redraw_mask & REDRAW_DOOR_3)
251       BlitBitmap(backbuffer, window, EX, EY, EXSIZE, EYSIZE, EX, EY);
252
253     redraw_mask &= ~REDRAW_DOORS;
254   }
255
256   if (redraw_mask & REDRAW_MICROLEVEL)
257   {
258     BlitBitmap(backbuffer, window, SX, SY + 10 * TILEY, SXSIZE, 7 * TILEY,
259                SX, SY + 10 * TILEY);
260
261     redraw_mask &= ~REDRAW_MICROLEVEL;
262   }
263
264   if (redraw_mask & REDRAW_TILES)
265   {
266     for(x=0; x<SCR_FIELDX; x++)
267       for(y=0; y<SCR_FIELDY; y++)
268         if (redraw[redraw_x1 + x][redraw_y1 + y])
269           BlitBitmap(buffer, window,
270                      FX + x * TILEX, FX + y * TILEY, TILEX, TILEY,
271                      SX + x * TILEX, SY + y * TILEY);
272   }
273
274   if (redraw_mask & REDRAW_FPS)         /* display frames per second */
275   {
276     char text[100];
277     char info1[100];
278
279     sprintf(info1, " (only every %d. frame)", global.fps_slowdown_factor);
280     if (!global.fps_slowdown)
281       info1[0] = '\0';
282
283     sprintf(text, "%.1f fps%s", global.frames_per_second, info1);
284     DrawTextExt(window, SX, SY, text, FONT_TEXT_2, BLIT_OPAQUE);
285   }
286
287   FlushDisplay();
288
289   for(x=0; x<MAX_BUF_XSIZE; x++)
290     for(y=0; y<MAX_BUF_YSIZE; y++)
291       redraw[x][y] = 0;
292   redraw_tiles = 0;
293   redraw_mask = REDRAW_NONE;
294 }
295
296 void FadeToFront()
297 {
298 #if 0
299   long fading_delay = 300;
300
301   if (setup.fading && (redraw_mask & REDRAW_FIELD))
302   {
303 #endif
304
305 #if 0
306     int x,y;
307
308     ClearRectangle(window, REAL_SX,REAL_SY,FULL_SXSIZE,FULL_SYSIZE);
309     FlushDisplay();
310
311     for(i=0;i<2*FULL_SYSIZE;i++)
312     {
313       for(y=0;y<FULL_SYSIZE;y++)
314       {
315         BlitBitmap(backbuffer, window,
316                    REAL_SX,REAL_SY+i, FULL_SXSIZE,1, REAL_SX,REAL_SY+i);
317       }
318       FlushDisplay();
319       Delay(10);
320     }
321 #endif
322
323 #if 0
324     for(i=1;i<FULL_SYSIZE;i+=2)
325       BlitBitmap(backbuffer, window,
326                  REAL_SX,REAL_SY+i, FULL_SXSIZE,1, REAL_SX,REAL_SY+i);
327     FlushDisplay();
328     Delay(fading_delay);
329 #endif
330
331 #if 0
332     SetClipOrigin(clip_gc[PIX_FADEMASK], 0, 0);
333     BlitBitmapMasked(backbuffer, window,
334                      REAL_SX,REAL_SY, FULL_SXSIZE,FULL_SYSIZE,
335                      REAL_SX,REAL_SY);
336     FlushDisplay();
337     Delay(fading_delay);
338
339     SetClipOrigin(clip_gc[PIX_FADEMASK], -1, -1);
340     BlitBitmapMasked(backbuffer, window,
341                      REAL_SX,REAL_SY, FULL_SXSIZE,FULL_SYSIZE,
342                      REAL_SX,REAL_SY);
343     FlushDisplay();
344     Delay(fading_delay);
345
346     SetClipOrigin(clip_gc[PIX_FADEMASK], 0, -1);
347     BlitBitmapMasked(backbuffer, window,
348                      REAL_SX,REAL_SY, FULL_SXSIZE,FULL_SYSIZE,
349                      REAL_SX,REAL_SY);
350     FlushDisplay();
351     Delay(fading_delay);
352
353     SetClipOrigin(clip_gc[PIX_FADEMASK], -1, 0);
354     BlitBitmapMasked(backbuffer, window,
355                      REAL_SX,REAL_SY, FULL_SXSIZE,FULL_SYSIZE,
356                      REAL_SX,REAL_SY);
357     FlushDisplay();
358     Delay(fading_delay);
359
360     redraw_mask &= ~REDRAW_MAIN;
361   }
362 #endif
363
364   BackToFront();
365 }
366
367 void SetMainBackgroundImage(int graphic)
368 {
369   SetMainBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
370                           graphic_info[graphic].bitmap ?
371                           graphic_info[graphic].bitmap :
372                           graphic_info[IMG_BACKGROUND].bitmap);
373 }
374
375 void SetDoorBackgroundImage(int graphic)
376 {
377   SetDoorBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
378                           graphic_info[graphic].bitmap ?
379                           graphic_info[graphic].bitmap :
380                           graphic_info[IMG_BACKGROUND].bitmap);
381 }
382
383 void DrawBackground(int dest_x, int dest_y, int width, int height)
384 {
385   ClearRectangleOnBackground(backbuffer, dest_x, dest_y, width, height);
386
387   redraw_mask |= REDRAW_FIELD;
388 }
389
390 void ClearWindow()
391 {
392   DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
393
394   if (setup.soft_scrolling && game_status == GAME_MODE_PLAYING)
395   {
396     ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
397     SetDrawtoField(DRAW_BUFFERED);
398   }
399   else
400     SetDrawtoField(DRAW_BACKBUFFER);
401
402   if (setup.direct_draw && game_status == GAME_MODE_PLAYING)
403   {
404     ClearRectangle(window, REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
405     SetDrawtoField(DRAW_DIRECT);
406   }
407 }
408
409 void MarkTileDirty(int x, int y)
410 {
411   int xx = redraw_x1 + x;
412   int yy = redraw_y1 + y;
413
414   if (!redraw[xx][yy])
415     redraw_tiles++;
416
417   redraw[xx][yy] = TRUE;
418   redraw_mask |= REDRAW_TILES;
419 }
420
421 void SetBorderElement()
422 {
423   int x, y;
424
425   BorderElement = EL_EMPTY;
426
427   for(y=0; y<lev_fieldy && BorderElement == EL_EMPTY; y++)
428   {
429     for(x=0; x<lev_fieldx; x++)
430     {
431       if (!IS_INDESTRUCTIBLE(Feld[x][y]))
432         BorderElement = EL_STEELWALL;
433
434       if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
435         x = lev_fieldx - 2;
436     }
437   }
438 }
439
440 void SetRandomAnimationValue(int x, int y)
441 {
442   gfx.anim_random_frame = GfxRandom[x][y];
443 }
444
445 inline int getGraphicAnimationFrame(int graphic, int sync_frame)
446 {
447   /* animation synchronized with global frame counter, not move position */
448   if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
449     sync_frame = FrameCounter;
450
451   return getAnimationFrame(graphic_info[graphic].anim_frames,
452                            graphic_info[graphic].anim_delay,
453                            graphic_info[graphic].anim_mode,
454                            graphic_info[graphic].anim_start_frame,
455                            sync_frame);
456 }
457
458 inline void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
459                                     int graphic, int sync_frame, int mask_mode)
460 {
461   int frame = getGraphicAnimationFrame(graphic, sync_frame);
462
463   if (mask_mode == USE_MASKING)
464     DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
465   else
466     DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
467 }
468
469 inline void DrawGraphicAnimation(int x, int y, int graphic)
470 {
471   int lx = LEVELX(x), ly = LEVELY(y);
472
473   if (!IN_SCR_FIELD(x, y))
474     return;
475
476   DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
477                           graphic, GfxFrame[lx][ly], NO_MASKING);
478   MarkTileDirty(x, y);
479 }
480
481 void DrawLevelGraphicAnimation(int x, int y, int graphic)
482 {
483   DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
484 }
485
486 void DrawLevelElementAnimation(int x, int y, int element)
487 {
488 #if 1
489   int graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
490
491   DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
492 #else
493   DrawGraphicAnimation(SCREENX(x), SCREENY(y), el2img(element));
494 #endif
495 }
496
497 inline void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
498 {
499   int sx = SCREENX(x), sy = SCREENY(y);
500
501   if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
502     return;
503
504   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
505     return;
506
507   DrawGraphicAnimation(sx, sy, graphic);
508
509   if (GFX_CRUMBLED(Feld[x][y]))
510     DrawLevelFieldCrumbledSand(x, y);
511 }
512
513 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
514 {
515   int sx = SCREENX(x), sy = SCREENY(y);
516   int graphic;
517
518   if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
519     return;
520
521   graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
522
523   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
524     return;
525
526   DrawGraphicAnimation(sx, sy, graphic);
527
528   if (GFX_CRUMBLED(element))
529     DrawLevelFieldCrumbledSand(x, y);
530 }
531
532 void DrawAllPlayers()
533 {
534   int i;
535
536   for(i=0; i<MAX_PLAYERS; i++)
537     if (stored_player[i].active)
538       DrawPlayer(&stored_player[i]);
539 }
540
541 void DrawPlayerField(int x, int y)
542 {
543   if (!IS_PLAYER(x, y))
544     return;
545
546   DrawPlayer(PLAYERINFO(x, y));
547 }
548
549 void DrawPlayer(struct PlayerInfo *player)
550 {
551 #if 0
552   int jx = player->jx, jy = player->jy;
553   int last_jx = player->last_jx, last_jy = player->last_jy;
554   int next_jx = jx + (jx - last_jx), next_jy = jy + (jy - last_jy);
555   int sx = SCREENX(jx), sy = SCREENY(jy);
556   int sxx = 0, syy = 0;
557   int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
558   int graphic;
559   int frame = 0;
560   boolean player_is_moving = (last_jx != jx || last_jy != jy ? TRUE : FALSE);
561   int move_dir = player->MovDir;
562   int action = ACTION_DEFAULT;
563 #else
564   int jx = player->jx, jy = player->jy;
565   int move_dir = player->MovDir;
566   int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
567   int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? +1 : 0);
568   int last_jx = (player->is_moving ? jx - dx : jx);
569   int last_jy = (player->is_moving ? jy - dy : jy);
570   int next_jx = jx + dx;
571   int next_jy = jy + dy;
572   int sx = SCREENX(jx), sy = SCREENY(jy);
573   int sxx = 0, syy = 0;
574   int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
575   int graphic;
576   int frame = 0;
577   boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
578   int action = ACTION_DEFAULT;
579 #endif
580
581   if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
582     return;
583
584 #if DEBUG
585   if (!IN_LEV_FIELD(jx,jy))
586   {
587     printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
588     printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
589     printf("DrawPlayerField(): This should never happen!\n");
590     return;
591   }
592 #endif
593
594   if (element == EL_EXPLOSION)
595     return;
596
597   action = (player->Pushing ? ACTION_PUSHING :
598             player->is_digging ? ACTION_DIGGING :
599             player->is_collecting ? ACTION_COLLECTING :
600             player->is_moving ? ACTION_MOVING :
601             player->snapped ? ACTION_SNAPPING : ACTION_DEFAULT);
602
603 #if 0
604   printf("::: '%s'\n", element_action_info[action].suffix);
605 #endif
606
607   InitPlayerGfxAnimation(player, action, move_dir);
608
609   /* ----------------------------------------------------------------------- */
610   /* draw things in the field the player is leaving, if needed               */
611   /* ----------------------------------------------------------------------- */
612
613 #if 1
614   if (player->is_moving)
615 #else
616   if (player_is_moving)
617 #endif
618   {
619     if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
620     {
621       DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
622
623       if (last_element == EL_DYNAMITE_ACTIVE ||
624           last_element == EL_SP_DISK_RED_ACTIVE)
625         DrawDynamite(last_jx, last_jy);
626       else
627         DrawLevelFieldThruMask(last_jx, last_jy);
628     }
629     else if (last_element == EL_DYNAMITE_ACTIVE ||
630              last_element == EL_SP_DISK_RED_ACTIVE)
631       DrawDynamite(last_jx, last_jy);
632     else
633       DrawLevelField(last_jx, last_jy);
634
635     if (player->Pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
636     {
637 #if 1
638 #if 1
639       DrawLevelElement(next_jx, next_jy, EL_EMPTY);
640 #else
641       if (player->GfxPos)
642       {
643         if (Feld[next_jx][next_jy] == EL_SOKOBAN_FIELD_FULL)
644           DrawLevelElement(next_jx, next_jy, EL_SOKOBAN_FIELD_EMPTY);
645         else
646           DrawLevelElement(next_jx, next_jy, EL_EMPTY);
647       }
648       else
649         DrawLevelField(next_jx, next_jy);
650 #endif
651 #endif
652     }
653   }
654
655   if (!IN_SCR_FIELD(sx, sy))
656     return;
657
658   if (setup.direct_draw)
659     SetDrawtoField(DRAW_BUFFERED);
660
661   /* ----------------------------------------------------------------------- */
662   /* draw things behind the player, if needed                                */
663   /* ----------------------------------------------------------------------- */
664
665   if (Back[jx][jy])
666     DrawLevelElement(jx, jy, Back[jx][jy]);
667   else if (IS_ACTIVE_BOMB(element))
668     DrawLevelElement(jx, jy, EL_EMPTY);
669   else
670   {
671     if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
672     {
673 #if 1
674       if (GFX_CRUMBLED(GfxElement[jx][jy]))
675         DrawLevelFieldCrumbledSandDigging(jx, jy, move_dir, player->StepFrame);
676 #else
677       if (GfxElement[jx][jy] == EL_SAND)
678         DrawLevelFieldCrumbledSandDigging(jx, jy, move_dir, player->StepFrame);
679 #endif
680       else
681       {
682         int old_element = GfxElement[jx][jy];
683         int old_graphic = el_act_dir2img(old_element, action, move_dir);
684         int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
685
686         DrawGraphic(sx, sy, old_graphic, frame);
687       }
688     }
689     else
690     {
691       GfxElement[jx][jy] = EL_UNDEFINED;
692
693       DrawLevelField(jx, jy);
694     }
695   }
696
697   /* ----------------------------------------------------------------------- */
698   /* draw player himself                                                     */
699   /* ----------------------------------------------------------------------- */
700
701   if (player->use_murphy_graphic)
702   {
703     static int last_horizontal_dir = MV_LEFT;
704     int direction;
705
706     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
707       last_horizontal_dir = move_dir;
708
709     direction = (player->snapped ? move_dir : last_horizontal_dir);
710
711     graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
712   }
713   else
714     graphic = el_act_dir2img(player->element_nr, player->GfxAction, move_dir);
715
716   frame = getGraphicAnimationFrame(graphic, player->Frame);
717
718   if (player->GfxPos)
719   {
720     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
721       sxx = player->GfxPos;
722     else
723       syy = player->GfxPos;
724   }
725
726   if (!setup.soft_scrolling && ScreenMovPos)
727     sxx = syy = 0;
728
729   DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
730
731   if (SHIELD_ON(player))
732   {
733     int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
734                    IMG_SHIELD_NORMAL_ACTIVE);
735     int frame = getGraphicAnimationFrame(graphic, -1);
736
737     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
738   }
739
740   /* ----------------------------------------------------------------------- */
741   /* draw things the player is pushing, if needed                            */
742   /* ----------------------------------------------------------------------- */
743
744 #if 0
745   printf("::: %d, %d [%d, %d] [%d]\n",
746          player->Pushing, player_is_moving, player->GfxAction,
747          player->is_moving, player_is_moving);
748 #endif
749
750 #if 1
751   if (player->Pushing && player->is_moving)
752 #else
753   if (player->Pushing && player_is_moving)
754 #endif
755   {
756     int px = SCREENX(next_jx), py = SCREENY(next_jy);
757
758     if (Back[next_jx][next_jy])
759       DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
760
761 #if 1
762     if ((sxx || syy) && element == EL_SOKOBAN_OBJECT)
763       DrawGraphicShiftedThruMask(px, py, sxx, syy, IMG_SOKOBAN_OBJECT, 0,
764                                  NO_CUTTING);
765 #else
766     if ((sxx || syy) &&
767         (element == EL_SOKOBAN_FIELD_EMPTY ||
768          Feld[next_jx][next_jy] == EL_SOKOBAN_FIELD_FULL))
769       DrawGraphicShiftedThruMask(px, py, sxx, syy, IMG_SOKOBAN_OBJECT, 0,
770                                  NO_CUTTING);
771 #endif
772     else
773     {
774 #if 1
775       int element = MovingOrBlocked2Element(next_jx, next_jy);
776 #else
777 #if 1
778       int element = Feld[jx][jy];
779 #else
780       int element = Feld[next_jx][next_jy];
781 #endif
782 #endif
783
784 #if 1
785       int graphic = el2img(element);
786       int frame = 0;
787
788 #if 0
789       if ((sxx || syy) && IS_PUSHABLE(element))
790 #endif
791       {
792         graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
793         frame = getGraphicAnimationFrame(graphic, player->Frame);
794       }
795
796 #if 0
797       printf("::: pushing %d: %d ...\n", sxx, frame);
798 #endif
799
800       DrawGraphicShifted(px, py, sxx, syy, graphic, frame,
801                          NO_CUTTING, NO_MASKING);
802 #endif
803     }
804   }
805
806   /* ----------------------------------------------------------------------- */
807   /* draw things in front of player (active dynamite or dynabombs)           */
808   /* ----------------------------------------------------------------------- */
809
810   if (IS_ACTIVE_BOMB(element))
811   {
812     graphic = el2img(element);
813     frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
814
815     if (game.emulation == EMU_SUPAPLEX)
816       DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
817     else
818       DrawGraphicThruMask(sx, sy, graphic, frame);
819   }
820
821   if (player_is_moving && last_element == EL_EXPLOSION)
822   {
823 #if 1
824     int graphic = el_act2img(GfxElement[last_jx][last_jy], ACTION_EXPLODING);
825 #else
826     int stored = Store[last_jx][last_jy];
827     int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
828                    stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
829                    IMG_SP_EXPLOSION);
830 #endif
831     int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
832     int phase = ExplodePhase[last_jx][last_jy] - 1;
833     int frame = getGraphicAnimationFrame(graphic, phase - delay);
834
835     if (phase >= delay)
836       DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
837   }
838
839   /* ----------------------------------------------------------------------- */
840   /* draw elements the player is just walking/passing through/under          */
841   /* ----------------------------------------------------------------------- */
842
843   /* handle the field the player is leaving ... */
844   if (player_is_moving && IS_ACCESSIBLE_INSIDE(last_element))
845     DrawLevelField(last_jx, last_jy);
846   else if (player_is_moving && IS_ACCESSIBLE_UNDER(last_element))
847     DrawLevelFieldThruMask(last_jx, last_jy);
848
849   /* ... and the field the player is entering */
850   if (IS_ACCESSIBLE_INSIDE(element))
851     DrawLevelField(jx, jy);
852   else if (IS_ACCESSIBLE_UNDER(element))
853     DrawLevelFieldThruMask(jx, jy);
854
855   if (setup.direct_draw)
856   {
857     int dest_x = SX + SCREENX(MIN(jx, last_jx)) * TILEX;
858     int dest_y = SY + SCREENY(MIN(jy, last_jy)) * TILEY;
859     int x_size = TILEX * (1 + ABS(jx - last_jx));
860     int y_size = TILEY * (1 + ABS(jy - last_jy));
861
862     BlitBitmap(drawto_field, window,
863                dest_x, dest_y, x_size, y_size, dest_x, dest_y);
864     SetDrawtoField(DRAW_DIRECT);
865   }
866
867   MarkTileDirty(sx,sy);
868 }
869
870 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
871 {
872   struct GraphicInfo *g = &graphic_info[graphic];
873
874   *bitmap = g->bitmap;
875
876   if (g->offset_y == 0)         /* frames are ordered horizontally */
877   {
878     int max_width = g->anim_frames_per_line * g->width;
879
880     *x = (g->src_x + frame * g->offset_x) % max_width;
881     *y = g->src_y + (g->src_x + frame * g->offset_x) / max_width * g->height;
882   }
883   else if (g->offset_x == 0)    /* frames are ordered vertically */
884   {
885     int max_height = g->anim_frames_per_line * g->height;
886
887     *x = g->src_x + (g->src_y + frame * g->offset_y) / max_height * g->width;
888     *y = (g->src_y + frame * g->offset_y) % max_height;
889   }
890   else                          /* frames are ordered diagonally */
891   {
892     *x = g->src_x + frame * g->offset_x;
893     *y = g->src_y + frame * g->offset_y;
894   }
895 }
896
897 void DrawGraphic(int x, int y, int graphic, int frame)
898 {
899 #if DEBUG
900   if (!IN_SCR_FIELD(x, y))
901   {
902     printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
903     printf("DrawGraphic(): This should never happen!\n");
904     return;
905   }
906 #endif
907
908   DrawGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic, frame);
909   MarkTileDirty(x, y);
910 }
911
912 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
913                     int frame)
914 {
915   Bitmap *src_bitmap;
916   int src_x, src_y;
917
918   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
919   BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
920 }
921
922 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
923 {
924 #if DEBUG
925   if (!IN_SCR_FIELD(x, y))
926   {
927     printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
928     printf("DrawGraphicThruMask(): This should never happen!\n");
929     return;
930   }
931 #endif
932
933   DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y *TILEY, graphic,
934                          frame);
935   MarkTileDirty(x, y);
936 }
937
938 void DrawGraphicThruMaskExt(DrawBuffer *d, int dest_x, int dest_y, int graphic,
939                             int frame)
940 {
941 #if 1
942   Bitmap *src_bitmap;
943   int src_x, src_y;
944   GC drawing_gc;
945
946   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
947   drawing_gc = src_bitmap->stored_clip_gc;
948 #else
949   GC drawing_gc = src_bitmap->stored_clip_gc;
950   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
951   int src_x = graphic_info[graphic].src_x;
952   int src_y = graphic_info[graphic].src_y;
953   int offset_x = graphic_info[graphic].offset_x;
954   int offset_y = graphic_info[graphic].offset_y;
955
956   src_x += frame * offset_x;
957   src_y += frame * offset_y;
958
959 #endif
960
961   SetClipOrigin(src_bitmap, drawing_gc, dest_x - src_x, dest_y - src_y);
962   BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY, dest_x, dest_y);
963 }
964
965 void DrawMiniGraphic(int x, int y, int graphic)
966 {
967   DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
968   MarkTileDirty(x / 2, y / 2);
969 }
970
971 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
972 {
973   struct GraphicInfo *g = &graphic_info[graphic];
974   int mini_startx = 0;
975   int mini_starty = g->bitmap->height * 2 / 3;
976
977   *bitmap = g->bitmap;
978   *x = mini_startx + g->src_x / 2;
979   *y = mini_starty + g->src_y / 2;
980 }
981
982 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
983 {
984   Bitmap *src_bitmap;
985   int src_x, src_y;
986
987   getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
988   BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
989 }
990
991 void DrawGraphicShifted(int x,int y, int dx,int dy, int graphic, int frame,
992                         int cut_mode, int mask_mode)
993 {
994   Bitmap *src_bitmap;
995   GC drawing_gc;
996   int src_x, src_y;
997   int width = TILEX, height = TILEY;
998   int cx = 0, cy = 0;
999   int dest_x, dest_y;
1000
1001   if (graphic < 0)
1002   {
1003     DrawGraphic(x, y, graphic, frame);
1004     return;
1005   }
1006
1007   if (dx || dy)                 /* shifted graphic */
1008   {
1009     if (x < BX1)                /* object enters playfield from the left */
1010     {
1011       x = BX1;
1012       width = dx;
1013       cx = TILEX - dx;
1014       dx = 0;
1015     }
1016     else if (x > BX2)           /* object enters playfield from the right */
1017     {
1018       x = BX2;
1019       width = -dx;
1020       dx = TILEX + dx;
1021     }
1022     else if (x==BX1 && dx < 0)  /* object leaves playfield to the left */
1023     {
1024       width += dx;
1025       cx = -dx;
1026       dx = 0;
1027     }
1028     else if (x==BX2 && dx > 0)  /* object leaves playfield to the right */
1029       width -= dx;
1030     else if (dx)                /* general horizontal movement */
1031       MarkTileDirty(x + SIGN(dx), y);
1032
1033     if (y < BY1)                /* object enters playfield from the top */
1034     {
1035       if (cut_mode==CUT_BELOW)  /* object completely above top border */
1036         return;
1037
1038       y = BY1;
1039       height = dy;
1040       cy = TILEY - dy;
1041       dy = 0;
1042     }
1043     else if (y > BY2)           /* object enters playfield from the bottom */
1044     {
1045       y = BY2;
1046       height = -dy;
1047       dy = TILEY + dy;
1048     }
1049     else if (y==BY1 && dy < 0)  /* object leaves playfield to the top */
1050     {
1051       height += dy;
1052       cy = -dy;
1053       dy = 0;
1054     }
1055     else if (dy > 0 && cut_mode == CUT_ABOVE)
1056     {
1057       if (y == BY2)             /* object completely above bottom border */
1058         return;
1059
1060       height = dy;
1061       cy = TILEY - dy;
1062       dy = TILEY;
1063       MarkTileDirty(x, y + 1);
1064     }                           /* object leaves playfield to the bottom */
1065     else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1066       height -= dy;
1067     else if (dy)                /* general vertical movement */
1068       MarkTileDirty(x, y + SIGN(dy));
1069   }
1070
1071 #if 1
1072   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1073 #else
1074   src_bitmap = graphic_info[graphic].bitmap;
1075   src_x = graphic_info[graphic].src_x;
1076   src_y = graphic_info[graphic].src_y;
1077   offset_x = graphic_info[graphic].offset_x;
1078   offset_y = graphic_info[graphic].offset_y;
1079
1080   src_x += frame * offset_x;
1081   src_y += frame * offset_y;
1082 #endif
1083
1084   drawing_gc = src_bitmap->stored_clip_gc;
1085
1086   src_x += cx;
1087   src_y += cy;
1088
1089   dest_x = FX + x * TILEX + dx;
1090   dest_y = FY + y * TILEY + dy;
1091
1092 #if DEBUG
1093   if (!IN_SCR_FIELD(x,y))
1094   {
1095     printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1096     printf("DrawGraphicShifted(): This should never happen!\n");
1097     return;
1098   }
1099 #endif
1100
1101   if (mask_mode == USE_MASKING)
1102   {
1103     SetClipOrigin(src_bitmap, drawing_gc, dest_x - src_x, dest_y - src_y);
1104     BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1105                      dest_x, dest_y);
1106   }
1107   else
1108     BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1109                dest_x, dest_y);
1110
1111   MarkTileDirty(x,y);
1112 }
1113
1114 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1115                                 int frame, int cut_mode)
1116 {
1117   DrawGraphicShifted(x,y, dx,dy, graphic, frame, cut_mode, USE_MASKING);
1118 }
1119
1120 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1121                           int cut_mode, int mask_mode)
1122 {
1123   int lx = LEVELX(x), ly = LEVELY(y);
1124   int graphic;
1125   int frame;
1126
1127   if (IN_LEV_FIELD(lx, ly))
1128   {
1129     SetRandomAnimationValue(lx, ly);
1130
1131     graphic = el_act_dir2img(element, GfxAction[lx][ly], MovDir[lx][ly]);
1132     frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1133   }
1134   else  /* border element */
1135   {
1136     graphic = el2img(element);
1137     frame = getGraphicAnimationFrame(graphic, -1);
1138   }
1139
1140   if (element == EL_EXPANDABLE_WALL)
1141   {
1142     boolean left_stopped = FALSE, right_stopped = FALSE;
1143
1144     if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1145       left_stopped = TRUE;
1146     if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1147       right_stopped = TRUE;
1148
1149     if (left_stopped && right_stopped)
1150       graphic = IMG_WALL;
1151     else if (left_stopped)
1152     {
1153       graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1154       frame = graphic_info[graphic].anim_frames - 1;
1155     }
1156     else if (right_stopped)
1157     {
1158       graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1159       frame = graphic_info[graphic].anim_frames - 1;
1160     }
1161   }
1162
1163   if (dx || dy)
1164     DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1165   else if (mask_mode == USE_MASKING)
1166     DrawGraphicThruMask(x, y, graphic, frame);
1167   else
1168     DrawGraphic(x, y, graphic, frame);
1169 }
1170
1171 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1172                          int cut_mode, int mask_mode)
1173 {
1174   if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1175     DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1176                          cut_mode, mask_mode);
1177 }
1178
1179 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1180                               int cut_mode)
1181 {
1182   DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1183 }
1184
1185 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1186                              int cut_mode)
1187 {
1188   DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1189 }
1190
1191 void DrawLevelElementThruMask(int x, int y, int element)
1192 {
1193   DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1194 }
1195
1196 void DrawLevelFieldThruMask(int x, int y)
1197 {
1198   DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1199 }
1200
1201 static void DrawLevelFieldCrumbledSandExt(int x, int y, int graphic, int frame)
1202 {
1203   Bitmap *src_bitmap;
1204   int src_x, src_y;
1205   int sx = SCREENX(x), sy = SCREENY(y);
1206   int element;
1207   int width, height, cx, cy, i;
1208 #if 1
1209   int crumbled_border_size = graphic_info[graphic].border_size;
1210 #else
1211   int snip = TILEX / 8; /* number of border pixels from "crumbled graphic" */
1212 #endif
1213   static int xy[4][2] =
1214   {
1215     { 0, -1 },
1216     { -1, 0 },
1217     { +1, 0 },
1218     { 0, +1 }
1219   };
1220
1221 #if 0
1222   if (x == 0 && y == 7)
1223     printf("::: %d, %d [%d]\n", GfxElement[x][y], Feld[x][y],
1224            crumbled_border_size);
1225 #endif
1226
1227   if (!IN_LEV_FIELD(x, y))
1228     return;
1229
1230   element = (GfxElement[x][y] != EL_UNDEFINED && Feld[x][y] != EL_EXPLOSION ?
1231              GfxElement[x][y] : Feld[x][y]);
1232
1233   /* crumble field itself */
1234   if (GFX_CRUMBLED(element) && !IS_MOVING(x, y))
1235   {
1236     if (!IN_SCR_FIELD(sx, sy))
1237       return;
1238
1239     getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1240
1241     for(i=0; i<4; i++)
1242     {
1243       int xx = x + xy[i][0];
1244       int yy = y + xy[i][1];
1245
1246       element = (IN_LEV_FIELD(xx, yy) ? Feld[xx][yy] : BorderElement);
1247
1248       /* check if neighbour field is of same type */
1249       if (GFX_CRUMBLED(element) && !IS_MOVING(xx, yy))
1250         continue;
1251
1252 #if 0
1253       if (Feld[x][y] == EL_CUSTOM_START + 123)
1254         printf("::: crumble [%d] THE CHAOS ENGINE (%d, %d): %d, %d\n",
1255                i, Feld[x][y], element,
1256                GFX_CRUMBLED(element), IS_MOVING(x, y));
1257 #endif
1258
1259       if (i == 1 || i == 2)
1260       {
1261         width = crumbled_border_size;
1262         height = TILEY;
1263         cx = (i == 2 ? TILEX - crumbled_border_size : 0);
1264         cy = 0;
1265       }
1266       else
1267       {
1268         width = TILEX;
1269         height = crumbled_border_size;
1270         cx = 0;
1271         cy = (i == 3 ? TILEY - crumbled_border_size : 0);
1272       }
1273
1274       BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1275                  width, height, FX + sx * TILEX + cx, FY + sy * TILEY + cy);
1276     }
1277
1278     MarkTileDirty(sx, sy);
1279   }
1280   else          /* crumble neighbour fields */
1281   {
1282 #if 0
1283     getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1284 #endif
1285
1286     for(i=0; i<4; i++)
1287     {
1288       int xx = x + xy[i][0];
1289       int yy = y + xy[i][1];
1290       int sxx = sx + xy[i][0];
1291       int syy = sy + xy[i][1];
1292
1293       if (!IN_LEV_FIELD(xx, yy) ||
1294           !IN_SCR_FIELD(sxx, syy) ||
1295           !GFX_CRUMBLED(Feld[xx][yy]) ||
1296           IS_MOVING(xx, yy))
1297         continue;
1298
1299 #if 1
1300       graphic = el_act2crm(Feld[xx][yy], ACTION_DEFAULT);
1301       crumbled_border_size = graphic_info[graphic].border_size;
1302
1303       getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1304 #endif
1305
1306       if (i == 1 || i == 2)
1307       {
1308         width = crumbled_border_size;
1309         height = TILEY;
1310         cx = (i == 1 ? TILEX - crumbled_border_size : 0);
1311         cy = 0;
1312       }
1313       else
1314       {
1315         width = TILEX;
1316         height = crumbled_border_size;
1317         cx = 0;
1318         cy = (i == 0 ? TILEY - crumbled_border_size : 0);
1319       }
1320
1321       BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1322                  width, height, FX + sxx * TILEX + cx, FY + syy * TILEY + cy);
1323
1324       MarkTileDirty(sxx, syy);
1325     }
1326   }
1327 }
1328
1329 void DrawLevelFieldCrumbledSand(int x, int y)
1330 {
1331 #if 1
1332   int graphic;
1333
1334   if (!IN_LEV_FIELD(x, y))
1335     return;
1336
1337   graphic = el_act2crm(Feld[x][y], ACTION_DEFAULT);
1338
1339   DrawLevelFieldCrumbledSandExt(x, y, graphic, 0);
1340 #else
1341   DrawLevelFieldCrumbledSandExt(x, y, IMG_SAND_CRUMBLED, 0);
1342 #endif
1343 }
1344
1345 void DrawLevelFieldCrumbledSandDigging(int x, int y, int direction,
1346                                        int step_frame)
1347 {
1348 #if 1
1349   int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
1350   int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
1351 #else
1352   int graphic1 = el_act_dir2img(EL_SAND,          ACTION_DIGGING, direction);
1353   int graphic2 = el_act_dir2img(EL_SAND_CRUMBLED, ACTION_DIGGING, direction);
1354 #endif
1355   int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
1356   int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
1357   int sx = SCREENX(x), sy = SCREENY(y);
1358
1359   DrawGraphic(sx, sy, graphic1, frame1);
1360   DrawLevelFieldCrumbledSandExt(x, y, graphic2, frame2);
1361 }
1362
1363 void DrawLevelFieldCrumbledSandNeighbours(int x, int y)
1364 {
1365   int sx = SCREENX(x), sy = SCREENY(y);
1366   static int xy[4][2] =
1367   {
1368     { 0, -1 },
1369     { -1, 0 },
1370     { +1, 0 },
1371     { 0, +1 }
1372   };
1373   int i;
1374
1375   for(i=0; i<4; i++)
1376   {
1377     int xx = x + xy[i][0];
1378     int yy = y + xy[i][1];
1379     int sxx = sx + xy[i][0];
1380     int syy = sy + xy[i][1];
1381
1382     if (!IN_LEV_FIELD(xx, yy) ||
1383         !IN_SCR_FIELD(sxx, syy) ||
1384         !GFX_CRUMBLED(Feld[xx][yy]) ||
1385         IS_MOVING(xx, yy))
1386       continue;
1387
1388     DrawLevelField(xx, yy);
1389   }
1390 }
1391
1392 static int getBorderElement(int x, int y)
1393 {
1394   int border[7][2] =
1395   {
1396     { EL_STEELWALL_TOPLEFT,             EL_INVISIBLE_STEELWALL_TOPLEFT     },
1397     { EL_STEELWALL_TOPRIGHT,            EL_INVISIBLE_STEELWALL_TOPRIGHT    },
1398     { EL_STEELWALL_BOTTOMLEFT,          EL_INVISIBLE_STEELWALL_BOTTOMLEFT  },
1399     { EL_STEELWALL_BOTTOMRIGHT,         EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
1400     { EL_STEELWALL_VERTICAL,            EL_INVISIBLE_STEELWALL_VERTICAL    },
1401     { EL_STEELWALL_HORIZONTAL,          EL_INVISIBLE_STEELWALL_HORIZONTAL  },
1402     { EL_STEELWALL,                     EL_INVISIBLE_STEELWALL             }
1403   };
1404   int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
1405   int steel_position = (x == -1         && y == -1              ? 0 :
1406                         x == lev_fieldx && y == -1              ? 1 :
1407                         x == -1         && y == lev_fieldy      ? 2 :
1408                         x == lev_fieldx && y == lev_fieldy      ? 3 :
1409                         x == -1         || x == lev_fieldx      ? 4 :
1410                         y == -1         || y == lev_fieldy      ? 5 : 6);
1411
1412   return border[steel_position][steel_type];
1413 }
1414
1415 void DrawScreenElement(int x, int y, int element)
1416 {
1417   DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
1418   DrawLevelFieldCrumbledSand(LEVELX(x), LEVELY(y));
1419 }
1420
1421 void DrawLevelElement(int x, int y, int element)
1422 {
1423   if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1424     DrawScreenElement(SCREENX(x), SCREENY(y), element);
1425 }
1426
1427 void DrawScreenField(int x, int y)
1428 {
1429   int lx = LEVELX(x), ly = LEVELY(y);
1430   int element, content;
1431
1432   if (!IN_LEV_FIELD(lx, ly))
1433   {
1434     if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
1435       element = EL_EMPTY;
1436     else
1437       element = getBorderElement(lx, ly);
1438
1439     DrawScreenElement(x, y, element);
1440     return;
1441   }
1442
1443   element = Feld[lx][ly];
1444   content = Store[lx][ly];
1445
1446   if (IS_MOVING(lx, ly))
1447   {
1448     int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
1449     boolean cut_mode = NO_CUTTING;
1450
1451     if (element == EL_QUICKSAND_EMPTYING ||
1452         element == EL_MAGIC_WALL_EMPTYING ||
1453         element == EL_BD_MAGIC_WALL_EMPTYING ||
1454         element == EL_AMOEBA_DROPPING)
1455       cut_mode = CUT_ABOVE;
1456     else if (element == EL_QUICKSAND_FILLING ||
1457              element == EL_MAGIC_WALL_FILLING ||
1458              element == EL_BD_MAGIC_WALL_FILLING)
1459       cut_mode = CUT_BELOW;
1460
1461     if (cut_mode == CUT_ABOVE)
1462       DrawScreenElementShifted(x, y, 0, 0, element, NO_CUTTING);
1463     else
1464       DrawScreenElement(x, y, EL_EMPTY);
1465
1466     if (horiz_move)
1467       DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
1468     else if (cut_mode == NO_CUTTING)
1469       DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
1470     else
1471       DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
1472
1473     if (content == EL_ACID)
1474       DrawLevelElementThruMask(lx, ly + 1, EL_ACID);
1475   }
1476   else if (IS_BLOCKED(lx, ly))
1477   {
1478     int oldx, oldy;
1479     int sx, sy;
1480     int horiz_move;
1481     boolean cut_mode = NO_CUTTING;
1482     int element_old, content_old;
1483
1484     Blocked2Moving(lx, ly, &oldx, &oldy);
1485     sx = SCREENX(oldx);
1486     sy = SCREENY(oldy);
1487     horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
1488                   MovDir[oldx][oldy] == MV_RIGHT);
1489
1490     element_old = Feld[oldx][oldy];
1491     content_old = Store[oldx][oldy];
1492
1493     if (element_old == EL_QUICKSAND_EMPTYING ||
1494         element_old == EL_MAGIC_WALL_EMPTYING ||
1495         element_old == EL_BD_MAGIC_WALL_EMPTYING ||
1496         element_old == EL_AMOEBA_DROPPING)
1497       cut_mode = CUT_ABOVE;
1498
1499     DrawScreenElement(x, y, EL_EMPTY);
1500
1501     if (horiz_move)
1502       DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
1503                                NO_CUTTING);
1504     else if (cut_mode == NO_CUTTING)
1505       DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
1506                                cut_mode);
1507     else
1508       DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
1509                                cut_mode);
1510   }
1511   else if (IS_DRAWABLE(element))
1512     DrawScreenElement(x, y, element);
1513   else
1514     DrawScreenElement(x, y, EL_EMPTY);
1515 }
1516
1517 void DrawLevelField(int x, int y)
1518 {
1519   if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1520     DrawScreenField(SCREENX(x), SCREENY(y));
1521   else if (IS_MOVING(x, y))
1522   {
1523     int newx,newy;
1524
1525     Moving2Blocked(x, y, &newx, &newy);
1526     if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
1527       DrawScreenField(SCREENX(newx), SCREENY(newy));
1528   }
1529   else if (IS_BLOCKED(x, y))
1530   {
1531     int oldx, oldy;
1532
1533     Blocked2Moving(x, y, &oldx, &oldy);
1534     if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
1535       DrawScreenField(SCREENX(oldx), SCREENY(oldy));
1536   }
1537 }
1538
1539 void DrawMiniElement(int x, int y, int element)
1540 {
1541   int graphic;
1542
1543   graphic = el2edimg(element);
1544   DrawMiniGraphic(x, y, graphic);
1545 }
1546
1547 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
1548 {
1549   int x = sx + scroll_x, y = sy + scroll_y;
1550
1551   if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
1552     DrawMiniElement(sx, sy, EL_EMPTY);
1553   else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
1554     DrawMiniElement(sx, sy, Feld[x][y]);
1555   else
1556     DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
1557 }
1558
1559 void DrawEnvelopeBackground(int envelope_nr, int startx, int starty,
1560                             int x, int y, int xsize, int ysize, int font_nr)
1561 {
1562   int font_width  = getFontWidth(font_nr);
1563   int font_height = getFontHeight(font_nr);
1564   int graphic = IMG_GAME_ENVELOPE_1_BACKGROUND + envelope_nr;
1565   Bitmap *src_bitmap;
1566   int src_x, src_y;
1567   int dst_x = SX + startx + x * font_width;
1568   int dst_y = SY + starty + y * font_height;
1569   int width  = graphic_info[graphic].width;
1570   int height = graphic_info[graphic].height;
1571   int inner_width  = MAX(width  - 2 * font_width,  font_width);
1572   int inner_height = MAX(height - 2 * font_height, font_height);
1573   int inner_sx = (width >= 3 * font_width ? font_width : 0);
1574   int inner_sy = (height >= 3 * font_height ? font_height : 0);
1575   boolean draw_masked = graphic_info[graphic].draw_masked;
1576
1577   getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1578
1579   if (src_bitmap == NULL || width < font_width || height < font_height)
1580   {
1581     ClearRectangle(drawto, dst_x, dst_y, font_width, font_height);
1582     return;
1583   }
1584
1585   src_x += (x == 0 ? 0 : x == xsize - 1 ? width  - font_width  :
1586             inner_sx + (x - 1) * font_width  % inner_width);
1587   src_y += (y == 0 ? 0 : y == ysize - 1 ? height - font_height :
1588             inner_sy + (y - 1) * font_height % inner_height);
1589
1590   if (draw_masked)
1591   {
1592     SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
1593                   dst_x - src_x, dst_y - src_y);
1594     BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, font_width, font_height,
1595                      dst_x, dst_y);
1596   }
1597   else
1598     BlitBitmap(src_bitmap, drawto, src_x, src_y, font_width, font_height,
1599                dst_x, dst_y);
1600 }
1601
1602 #if 1
1603
1604 void AnimateEnvelope(int envelope_nr)
1605 {
1606 }
1607
1608 void ShowEnvelope(int envelope_nr)
1609 {
1610   int element = EL_ENVELOPE_1 + envelope_nr;
1611   int graphic = IMG_GAME_ENVELOPE_1_BACKGROUND + envelope_nr;
1612   int sound_opening = element_info[element].sound[ACTION_OPENING];
1613   int sound_closing = element_info[element].sound[ACTION_CLOSING];
1614   boolean draw_masked = graphic_info[graphic].draw_masked;
1615   int anim_mode = graphic_info[graphic].anim_mode;
1616   int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_ON_BACKGROUND);
1617   int font_nr = FONT_TEXT_1 + envelope_nr;
1618   int font_width = getFontWidth(font_nr);
1619   int font_height = getFontHeight(font_nr);
1620   boolean ffwd_delay = (tape.playing && tape.fast_forward);
1621   unsigned long anim_delay = 0;
1622   int anim_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
1623   int wait_delay_value = (ffwd_delay ? 500 : 1000);
1624   int start_pos_vertically = 0;
1625   int i, x, y;
1626
1627   game.envelope_active = TRUE;
1628
1629   if (anim_mode != ANIM_NONE)
1630     PlaySoundStereo(sound_opening, SOUND_MIDDLE);
1631
1632   if (anim_mode == ANIM_DEFAULT)
1633   {
1634     /* open envelope window horizontally */
1635     for (i = 0; i <= level.envelope_xsize[envelope_nr]; i++)
1636     {
1637       int xsize = i + 2;
1638       int ysize = 2;
1639       int startx = (SXSIZE - xsize * font_width)  / 2;
1640       int starty = (SYSIZE - ysize * font_height) / 2;
1641
1642       SetDrawtoField(DRAW_BUFFERED);
1643
1644       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1645
1646       SetDrawtoField(DRAW_BACKBUFFER);
1647
1648       for (y=0; y < ysize; y++) for (x=0; x < xsize; x++)
1649         DrawEnvelopeBackground(envelope_nr, startx, starty, x, y, xsize, ysize,
1650                                font_nr);
1651
1652       redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
1653       BackToFront();
1654
1655       WaitUntilDelayReached(&anim_delay, anim_delay_value / 2);
1656     }
1657   }
1658
1659   if (anim_mode == ANIM_NONE)
1660     start_pos_vertically = level.envelope_ysize[envelope_nr];
1661
1662   /* open envelope window vertically */
1663   for (i = start_pos_vertically; i <= level.envelope_ysize[envelope_nr]; i++)
1664   {
1665     int xsize = level.envelope_xsize[envelope_nr] + 2;
1666     int ysize = i + 2;
1667     int startx = (SXSIZE - xsize * font_width)  / 2;
1668     int starty = (SYSIZE - ysize * font_height) / 2;
1669
1670     SetDrawtoField(DRAW_BUFFERED);
1671
1672     BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1673
1674     SetDrawtoField(DRAW_BACKBUFFER);
1675
1676     for (y=0; y < ysize; y++) for (x=0; x < xsize; x++)
1677       DrawEnvelopeBackground(envelope_nr, startx, starty, x, y, xsize, ysize,
1678                              font_nr);
1679
1680     DrawTextToTextArea(SX + startx + font_width, SY + starty + font_height,
1681                        level.envelope_text[envelope_nr], font_nr,
1682                        level.envelope_xsize[envelope_nr], i, mask_mode);
1683
1684     redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
1685     BackToFront();
1686
1687     WaitUntilDelayReached(&anim_delay, anim_delay_value);
1688   }
1689
1690   if (tape.playing)
1691     Delay(wait_delay_value);
1692   else
1693     WaitForEventToContinue();
1694
1695   if (anim_mode != ANIM_NONE)
1696     PlaySoundStereo(sound_closing, SOUND_MIDDLE);
1697
1698   if (anim_mode != ANIM_NONE)
1699   {
1700     /* close envelope window vertically */
1701     for (i = level.envelope_ysize[envelope_nr]; i >= 0; i--)
1702     {
1703       int xsize = level.envelope_xsize[envelope_nr] + 2;
1704       int ysize = i + 2;
1705       int startx = (SXSIZE - xsize * font_width)  / 2;
1706       int starty = (SYSIZE - ysize * font_height) / 2;
1707
1708       SetDrawtoField(DRAW_BUFFERED);
1709
1710       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1711
1712       SetDrawtoField(DRAW_BACKBUFFER);
1713
1714       for (y=0; y < ysize; y++) for (x=0; x < xsize; x++)
1715         DrawEnvelopeBackground(envelope_nr, startx, starty, x, y, xsize, ysize,
1716                                font_nr);
1717
1718       DrawTextToTextArea(SX + startx + font_width, SY + starty + font_height,
1719                          level.envelope_text[envelope_nr], font_nr,
1720                          level.envelope_xsize[envelope_nr], i, mask_mode);
1721
1722       redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
1723       BackToFront();
1724
1725       WaitUntilDelayReached(&anim_delay, anim_delay_value);
1726     }
1727   }
1728
1729   if (anim_mode == ANIM_DEFAULT)
1730   {
1731     /* close envelope window horizontally */
1732     for (i = level.envelope_xsize[envelope_nr]; i >= 0; i--)
1733     {
1734       int xsize = i + 2;
1735       int ysize = 2;
1736       int startx = (SXSIZE - xsize * font_width)  / 2;
1737       int starty = (SYSIZE - ysize * font_height) / 2;
1738
1739       SetDrawtoField(DRAW_BUFFERED);
1740
1741       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1742
1743       SetDrawtoField(DRAW_BACKBUFFER);
1744
1745       for (y=0; y < ysize; y++) for (x=0; x < xsize; x++)
1746         DrawEnvelopeBackground(envelope_nr, startx, starty, x, y, xsize, ysize,
1747                                font_nr);
1748
1749       redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
1750       BackToFront();
1751
1752       WaitUntilDelayReached(&anim_delay, anim_delay_value / 2);
1753     }
1754   }
1755
1756   game.envelope_active = FALSE;
1757
1758   SetDrawtoField(DRAW_BUFFERED);
1759
1760   redraw_mask |= REDRAW_FIELD;
1761   BackToFront();
1762 }
1763
1764 #else
1765
1766 void ShowEnvelope(int envelope_nr)
1767 {
1768   int element = EL_ENVELOPE_1 + envelope_nr;
1769   int graphic = IMG_GAME_ENVELOPE_1_BACKGROUND + envelope_nr;
1770   int sound_opening = element_info[element].sound[ACTION_OPENING];
1771   int sound_closing = element_info[element].sound[ACTION_CLOSING];
1772   boolean draw_masked = graphic_info[graphic].draw_masked;
1773   int anim_mode = graphic_info[graphic].anim_mode;
1774   int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_ON_BACKGROUND);
1775   int font_nr = FONT_TEXT_1;
1776   int font_width = getFontWidth(font_nr);
1777   int font_height = getFontHeight(font_nr);
1778   boolean ffwd_delay = (tape.playing && tape.fast_forward);
1779   unsigned long anim_delay = 0;
1780   int anim_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
1781   int wait_delay_value = (ffwd_delay ? 500 : 1000);
1782   int start_pos_vertically = 0;
1783   int i, x, y;
1784
1785   game.envelope_active = TRUE;
1786
1787   if (anim_mode != ANIM_NONE)
1788     PlaySoundStereo(sound_opening, SOUND_MIDDLE);
1789
1790   if (anim_mode == ANIM_DEFAULT)
1791   {
1792     /* open envelope window horizontally */
1793     for (i = 0; i <= level.envelope_xsize[envelope_nr]; i++)
1794     {
1795       int xsize = i + 2;
1796       int ysize = 2;
1797       int startx = (SXSIZE - xsize * font_width)  / 2;
1798       int starty = (SYSIZE - ysize * font_height) / 2;
1799
1800       SetDrawtoField(DRAW_BUFFERED);
1801
1802       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1803
1804       SetDrawtoField(DRAW_BACKBUFFER);
1805
1806       for (y=0; y < ysize; y++) for (x=0; x < xsize; x++)
1807         DrawEnvelopeBackground(envelope_nr, startx, starty, x, y, xsize, ysize,
1808                                font_nr);
1809
1810       redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
1811       BackToFront();
1812
1813       WaitUntilDelayReached(&anim_delay, anim_delay_value / 2);
1814     }
1815   }
1816
1817   if (anim_mode == ANIM_NONE)
1818     start_pos_vertically = level.envelope_ysize[envelope_nr];
1819
1820   /* open envelope window vertically */
1821   for (i = start_pos_vertically; i <= level.envelope_ysize[envelope_nr]; i++)
1822   {
1823     int xsize = level.envelope_xsize[envelope_nr] + 2;
1824     int ysize = i + 2;
1825     int startx = (SXSIZE - xsize * font_width)  / 2;
1826     int starty = (SYSIZE - ysize * font_height) / 2;
1827
1828     SetDrawtoField(DRAW_BUFFERED);
1829
1830     BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1831
1832     SetDrawtoField(DRAW_BACKBUFFER);
1833
1834     for (y=0; y < ysize; y++) for (x=0; x < xsize; x++)
1835       DrawEnvelopeBackground(envelope_nr, startx, starty, x, y, xsize, ysize,
1836                              font_nr);
1837
1838     DrawTextToTextArea(SX + startx + font_width, SY + starty + font_height,
1839                        level.envelope_text[envelope_nr], FONT_TEXT_1,
1840                        level.envelope_xsize[envelope_nr], i, mask_mode);
1841
1842     redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
1843     BackToFront();
1844
1845     WaitUntilDelayReached(&anim_delay, anim_delay_value);
1846   }
1847
1848   if (tape.playing)
1849     Delay(wait_delay_value);
1850   else
1851     WaitForEventToContinue();
1852
1853   if (anim_mode != ANIM_NONE)
1854     PlaySoundStereo(sound_closing, SOUND_MIDDLE);
1855
1856   if (anim_mode != ANIM_NONE)
1857   {
1858     /* close envelope window vertically */
1859     for (i = level.envelope_ysize[envelope_nr]; i >= 0; i--)
1860     {
1861       int xsize = level.envelope_xsize[envelope_nr] + 2;
1862       int ysize = i + 2;
1863       int startx = (SXSIZE - xsize * font_width)  / 2;
1864       int starty = (SYSIZE - ysize * font_height) / 2;
1865
1866       SetDrawtoField(DRAW_BUFFERED);
1867
1868       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1869
1870       SetDrawtoField(DRAW_BACKBUFFER);
1871
1872       for (y=0; y < ysize; y++) for (x=0; x < xsize; x++)
1873         DrawEnvelopeBackground(envelope_nr, startx, starty, x, y, xsize, ysize,
1874                                font_nr);
1875
1876       DrawTextToTextArea(SX + startx + font_width, SY + starty + font_height,
1877                          level.envelope_text[envelope_nr], FONT_TEXT_1,
1878                          level.envelope_xsize[envelope_nr], i, mask_mode);
1879
1880       redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
1881       BackToFront();
1882
1883       WaitUntilDelayReached(&anim_delay, anim_delay_value);
1884     }
1885   }
1886
1887   if (anim_mode == ANIM_DEFAULT)
1888   {
1889     /* close envelope window horizontally */
1890     for (i = level.envelope_xsize[envelope_nr]; i >= 0; i--)
1891     {
1892       int xsize = i + 2;
1893       int ysize = 2;
1894       int startx = (SXSIZE - xsize * font_width)  / 2;
1895       int starty = (SYSIZE - ysize * font_height) / 2;
1896
1897       SetDrawtoField(DRAW_BUFFERED);
1898
1899       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1900
1901       SetDrawtoField(DRAW_BACKBUFFER);
1902
1903       for (y=0; y < ysize; y++) for (x=0; x < xsize; x++)
1904         DrawEnvelopeBackground(envelope_nr, startx, starty, x, y, xsize, ysize,
1905                                font_nr);
1906
1907       redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
1908       BackToFront();
1909
1910       WaitUntilDelayReached(&anim_delay, anim_delay_value / 2);
1911     }
1912   }
1913
1914   game.envelope_active = FALSE;
1915
1916   SetDrawtoField(DRAW_BUFFERED);
1917
1918   redraw_mask |= REDRAW_FIELD;
1919   BackToFront();
1920 }
1921
1922 #endif
1923
1924 void getMicroGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1925 {
1926   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
1927   int mini_startx = src_bitmap->width * 3 / 4;
1928   int mini_starty = src_bitmap->height * 2 / 3;
1929   int src_x = mini_startx + graphic_info[graphic].src_x / 8;
1930   int src_y = mini_starty + graphic_info[graphic].src_y / 8;
1931
1932   *bitmap = src_bitmap;
1933   *x = src_x;
1934   *y = src_y;
1935 }
1936
1937 void DrawMicroElement(int xpos, int ypos, int element)
1938 {
1939   Bitmap *src_bitmap;
1940   int src_x, src_y;
1941   int graphic = el2preimg(element);
1942
1943   getMicroGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1944   BlitBitmap(src_bitmap, drawto, src_x, src_y, MICRO_TILEX, MICRO_TILEY,
1945              xpos, ypos);
1946 }
1947
1948 void DrawLevel()
1949 {
1950   int x,y;
1951
1952   SetDrawBackgroundMask(REDRAW_NONE);
1953   ClearWindow();
1954
1955   for(x=BX1; x<=BX2; x++)
1956     for(y=BY1; y<=BY2; y++)
1957       DrawScreenField(x, y);
1958
1959   redraw_mask |= REDRAW_FIELD;
1960 }
1961
1962 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
1963 {
1964   int x,y;
1965
1966   for(x=0; x<size_x; x++)
1967     for(y=0; y<size_y; y++)
1968       DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
1969
1970   redraw_mask |= REDRAW_FIELD;
1971 }
1972
1973 static void DrawMicroLevelExt(int xpos, int ypos, int from_x, int from_y)
1974 {
1975   int x, y;
1976
1977   DrawBackground(xpos, ypos, MICROLEV_XSIZE, MICROLEV_YSIZE);
1978
1979   if (lev_fieldx < STD_LEV_FIELDX)
1980     xpos += (STD_LEV_FIELDX - lev_fieldx) / 2 * MICRO_TILEX;
1981   if (lev_fieldy < STD_LEV_FIELDY)
1982     ypos += (STD_LEV_FIELDY - lev_fieldy) / 2 * MICRO_TILEY;
1983
1984   xpos += MICRO_TILEX;
1985   ypos += MICRO_TILEY;
1986
1987   for(x=-1; x<=STD_LEV_FIELDX; x++)
1988   {
1989     for(y=-1; y<=STD_LEV_FIELDY; y++)
1990     {
1991       int lx = from_x + x, ly = from_y + y;
1992
1993       if (lx >= 0 && lx < lev_fieldx && ly >= 0 && ly < lev_fieldy)
1994         DrawMicroElement(xpos + x * MICRO_TILEX, ypos + y * MICRO_TILEY,
1995                          level.field[lx][ly]);
1996       else if (lx >= -1 && lx < lev_fieldx+1 && ly >= -1 && ly < lev_fieldy+1
1997                && BorderElement != EL_EMPTY)
1998         DrawMicroElement(xpos + x * MICRO_TILEX, ypos + y * MICRO_TILEY,
1999                          getBorderElement(lx, ly));
2000     }
2001   }
2002
2003   redraw_mask |= REDRAW_MICROLEVEL;
2004 }
2005
2006 #define MICROLABEL_EMPTY                0
2007 #define MICROLABEL_LEVEL_NAME           1
2008 #define MICROLABEL_CREATED_BY           2
2009 #define MICROLABEL_LEVEL_AUTHOR         3
2010 #define MICROLABEL_IMPORTED_FROM        4
2011 #define MICROLABEL_LEVEL_IMPORT_INFO    5
2012
2013 static void DrawMicroLevelLabelExt(int mode)
2014 {
2015   char label_text[MAX_OUTPUT_LINESIZE + 1];
2016   int max_len_label_text;
2017   int font_nr = FONT_TEXT_2;
2018
2019   if (mode == MICROLABEL_CREATED_BY || mode == MICROLABEL_IMPORTED_FROM)
2020     font_nr = FONT_TEXT_3;
2021
2022   max_len_label_text = SXSIZE / getFontWidth(font_nr);
2023
2024   DrawBackground(SX, MICROLABEL_YPOS, SXSIZE, getFontHeight(font_nr));
2025
2026   strncpy(label_text, (mode == MICROLABEL_LEVEL_NAME ? level.name :
2027                        mode == MICROLABEL_CREATED_BY ? "created by" :
2028                        mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
2029                        mode == MICROLABEL_IMPORTED_FROM ? "imported from" :
2030                        mode == MICROLABEL_LEVEL_IMPORT_INFO ?
2031                        leveldir_current->imported_from : ""),
2032           max_len_label_text);
2033   label_text[max_len_label_text] = '\0';
2034
2035   if (strlen(label_text) > 0)
2036   {
2037     int lxpos = SX + (SXSIZE - getTextWidth(label_text, font_nr)) / 2;
2038     int lypos = MICROLABEL_YPOS;
2039
2040     DrawText(lxpos, lypos, label_text, font_nr);
2041   }
2042
2043   redraw_mask |= REDRAW_MICROLEVEL;
2044 }
2045
2046 void DrawMicroLevel(int xpos, int ypos, boolean restart)
2047 {
2048   static unsigned long scroll_delay = 0;
2049   static unsigned long label_delay = 0;
2050   static int from_x, from_y, scroll_direction;
2051   static int label_state, label_counter;
2052   int last_game_status = game_status;   /* save current game status */
2053
2054   /* force PREVIEW font on preview level */
2055   game_status = GAME_MODE_PSEUDO_PREVIEW;
2056
2057   if (restart)
2058   {
2059     from_x = from_y = 0;
2060     scroll_direction = MV_RIGHT;
2061     label_state = 1;
2062     label_counter = 0;
2063
2064     DrawMicroLevelExt(xpos, ypos, from_x, from_y);
2065     DrawMicroLevelLabelExt(label_state);
2066
2067     /* initialize delay counters */
2068     DelayReached(&scroll_delay, 0);
2069     DelayReached(&label_delay, 0);
2070
2071     if (leveldir_current->name)
2072     {
2073       int text_width = getTextWidth(leveldir_current->name, FONT_TEXT_1);
2074       int lxpos = SX + (SXSIZE - text_width) / 2;
2075       int lypos = SY + 352;
2076
2077       DrawText(lxpos, lypos, leveldir_current->name, FONT_TEXT_1);
2078     }
2079
2080     game_status = last_game_status;     /* restore current game status */
2081
2082     return;
2083   }
2084
2085   /* scroll micro level, if needed */
2086   if ((lev_fieldx > STD_LEV_FIELDX || lev_fieldy > STD_LEV_FIELDY) &&
2087       DelayReached(&scroll_delay, MICROLEVEL_SCROLL_DELAY))
2088   {
2089     switch (scroll_direction)
2090     {
2091       case MV_LEFT:
2092         if (from_x > 0)
2093           from_x--;
2094         else
2095           scroll_direction = MV_UP;
2096         break;
2097
2098       case MV_RIGHT:
2099         if (from_x < lev_fieldx - STD_LEV_FIELDX)
2100           from_x++;
2101         else
2102           scroll_direction = MV_DOWN;
2103         break;
2104
2105       case MV_UP:
2106         if (from_y > 0)
2107           from_y--;
2108         else
2109           scroll_direction = MV_RIGHT;
2110         break;
2111
2112       case MV_DOWN:
2113         if (from_y < lev_fieldy - STD_LEV_FIELDY)
2114           from_y++;
2115         else
2116           scroll_direction = MV_LEFT;
2117         break;
2118
2119       default:
2120         break;
2121     }
2122
2123     DrawMicroLevelExt(xpos, ypos, from_x, from_y);
2124   }
2125
2126   /* redraw micro level label, if needed */
2127   if (strcmp(level.name, NAMELESS_LEVEL_NAME) != 0 &&
2128       strcmp(level.author, ANONYMOUS_NAME) != 0 &&
2129       strcmp(level.author, leveldir_current->name) != 0 &&
2130       DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
2131   {
2132     int max_label_counter = 23;
2133
2134     if (leveldir_current->imported_from != NULL)
2135       max_label_counter += 14;
2136
2137     label_counter = (label_counter + 1) % max_label_counter;
2138     label_state = (label_counter >= 0 && label_counter <= 7 ?
2139                    MICROLABEL_LEVEL_NAME :
2140                    label_counter >= 9 && label_counter <= 12 ?
2141                    MICROLABEL_CREATED_BY :
2142                    label_counter >= 14 && label_counter <= 21 ?
2143                    MICROLABEL_LEVEL_AUTHOR :
2144                    label_counter >= 23 && label_counter <= 26 ?
2145                    MICROLABEL_IMPORTED_FROM :
2146                    label_counter >= 28 && label_counter <= 35 ?
2147                    MICROLABEL_LEVEL_IMPORT_INFO : MICROLABEL_EMPTY);
2148     DrawMicroLevelLabelExt(label_state);
2149   }
2150
2151   game_status = last_game_status;       /* restore current game status */
2152 }
2153
2154 void WaitForEventToContinue()
2155 {
2156   boolean still_wait = TRUE;
2157
2158   /* simulate releasing mouse button over last gadget, if still pressed */
2159   if (button_status)
2160     HandleGadgets(-1, -1, 0);
2161
2162   button_status = MB_RELEASED;
2163
2164   while (still_wait)
2165   {
2166     if (PendingEvent())
2167     {
2168       Event event;
2169
2170       NextEvent(&event);
2171
2172       switch (event.type)
2173       {
2174         case EVENT_BUTTONPRESS:
2175         case EVENT_KEYPRESS:
2176           still_wait = FALSE;
2177           break;
2178
2179         case EVENT_KEYRELEASE:
2180           ClearPlayerAction();
2181           break;
2182
2183         default:
2184           HandleOtherEvents(&event);
2185           break;
2186       }
2187     }
2188     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
2189     {
2190       still_wait = FALSE;
2191     }
2192
2193     DoAnimation();
2194
2195     /* don't eat all CPU time */
2196     Delay(10);
2197   }
2198 }
2199
2200 #define MAX_REQUEST_LINES               13
2201 #define MAX_REQUEST_LINE_LEN            7
2202
2203 boolean Request(char *text, unsigned int req_state)
2204 {
2205   int mx, my, ty, result = -1;
2206   unsigned int old_door_state;
2207   int last_game_status = game_status;   /* save current game status */
2208
2209 #if 1
2210   SetMouseCursor(CURSOR_DEFAULT);
2211 #endif
2212
2213 #if defined(PLATFORM_UNIX)
2214   /* pause network game while waiting for request to answer */
2215   if (options.network &&
2216       game_status == GAME_MODE_PLAYING &&
2217       req_state & REQUEST_WAIT_FOR)
2218     SendToServer_PausePlaying();
2219 #endif
2220
2221   old_door_state = GetDoorState();
2222
2223   /* simulate releasing mouse button over last gadget, if still pressed */
2224   if (button_status)
2225     HandleGadgets(-1, -1, 0);
2226
2227   UnmapAllGadgets();
2228
2229   CloseDoor(DOOR_CLOSE_1);
2230
2231   /* save old door content */
2232   BlitBitmap(bitmap_db_door, bitmap_db_door,
2233              DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE,
2234              DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY1);
2235
2236   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
2237
2238   /* clear door drawing field */
2239   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2240
2241   /* force DOOR font on preview level */
2242   game_status = GAME_MODE_PSEUDO_DOOR;
2243
2244   /* write text for request */
2245   for(ty=0; ty < MAX_REQUEST_LINES; ty++)
2246   {
2247     char text_line[MAX_REQUEST_LINE_LEN + 1];
2248     int tx, tl, tc;
2249
2250     if (!*text)
2251       break;
2252
2253     for(tl=0,tx=0; tx < MAX_REQUEST_LINE_LEN; tl++,tx++)
2254     {
2255       tc = *(text + tx);
2256       if (!tc || tc == ' ')
2257         break;
2258     }
2259
2260     if (!tl)
2261     { 
2262       text++; 
2263       ty--; 
2264       continue; 
2265     }
2266
2267     strncpy(text_line, text, tl);
2268     text_line[tl] = 0;
2269
2270     DrawText(DX + (DXSIZE - tl * getFontWidth(FONT_TEXT_2)) / 2,
2271              DY + 8 + ty * (getFontHeight(FONT_TEXT_2) + 2),
2272              text_line, FONT_TEXT_2);
2273
2274     text += tl + (tc == ' ' ? 1 : 0);
2275   }
2276
2277   game_status = last_game_status;       /* restore current game status */
2278
2279   if (req_state & REQ_ASK)
2280   {
2281     MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2282     MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2283   }
2284   else if (req_state & REQ_CONFIRM)
2285   {
2286     MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2287   }
2288   else if (req_state & REQ_PLAYER)
2289   {
2290     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2291     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2292     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2293     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2294   }
2295
2296   /* copy request gadgets to door backbuffer */
2297   BlitBitmap(drawto, bitmap_db_door,
2298              DX, DY, DXSIZE, DYSIZE,
2299              DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2300
2301   OpenDoor(DOOR_OPEN_1);
2302
2303 #if 0
2304   ClearEventQueue();
2305 #endif
2306
2307   if (!(req_state & REQUEST_WAIT_FOR))
2308   {
2309     SetDrawBackgroundMask(REDRAW_FIELD);
2310
2311     return FALSE;
2312   }
2313
2314   if (game_status != GAME_MODE_MAIN)
2315     InitAnimation();
2316
2317   button_status = MB_RELEASED;
2318
2319   request_gadget_id = -1;
2320
2321   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
2322
2323 #if 0
2324   SetMouseCursor(CURSOR_DEFAULT);
2325 #endif
2326
2327   while(result < 0)
2328   {
2329     if (PendingEvent())
2330     {
2331       Event event;
2332
2333       NextEvent(&event);
2334
2335       switch(event.type)
2336       {
2337         case EVENT_BUTTONPRESS:
2338         case EVENT_BUTTONRELEASE:
2339         case EVENT_MOTIONNOTIFY:
2340         {
2341           if (event.type == EVENT_MOTIONNOTIFY)
2342           {
2343             if (!PointerInWindow(window))
2344               continue; /* window and pointer are on different screens */
2345
2346             if (!button_status)
2347               continue;
2348
2349             motion_status = TRUE;
2350             mx = ((MotionEvent *) &event)->x;
2351             my = ((MotionEvent *) &event)->y;
2352           }
2353           else
2354           {
2355             motion_status = FALSE;
2356             mx = ((ButtonEvent *) &event)->x;
2357             my = ((ButtonEvent *) &event)->y;
2358             if (event.type == EVENT_BUTTONPRESS)
2359               button_status = ((ButtonEvent *) &event)->button;
2360             else
2361               button_status = MB_RELEASED;
2362           }
2363
2364           /* this sets 'request_gadget_id' */
2365           HandleGadgets(mx, my, button_status);
2366
2367           switch(request_gadget_id)
2368           {
2369             case TOOL_CTRL_ID_YES:
2370               result = TRUE;
2371               break;
2372             case TOOL_CTRL_ID_NO:
2373               result = FALSE;
2374               break;
2375             case TOOL_CTRL_ID_CONFIRM:
2376               result = TRUE | FALSE;
2377               break;
2378
2379             case TOOL_CTRL_ID_PLAYER_1:
2380               result = 1;
2381               break;
2382             case TOOL_CTRL_ID_PLAYER_2:
2383               result = 2;
2384               break;
2385             case TOOL_CTRL_ID_PLAYER_3:
2386               result = 3;
2387               break;
2388             case TOOL_CTRL_ID_PLAYER_4:
2389               result = 4;
2390               break;
2391
2392             default:
2393               break;
2394           }
2395
2396           break;
2397         }
2398
2399         case EVENT_KEYPRESS:
2400           switch(GetEventKey((KeyEvent *)&event, TRUE))
2401           {
2402             case KSYM_Return:
2403               result = 1;
2404               break;
2405
2406             case KSYM_Escape:
2407               result = 0;
2408               break;
2409
2410             default:
2411               break;
2412           }
2413           if (req_state & REQ_PLAYER)
2414             result = 0;
2415           break;
2416
2417         case EVENT_KEYRELEASE:
2418           ClearPlayerAction();
2419           break;
2420
2421         default:
2422           HandleOtherEvents(&event);
2423           break;
2424       }
2425     }
2426     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
2427     {
2428       int joy = AnyJoystick();
2429
2430       if (joy & JOY_BUTTON_1)
2431         result = 1;
2432       else if (joy & JOY_BUTTON_2)
2433         result = 0;
2434     }
2435
2436     DoAnimation();
2437
2438     /* don't eat all CPU time */
2439     Delay(10);
2440   }
2441
2442   if (game_status != GAME_MODE_MAIN)
2443     StopAnimation();
2444
2445   UnmapToolButtons();
2446
2447   if (!(req_state & REQ_STAY_OPEN))
2448   {
2449     CloseDoor(DOOR_CLOSE_1);
2450
2451     if (!(req_state & REQ_STAY_CLOSED) && (old_door_state & DOOR_OPEN_1))
2452     {
2453       BlitBitmap(bitmap_db_door, bitmap_db_door,
2454                  DOOR_GFX_PAGEX2,DOOR_GFX_PAGEY1, DXSIZE,DYSIZE,
2455                  DOOR_GFX_PAGEX1,DOOR_GFX_PAGEY1);
2456       OpenDoor(DOOR_OPEN_1);
2457     }
2458   }
2459
2460   RemapAllGadgets();
2461
2462   SetDrawBackgroundMask(REDRAW_FIELD);
2463
2464 #if defined(PLATFORM_UNIX)
2465   /* continue network game after request */
2466   if (options.network &&
2467       game_status == GAME_MODE_PLAYING &&
2468       req_state & REQUEST_WAIT_FOR)
2469     SendToServer_ContinuePlaying();
2470 #endif
2471
2472   return result;
2473 }
2474
2475 unsigned int OpenDoor(unsigned int door_state)
2476 {
2477   unsigned int new_door_state;
2478
2479   if (door_state & DOOR_COPY_BACK)
2480   {
2481     BlitBitmap(bitmap_db_door, bitmap_db_door,
2482                DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE + VYSIZE,
2483                DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2484     door_state &= ~DOOR_COPY_BACK;
2485   }
2486
2487   new_door_state = MoveDoor(door_state);
2488
2489   return(new_door_state);
2490 }
2491
2492 unsigned int CloseDoor(unsigned int door_state)
2493 {
2494   unsigned int new_door_state;
2495
2496   BlitBitmap(backbuffer, bitmap_db_door,
2497              DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2498   BlitBitmap(backbuffer, bitmap_db_door,
2499              VX, VY, VXSIZE, VYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY2);
2500
2501   new_door_state = MoveDoor(door_state);
2502
2503   return(new_door_state);
2504 }
2505
2506 unsigned int GetDoorState()
2507 {
2508   return MoveDoor(DOOR_GET_STATE);
2509 }
2510
2511 unsigned int SetDoorState(unsigned int door_state)
2512 {
2513   return MoveDoor(door_state | DOOR_SET_STATE);
2514 }
2515
2516 unsigned int MoveDoor(unsigned int door_state)
2517 {
2518   static int door1 = DOOR_OPEN_1;
2519   static int door2 = DOOR_CLOSE_2;
2520   unsigned long door_delay = 0;
2521   unsigned long door_delay_value;
2522   int stepsize = 1;
2523
2524   if (door_state == DOOR_GET_STATE)
2525     return(door1 | door2);
2526
2527   if (door_state & DOOR_SET_STATE)
2528   {
2529     if (door_state & DOOR_ACTION_1)
2530       door1 = door_state & DOOR_ACTION_1;
2531     if (door_state & DOOR_ACTION_2)
2532       door2 = door_state & DOOR_ACTION_2;
2533
2534     return(door1 | door2);
2535   }
2536
2537   if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
2538     door_state &= ~DOOR_OPEN_1;
2539   else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
2540     door_state &= ~DOOR_CLOSE_1;
2541   if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
2542     door_state &= ~DOOR_OPEN_2;
2543   else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
2544     door_state &= ~DOOR_CLOSE_2;
2545
2546   door_delay_value = (door_state & DOOR_ACTION_1 ? door_1.step_delay :
2547                       door_2.step_delay);
2548
2549   if (setup.quick_doors)
2550   {
2551     stepsize = 20;              /* must be choosen to always draw last frame */
2552     door_delay_value = 0;
2553
2554     StopSound(SND_DOOR_OPENING);
2555     StopSound(SND_DOOR_CLOSING);
2556   }
2557
2558   if (global.autoplay_leveldir)
2559   {
2560     door_state |= DOOR_NO_DELAY;
2561     door_state &= ~DOOR_CLOSE_ALL;
2562   }
2563
2564   if (door_state & DOOR_ACTION)
2565   {
2566     boolean door_1_done = !(door_state & DOOR_ACTION_1);
2567     boolean door_2_done = !(door_state & DOOR_ACTION_2);
2568     int start = ((door_state & DOOR_NO_DELAY) ? DXSIZE : 0);
2569     int end = (door_state & DOOR_ACTION_1 &&
2570                door_1.anim_mode == ANIM_VERTICAL ? DYSIZE : DXSIZE);
2571     int x;
2572
2573     if (!(door_state & DOOR_NO_DELAY))
2574     {
2575       /* opening door sound has priority over simultaneously closing door */
2576       if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
2577         PlaySoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
2578       else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
2579         PlaySoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
2580     }
2581
2582     for(x = start; x <= end && !(door_1_done && door_2_done); x += stepsize)
2583     {
2584       Bitmap *bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2585       GC gc = bitmap->stored_clip_gc;
2586
2587       if (door_state & DOOR_ACTION_1)
2588       {
2589         int a = MIN(x * door_1.step_offset, end);
2590         int i = (door_state & DOOR_OPEN_1 ? end - a : a);
2591
2592         if (x <= a)
2593         {
2594           BlitBitmap(bitmap_db_door, drawto,
2595                      DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1 + i / 2,
2596                      DXSIZE,DYSIZE - i / 2, DX, DY);
2597
2598           ClearRectangle(drawto, DX, DY + DYSIZE - i / 2, DXSIZE, i / 2);
2599         }
2600
2601         if (door_1.anim_mode == ANIM_HORIZONTAL && x <= DXSIZE)
2602         {
2603           int src1_x = DXSIZE,          src1_y = DOOR_GFX_PAGEY1;
2604           int dst1_x = DX + DXSIZE - i, dst1_y = DY;
2605           int src2_x = DXSIZE - i,      src2_y = DOOR_GFX_PAGEY1;
2606           int dst2_x = DX,              dst2_y = DY;
2607           int width = i, height = DYSIZE;
2608
2609           SetClipOrigin(bitmap, gc, dst1_x - src1_x, dst1_y - src1_y);
2610           BlitBitmapMasked(bitmap, drawto, src1_x, src1_y, width, height,
2611                            dst1_x, dst1_y);
2612
2613           SetClipOrigin(bitmap, gc, dst2_x - src2_x, dst2_y - src2_y);
2614           BlitBitmapMasked(bitmap, drawto, src2_x, src2_y, width, height,
2615                            dst2_x, dst2_y);
2616         }
2617         else if (door_1.anim_mode == ANIM_VERTICAL && x <= DYSIZE)
2618         {
2619           int src1_x = DXSIZE,          src1_y = DOOR_GFX_PAGEY1;
2620           int dst1_x = DX,              dst1_y = DY + DYSIZE - i;
2621           int src2_x = 0,               src2_y = DOOR_GFX_PAGEY1 + DYSIZE - i;
2622           int dst2_x = DX,              dst2_y = DY;
2623           int width = DXSIZE, height = i;
2624
2625           SetClipOrigin(bitmap, gc, dst1_x - src1_x, dst1_y - src1_y);
2626           BlitBitmapMasked(bitmap, drawto, src1_x, src1_y, width, height,
2627                            dst1_x, dst1_y);
2628
2629           SetClipOrigin(bitmap, gc, dst2_x - src2_x, dst2_y - src2_y);
2630           BlitBitmapMasked(bitmap, drawto, src2_x, src2_y, width, height,
2631                            dst2_x, dst2_y);
2632         }
2633         else if (x <= DXSIZE)   /* ANIM_DEFAULT */
2634         {
2635           int j = (door_1.anim_mode == ANIM_DEFAULT ? (DXSIZE - i) / 3 : 0);
2636
2637           SetClipOrigin(bitmap, gc, DX - i, (DY + j) - DOOR_GFX_PAGEY1);
2638           BlitBitmapMasked(bitmap, drawto,
2639                            DXSIZE, DOOR_GFX_PAGEY1, i, 77,
2640                            DX + DXSIZE - i, DY + j);
2641           BlitBitmapMasked(bitmap, drawto,
2642                            DXSIZE, DOOR_GFX_PAGEY1 + 140, i, 63,
2643                            DX + DXSIZE - i, DY + 140 + j);
2644           SetClipOrigin(bitmap, gc, DX - DXSIZE + i,
2645                         DY - (DOOR_GFX_PAGEY1 + j));
2646           BlitBitmapMasked(bitmap, drawto,
2647                            DXSIZE - i, DOOR_GFX_PAGEY1 + j, i, 77 - j,
2648                            DX, DY);
2649           BlitBitmapMasked(bitmap, drawto,
2650                            DXSIZE-i, DOOR_GFX_PAGEY1 + 140, i, 63,
2651                            DX, DY + 140 - j);
2652
2653           BlitBitmapMasked(bitmap, drawto,
2654                            DXSIZE - i, DOOR_GFX_PAGEY1 + 77, i, 63,
2655                            DX, DY + 77 - j);
2656           BlitBitmapMasked(bitmap, drawto,
2657                            DXSIZE - i, DOOR_GFX_PAGEY1 + 203, i, 77,
2658                            DX, DY + 203 - j);
2659           SetClipOrigin(bitmap, gc, DX - i, (DY + j) - DOOR_GFX_PAGEY1);
2660           BlitBitmapMasked(bitmap, drawto,
2661                            DXSIZE, DOOR_GFX_PAGEY1 + 77, i, 63,
2662                            DX + DXSIZE - i, DY + 77 + j);
2663           BlitBitmapMasked(bitmap, drawto,
2664                            DXSIZE, DOOR_GFX_PAGEY1 + 203, i, 77 - j,
2665                            DX + DXSIZE - i, DY + 203 + j);
2666         }
2667
2668         redraw_mask |= REDRAW_DOOR_1;
2669         door_1_done = (a == end);
2670       }
2671
2672       if (door_state & DOOR_ACTION_2)
2673       {
2674         int a = MIN(x * door_2.step_offset, VXSIZE);
2675         int i = (door_state & DOOR_OPEN_2 ? VXSIZE - a : a);
2676
2677         if (x <= VYSIZE)
2678         {
2679           BlitBitmap(bitmap_db_door, drawto,
2680                      DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY2 + i / 2,
2681                      VXSIZE, VYSIZE - i / 2, VX, VY);
2682
2683           ClearRectangle(drawto, VX, VY + VYSIZE - i / 2, VXSIZE, i / 2);
2684         }
2685
2686         if (door_2.anim_mode == ANIM_HORIZONTAL && x <= VXSIZE)
2687         {
2688           int src1_x = VXSIZE,          src1_y = DOOR_GFX_PAGEY2;
2689           int dst1_x = VX + VXSIZE - i, dst1_y = VY;
2690           int src2_x = VXSIZE - i,      src2_y = DOOR_GFX_PAGEY2;
2691           int dst2_x = VX,              dst2_y = VY;
2692           int width = i, height = VYSIZE;
2693
2694           SetClipOrigin(bitmap, gc, dst1_x - src1_x, dst1_y - src1_y);
2695           BlitBitmapMasked(bitmap, drawto, src1_x, src1_y, width, height,
2696                            dst1_x, dst1_y);
2697
2698           SetClipOrigin(bitmap, gc, dst2_x - src2_x, dst2_y - src2_y);
2699           BlitBitmapMasked(bitmap, drawto, src2_x, src2_y, width, height,
2700                            dst2_x, dst2_y);
2701         }
2702         else if (door_2.anim_mode == ANIM_VERTICAL && x <= VYSIZE)
2703         {
2704           int src1_x = VXSIZE,          src1_y = DOOR_GFX_PAGEY2;
2705           int dst1_x = VX,              dst1_y = VY + VYSIZE - i;
2706           int src2_x = 0,               src2_y = DOOR_GFX_PAGEY2 + VYSIZE - i;
2707           int dst2_x = VX,              dst2_y = VY;
2708           int width = VXSIZE, height = i;
2709
2710           SetClipOrigin(bitmap, gc, dst1_x - src1_x, dst1_y - src1_y);
2711           BlitBitmapMasked(bitmap, drawto, src1_x, src1_y, width, height,
2712                            dst1_x, dst1_y);
2713
2714           SetClipOrigin(bitmap, gc, dst2_x - src2_x, dst2_y - src2_y);
2715           BlitBitmapMasked(bitmap, drawto, src2_x, src2_y, width, height,
2716                            dst2_x, dst2_y);
2717         }
2718         else if (x <= VXSIZE)   /* ANIM_DEFAULT */
2719         {
2720           int j = (door_2.anim_mode == ANIM_DEFAULT ? (VXSIZE - i) / 3 : 0);
2721
2722           SetClipOrigin(bitmap, gc, VX - i, (VY + j) - DOOR_GFX_PAGEY2);
2723           BlitBitmapMasked(bitmap, drawto,
2724                            VXSIZE, DOOR_GFX_PAGEY2, i, VYSIZE / 2,
2725                            VX + VXSIZE - i, VY + j);
2726           SetClipOrigin(bitmap, gc,
2727                         VX - VXSIZE + i, VY - (DOOR_GFX_PAGEY2 + j));
2728           BlitBitmapMasked(bitmap, drawto,
2729                            VXSIZE - i, DOOR_GFX_PAGEY2 + j, i, VYSIZE / 2 - j,
2730                            VX, VY);
2731
2732           BlitBitmapMasked(bitmap, drawto,
2733                            VXSIZE - i, DOOR_GFX_PAGEY2 + VYSIZE / 2,
2734                            i, VYSIZE / 2, VX, VY + VYSIZE / 2 - j);
2735           SetClipOrigin(bitmap, gc, VX - i, (VY + j) - DOOR_GFX_PAGEY2);
2736           BlitBitmapMasked(bitmap, drawto,
2737                            VXSIZE, DOOR_GFX_PAGEY2 + VYSIZE / 2,
2738                            i, VYSIZE / 2 - j,
2739                            VX + VXSIZE - i, VY + VYSIZE / 2 + j);
2740         }
2741
2742         redraw_mask |= REDRAW_DOOR_2;
2743         door_2_done = (a == VXSIZE);
2744       }
2745
2746       BackToFront();
2747
2748       if (game_status == GAME_MODE_MAIN)
2749         DoAnimation();
2750
2751       if (!(door_state & DOOR_NO_DELAY))
2752         WaitUntilDelayReached(&door_delay, door_delay_value);
2753     }
2754   }
2755
2756   if (setup.quick_doors)
2757   {
2758     StopSound(SND_DOOR_OPENING);
2759     StopSound(SND_DOOR_CLOSING);
2760   }
2761
2762   if (door_state & DOOR_ACTION_1)
2763     door1 = door_state & DOOR_ACTION_1;
2764   if (door_state & DOOR_ACTION_2)
2765     door2 = door_state & DOOR_ACTION_2;
2766
2767   return (door1 | door2);
2768 }
2769
2770 void DrawSpecialEditorDoor()
2771 {
2772   /* draw bigger toolbox window */
2773   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2774              DOOR_GFX_PAGEX7, 0, EXSIZE + 8, 8,
2775              EX - 4, EY - 12);
2776   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
2777              EX - 4, VY - 4, EXSIZE + 8, EYSIZE - VYSIZE + 4,
2778              EX - 4, EY - 4);
2779
2780   redraw_mask |= REDRAW_ALL;
2781 }
2782
2783 void UndrawSpecialEditorDoor()
2784 {
2785   /* draw normal tape recorder window */
2786   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
2787              EX - 4, EY - 12, EXSIZE + 8, EYSIZE - VYSIZE + 12,
2788              EX - 4, EY - 12);
2789
2790   redraw_mask |= REDRAW_ALL;
2791 }
2792
2793
2794 /* ---------- new tool button stuff ---------------------------------------- */
2795
2796 /* graphic position values for tool buttons */
2797 #define TOOL_BUTTON_YES_XPOS            2
2798 #define TOOL_BUTTON_YES_YPOS            250
2799 #define TOOL_BUTTON_YES_GFX_YPOS        0
2800 #define TOOL_BUTTON_YES_XSIZE           46
2801 #define TOOL_BUTTON_YES_YSIZE           28
2802 #define TOOL_BUTTON_NO_XPOS             52
2803 #define TOOL_BUTTON_NO_YPOS             TOOL_BUTTON_YES_YPOS
2804 #define TOOL_BUTTON_NO_GFX_YPOS         TOOL_BUTTON_YES_GFX_YPOS
2805 #define TOOL_BUTTON_NO_XSIZE            TOOL_BUTTON_YES_XSIZE
2806 #define TOOL_BUTTON_NO_YSIZE            TOOL_BUTTON_YES_YSIZE
2807 #define TOOL_BUTTON_CONFIRM_XPOS        TOOL_BUTTON_YES_XPOS
2808 #define TOOL_BUTTON_CONFIRM_YPOS        TOOL_BUTTON_YES_YPOS
2809 #define TOOL_BUTTON_CONFIRM_GFX_YPOS    30
2810 #define TOOL_BUTTON_CONFIRM_XSIZE       96
2811 #define TOOL_BUTTON_CONFIRM_YSIZE       TOOL_BUTTON_YES_YSIZE
2812 #define TOOL_BUTTON_PLAYER_XSIZE        30
2813 #define TOOL_BUTTON_PLAYER_YSIZE        30
2814 #define TOOL_BUTTON_PLAYER_GFX_XPOS     5
2815 #define TOOL_BUTTON_PLAYER_GFX_YPOS     185
2816 #define TOOL_BUTTON_PLAYER_XPOS         (5 + TOOL_BUTTON_PLAYER_XSIZE / 2)
2817 #define TOOL_BUTTON_PLAYER_YPOS         (215 - TOOL_BUTTON_PLAYER_YSIZE / 2)
2818 #define TOOL_BUTTON_PLAYER1_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
2819                                          + 0 * TOOL_BUTTON_PLAYER_XSIZE)
2820 #define TOOL_BUTTON_PLAYER2_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
2821                                          + 1 * TOOL_BUTTON_PLAYER_XSIZE)
2822 #define TOOL_BUTTON_PLAYER3_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
2823                                          + 0 * TOOL_BUTTON_PLAYER_XSIZE)
2824 #define TOOL_BUTTON_PLAYER4_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
2825                                          + 1 * TOOL_BUTTON_PLAYER_XSIZE)
2826 #define TOOL_BUTTON_PLAYER1_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
2827                                          + 0 * TOOL_BUTTON_PLAYER_YSIZE)
2828 #define TOOL_BUTTON_PLAYER2_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
2829                                          + 0 * TOOL_BUTTON_PLAYER_YSIZE)
2830 #define TOOL_BUTTON_PLAYER3_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
2831                                          + 1 * TOOL_BUTTON_PLAYER_YSIZE)
2832 #define TOOL_BUTTON_PLAYER4_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
2833                                          + 1 * TOOL_BUTTON_PLAYER_YSIZE)
2834
2835 static struct
2836 {
2837   int xpos, ypos;
2838   int x, y;
2839   int width, height;
2840   int gadget_id;
2841   char *infotext;
2842 } toolbutton_info[NUM_TOOL_BUTTONS] =
2843 {
2844   {
2845     TOOL_BUTTON_YES_XPOS,       TOOL_BUTTON_YES_GFX_YPOS,
2846     TOOL_BUTTON_YES_XPOS,       TOOL_BUTTON_YES_YPOS,
2847     TOOL_BUTTON_YES_XSIZE,      TOOL_BUTTON_YES_YSIZE,
2848     TOOL_CTRL_ID_YES,
2849     "yes"
2850   },
2851   {
2852     TOOL_BUTTON_NO_XPOS,        TOOL_BUTTON_NO_GFX_YPOS,
2853     TOOL_BUTTON_NO_XPOS,        TOOL_BUTTON_NO_YPOS,
2854     TOOL_BUTTON_NO_XSIZE,       TOOL_BUTTON_NO_YSIZE,
2855     TOOL_CTRL_ID_NO,
2856     "no"
2857   },
2858   {
2859     TOOL_BUTTON_CONFIRM_XPOS,   TOOL_BUTTON_CONFIRM_GFX_YPOS,
2860     TOOL_BUTTON_CONFIRM_XPOS,   TOOL_BUTTON_CONFIRM_YPOS,
2861     TOOL_BUTTON_CONFIRM_XSIZE,  TOOL_BUTTON_CONFIRM_YSIZE,
2862     TOOL_CTRL_ID_CONFIRM,
2863     "confirm"
2864   },
2865   {
2866     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
2867     TOOL_BUTTON_PLAYER1_XPOS,   TOOL_BUTTON_PLAYER1_YPOS,
2868     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
2869     TOOL_CTRL_ID_PLAYER_1,
2870     "player 1"
2871   },
2872   {
2873     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
2874     TOOL_BUTTON_PLAYER2_XPOS,   TOOL_BUTTON_PLAYER2_YPOS,
2875     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
2876     TOOL_CTRL_ID_PLAYER_2,
2877     "player 2"
2878   },
2879   {
2880     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
2881     TOOL_BUTTON_PLAYER3_XPOS,   TOOL_BUTTON_PLAYER3_YPOS,
2882     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
2883     TOOL_CTRL_ID_PLAYER_3,
2884     "player 3"
2885   },
2886   {
2887     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
2888     TOOL_BUTTON_PLAYER4_XPOS,   TOOL_BUTTON_PLAYER4_YPOS,
2889     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
2890     TOOL_CTRL_ID_PLAYER_4,
2891     "player 4"
2892   }
2893 };
2894
2895 void CreateToolButtons()
2896 {
2897   int i;
2898
2899   for (i=0; i<NUM_TOOL_BUTTONS; i++)
2900   {
2901     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2902     Bitmap *deco_bitmap = None;
2903     int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
2904     struct GadgetInfo *gi;
2905     unsigned long event_mask;
2906     int gd_xoffset, gd_yoffset;
2907     int gd_x1, gd_x2, gd_y;
2908     int id = i;
2909
2910     event_mask = GD_EVENT_RELEASED;
2911
2912     gd_xoffset = toolbutton_info[i].xpos;
2913     gd_yoffset = toolbutton_info[i].ypos;
2914     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
2915     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
2916     gd_y = DOOR_GFX_PAGEY1 + gd_yoffset;
2917
2918     if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
2919     {
2920       int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
2921
2922       getMiniGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr),
2923                            &deco_bitmap, &deco_x, &deco_y);
2924       deco_xpos = (toolbutton_info[i].width - MINI_TILEX) / 2;
2925       deco_ypos = (toolbutton_info[i].height - MINI_TILEY) / 2;
2926     }
2927
2928     gi = CreateGadget(GDI_CUSTOM_ID, id,
2929                       GDI_INFO_TEXT, toolbutton_info[i].infotext,
2930                       GDI_X, DX + toolbutton_info[i].x,
2931                       GDI_Y, DY + toolbutton_info[i].y,
2932                       GDI_WIDTH, toolbutton_info[i].width,
2933                       GDI_HEIGHT, toolbutton_info[i].height,
2934                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
2935                       GDI_STATE, GD_BUTTON_UNPRESSED,
2936                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y,
2937                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y,
2938                       GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
2939                       GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
2940                       GDI_DECORATION_SIZE, MINI_TILEX, MINI_TILEY,
2941                       GDI_DECORATION_SHIFTING, 1, 1,
2942                       GDI_EVENT_MASK, event_mask,
2943                       GDI_CALLBACK_ACTION, HandleToolButtons,
2944                       GDI_END);
2945
2946     if (gi == NULL)
2947       Error(ERR_EXIT, "cannot create gadget");
2948
2949     tool_gadget[id] = gi;
2950   }
2951 }
2952
2953 void FreeToolButtons()
2954 {
2955   int i;
2956
2957   for (i=0; i<NUM_TOOL_BUTTONS; i++)
2958     FreeGadget(tool_gadget[i]);
2959 }
2960
2961 static void UnmapToolButtons()
2962 {
2963   int i;
2964
2965   for (i=0; i<NUM_TOOL_BUTTONS; i++)
2966     UnmapGadget(tool_gadget[i]);
2967 }
2968
2969 static void HandleToolButtons(struct GadgetInfo *gi)
2970 {
2971   request_gadget_id = gi->custom_id;
2972 }
2973
2974 int get_next_element(int element)
2975 {
2976   switch(element)
2977   {
2978     case EL_QUICKSAND_FILLING:          return EL_QUICKSAND_FULL;
2979     case EL_QUICKSAND_EMPTYING:         return EL_QUICKSAND_EMPTY;
2980     case EL_MAGIC_WALL_FILLING:         return EL_MAGIC_WALL_FULL;
2981     case EL_MAGIC_WALL_EMPTYING:        return EL_MAGIC_WALL_ACTIVE;
2982     case EL_BD_MAGIC_WALL_FILLING:      return EL_BD_MAGIC_WALL_FULL;
2983     case EL_BD_MAGIC_WALL_EMPTYING:     return EL_BD_MAGIC_WALL_ACTIVE;
2984     case EL_AMOEBA_DROPPING:            return EL_AMOEBA_WET;
2985
2986     default:                            return element;
2987   }
2988 }
2989
2990 int el_act_dir2img(int element, int action, int direction)
2991 {
2992   element = GFX_ELEMENT(element);
2993   direction = MV_DIR_BIT(direction);
2994
2995   return element_info[element].direction_graphic[action][direction];
2996 }
2997
2998 static int el_act_dir2crm(int element, int action, int direction)
2999 {
3000   element = GFX_ELEMENT(element);
3001   direction = MV_DIR_BIT(direction);
3002
3003   return element_info[element].direction_crumbled[action][direction];
3004 }
3005
3006 int el_act2img(int element, int action)
3007 {
3008   element = GFX_ELEMENT(element);
3009
3010   return element_info[element].graphic[action];
3011 }
3012
3013 int el_act2crm(int element, int action)
3014 {
3015   element = GFX_ELEMENT(element);
3016
3017   return element_info[element].crumbled[action];
3018 }
3019
3020 int el_dir2img(int element, int direction)
3021 {
3022   element = GFX_ELEMENT(element);
3023
3024   return el_act_dir2img(element, ACTION_DEFAULT, direction);
3025 }
3026
3027 int el2img(int element)
3028 {
3029   element = GFX_ELEMENT(element);
3030
3031   return element_info[element].graphic[ACTION_DEFAULT];
3032 }
3033
3034 int el2edimg(int element)
3035 {
3036   element = GFX_ELEMENT(element);
3037
3038   return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
3039 }
3040
3041 int el2preimg(int element)
3042 {
3043   element = GFX_ELEMENT(element);
3044
3045   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
3046 }