rnd-20031109-2-src
[rocksndiamonds.git] / src / tools.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * tools.c                                                  *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "tools.h"
17 #include "game.h"
18 #include "events.h"
19 #include "cartoons.h"
20 #include "network.h"
21 #include "tape.h"
22
23 /* tool button identifiers */
24 #define TOOL_CTRL_ID_YES        0
25 #define TOOL_CTRL_ID_NO         1
26 #define TOOL_CTRL_ID_CONFIRM    2
27 #define TOOL_CTRL_ID_PLAYER_1   3
28 #define TOOL_CTRL_ID_PLAYER_2   4
29 #define TOOL_CTRL_ID_PLAYER_3   5
30 #define TOOL_CTRL_ID_PLAYER_4   6
31
32 #define NUM_TOOL_BUTTONS        7
33
34 /* forward declaration for internal use */
35 static void UnmapToolButtons();
36 static void HandleToolButtons(struct GadgetInfo *);
37 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], GfxDir[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], GfxDir[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 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
533 {
534   if (player->use_murphy_graphic)
535   {
536     /* this works only because currently only one player can be "murphy" ... */
537     static int last_horizontal_dir = MV_LEFT;
538     int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
539
540     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
541       last_horizontal_dir = move_dir;
542
543     if (graphic == IMG_SP_MURPHY)       /* undefined => use special graphic */
544     {
545       int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
546
547       graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
548     }
549
550     return graphic;
551   }
552   else
553     return el_act_dir2img(player->element_nr, player->GfxAction, move_dir);
554 }
555
556 void DrawAllPlayers()
557 {
558   int i;
559
560   for(i=0; i<MAX_PLAYERS; i++)
561     if (stored_player[i].active)
562       DrawPlayer(&stored_player[i]);
563 }
564
565 void DrawPlayerField(int x, int y)
566 {
567   if (!IS_PLAYER(x, y))
568     return;
569
570   DrawPlayer(PLAYERINFO(x, y));
571 }
572
573 void DrawPlayer(struct PlayerInfo *player)
574 {
575   int jx = player->jx;
576   int jy = player->jy;
577   int move_dir = player->MovDir;
578 #if 0
579   int last_jx = player->last_jx;
580   int last_jy = player->last_jy;
581   int next_jx = jx + (jx - last_jx);
582   int next_jy = jy + (jy - last_jy);
583   boolean player_is_moving = (last_jx != jx || last_jy != jy ? TRUE : FALSE);
584 #else
585   int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
586   int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? +1 : 0);
587   int last_jx = (player->is_moving ? jx - dx : jx);
588   int last_jy = (player->is_moving ? jy - dy : jy);
589   int next_jx = jx + dx;
590   int next_jy = jy + dy;
591   boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
592 #endif
593   int sx = SCREENX(jx), sy = SCREENY(jy);
594   int sxx = 0, syy = 0;
595   int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
596   int graphic;
597   int action = ACTION_DEFAULT;
598   int last_player_graphic = getPlayerGraphic(player, move_dir);
599   int last_player_frame = player->Frame;
600   int frame = 0;
601
602   if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
603     return;
604
605 #if DEBUG
606   if (!IN_LEV_FIELD(jx, jy))
607   {
608     printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
609     printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
610     printf("DrawPlayerField(): This should never happen!\n");
611     return;
612   }
613 #endif
614
615   if (element == EL_EXPLOSION)
616     return;
617
618   action = (player->is_pushing    ? ACTION_PUSHING    :
619             player->is_digging    ? ACTION_DIGGING    :
620             player->is_collecting ? ACTION_COLLECTING :
621             player->is_moving     ? ACTION_MOVING     :
622             player->is_snapping   ? ACTION_SNAPPING   : ACTION_DEFAULT);
623
624 #if 0
625   printf("::: '%s'\n", element_action_info[action].suffix);
626 #endif
627
628   InitPlayerGfxAnimation(player, action, move_dir);
629
630   /* ----------------------------------------------------------------------- */
631   /* draw things in the field the player is leaving, if needed               */
632   /* ----------------------------------------------------------------------- */
633
634 #if 1
635   if (player->is_moving)
636 #else
637   if (player_is_moving)
638 #endif
639   {
640     if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
641     {
642       DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
643
644       if (last_element == EL_DYNAMITE_ACTIVE ||
645           last_element == EL_SP_DISK_RED_ACTIVE)
646         DrawDynamite(last_jx, last_jy);
647       else
648         DrawLevelFieldThruMask(last_jx, last_jy);
649     }
650     else if (last_element == EL_DYNAMITE_ACTIVE ||
651              last_element == EL_SP_DISK_RED_ACTIVE)
652       DrawDynamite(last_jx, last_jy);
653     else
654       DrawLevelField(last_jx, last_jy);
655
656     if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
657       DrawLevelElement(next_jx, next_jy, EL_EMPTY);
658   }
659
660   if (!IN_SCR_FIELD(sx, sy))
661     return;
662
663   if (setup.direct_draw)
664     SetDrawtoField(DRAW_BUFFERED);
665
666   /* ----------------------------------------------------------------------- */
667   /* draw things behind the player, if needed                                */
668   /* ----------------------------------------------------------------------- */
669
670   if (Back[jx][jy])
671     DrawLevelElement(jx, jy, Back[jx][jy]);
672   else if (IS_ACTIVE_BOMB(element))
673     DrawLevelElement(jx, jy, EL_EMPTY);
674   else
675   {
676     if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
677     {
678       if (GFX_CRUMBLED(GfxElement[jx][jy]))
679         DrawLevelFieldCrumbledSandDigging(jx, jy, move_dir, player->StepFrame);
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 1
702
703   graphic = getPlayerGraphic(player, move_dir);
704
705   /* in the case of changed player action or direction, prevent the current
706      animation frame from being restarted for identical animations */
707   if (player->Frame == 0 &&
708       graphic_info[graphic].bitmap == graphic_info[last_player_graphic].bitmap)
709     player->Frame = last_player_frame;
710
711 #else
712
713   if (player->use_murphy_graphic)
714   {
715     static int last_horizontal_dir = MV_LEFT;
716
717     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
718       last_horizontal_dir = move_dir;
719
720     graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
721
722     if (graphic == IMG_SP_MURPHY)       /* undefined => use special graphic */
723     {
724       int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
725
726       graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
727     }
728   }
729   else
730     graphic = el_act_dir2img(player->element_nr, player->GfxAction, move_dir);
731
732 #endif
733
734   frame = getGraphicAnimationFrame(graphic, player->Frame);
735
736   if (player->GfxPos)
737   {
738     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
739       sxx = player->GfxPos;
740     else
741       syy = player->GfxPos;
742   }
743
744   if (!setup.soft_scrolling && ScreenMovPos)
745     sxx = syy = 0;
746
747   DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
748
749   if (SHIELD_ON(player))
750   {
751     int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
752                    IMG_SHIELD_NORMAL_ACTIVE);
753     int frame = getGraphicAnimationFrame(graphic, -1);
754
755     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
756   }
757
758   /* ----------------------------------------------------------------------- */
759   /* draw things the player is pushing, if needed                            */
760   /* ----------------------------------------------------------------------- */
761
762 #if 0
763   printf("::: %d, %d [%d, %d] [%d]\n",
764          player->is_pushing, player_is_moving, player->GfxAction,
765          player->is_moving, player_is_moving);
766 #endif
767
768 #if 1
769   if (player->is_pushing && player->is_moving)
770 #else
771   if (player->is_pushing && player_is_moving)
772 #endif
773   {
774     int px = SCREENX(next_jx), py = SCREENY(next_jy);
775
776     if (Back[next_jx][next_jy])
777       DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
778
779     if ((sxx || syy) && element == EL_SOKOBAN_OBJECT)
780       DrawGraphicShiftedThruMask(px, py, sxx, syy, IMG_SOKOBAN_OBJECT, 0,
781                                  NO_CUTTING);
782     else
783     {
784       int element = MovingOrBlocked2Element(next_jx, next_jy);
785       int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
786 #if 1
787       int frame = getGraphicAnimationFrame(graphic, player->StepFrame);
788 #else
789       int frame = getGraphicAnimationFrame(graphic, player->Frame);
790 #endif
791
792       DrawGraphicShifted(px, py, sxx, syy, graphic, frame,
793                          NO_CUTTING, NO_MASKING);
794     }
795   }
796
797   /* ----------------------------------------------------------------------- */
798   /* draw things in front of player (active dynamite or dynabombs)           */
799   /* ----------------------------------------------------------------------- */
800
801   if (IS_ACTIVE_BOMB(element))
802   {
803     graphic = el2img(element);
804     frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
805
806     if (game.emulation == EMU_SUPAPLEX)
807       DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
808     else
809       DrawGraphicThruMask(sx, sy, graphic, frame);
810   }
811
812   if (player_is_moving && last_element == EL_EXPLOSION)
813   {
814     int graphic = el_act2img(GfxElement[last_jx][last_jy], ACTION_EXPLODING);
815     int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
816     int phase = ExplodePhase[last_jx][last_jy] - 1;
817     int frame = getGraphicAnimationFrame(graphic, phase - delay);
818
819     if (phase >= delay)
820       DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
821   }
822
823   /* ----------------------------------------------------------------------- */
824   /* draw elements the player is just walking/passing through/under          */
825   /* ----------------------------------------------------------------------- */
826
827   /* handle the field the player is leaving ... */
828   if (player_is_moving && IS_ACCESSIBLE_INSIDE(last_element))
829     DrawLevelField(last_jx, last_jy);
830   else if (player_is_moving && IS_ACCESSIBLE_UNDER(last_element))
831     DrawLevelFieldThruMask(last_jx, last_jy);
832
833   /* ... and the field the player is entering */
834   if (IS_ACCESSIBLE_INSIDE(element))
835     DrawLevelField(jx, jy);
836   else if (IS_ACCESSIBLE_UNDER(element))
837     DrawLevelFieldThruMask(jx, jy);
838
839   if (setup.direct_draw)
840   {
841     int dest_x = SX + SCREENX(MIN(jx, last_jx)) * TILEX;
842     int dest_y = SY + SCREENY(MIN(jy, last_jy)) * TILEY;
843     int x_size = TILEX * (1 + ABS(jx - last_jx));
844     int y_size = TILEY * (1 + ABS(jy - last_jy));
845
846     BlitBitmap(drawto_field, window,
847                dest_x, dest_y, x_size, y_size, dest_x, dest_y);
848     SetDrawtoField(DRAW_DIRECT);
849   }
850
851   MarkTileDirty(sx,sy);
852 }
853
854 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
855 {
856   struct GraphicInfo *g = &graphic_info[graphic];
857
858   *bitmap = g->bitmap;
859
860   if (g->offset_y == 0)         /* frames are ordered horizontally */
861   {
862     int max_width = g->anim_frames_per_line * g->width;
863
864     *x = (g->src_x + frame * g->offset_x) % max_width;
865     *y = g->src_y + (g->src_x + frame * g->offset_x) / max_width * g->height;
866   }
867   else if (g->offset_x == 0)    /* frames are ordered vertically */
868   {
869     int max_height = g->anim_frames_per_line * g->height;
870
871     *x = g->src_x + (g->src_y + frame * g->offset_y) / max_height * g->width;
872     *y = (g->src_y + frame * g->offset_y) % max_height;
873   }
874   else                          /* frames are ordered diagonally */
875   {
876     *x = g->src_x + frame * g->offset_x;
877     *y = g->src_y + frame * g->offset_y;
878   }
879 }
880
881 void DrawGraphic(int x, int y, int graphic, int frame)
882 {
883 #if DEBUG
884   if (!IN_SCR_FIELD(x, y))
885   {
886     printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
887     printf("DrawGraphic(): This should never happen!\n");
888     return;
889   }
890 #endif
891
892   DrawGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic, frame);
893   MarkTileDirty(x, y);
894 }
895
896 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
897                     int frame)
898 {
899   Bitmap *src_bitmap;
900   int src_x, src_y;
901
902   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
903   BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
904 }
905
906 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
907 {
908 #if DEBUG
909   if (!IN_SCR_FIELD(x, y))
910   {
911     printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
912     printf("DrawGraphicThruMask(): This should never happen!\n");
913     return;
914   }
915 #endif
916
917   DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y *TILEY, graphic,
918                          frame);
919   MarkTileDirty(x, y);
920 }
921
922 void DrawGraphicThruMaskExt(DrawBuffer *d, int dest_x, int dest_y, int graphic,
923                             int frame)
924 {
925 #if 1
926   Bitmap *src_bitmap;
927   int src_x, src_y;
928   GC drawing_gc;
929
930   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
931   drawing_gc = src_bitmap->stored_clip_gc;
932 #else
933   GC drawing_gc = src_bitmap->stored_clip_gc;
934   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
935   int src_x = graphic_info[graphic].src_x;
936   int src_y = graphic_info[graphic].src_y;
937   int offset_x = graphic_info[graphic].offset_x;
938   int offset_y = graphic_info[graphic].offset_y;
939
940   src_x += frame * offset_x;
941   src_y += frame * offset_y;
942
943 #endif
944
945   SetClipOrigin(src_bitmap, drawing_gc, dest_x - src_x, dest_y - src_y);
946   BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY, dest_x, dest_y);
947 }
948
949 void DrawMiniGraphic(int x, int y, int graphic)
950 {
951   DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
952   MarkTileDirty(x / 2, y / 2);
953 }
954
955 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
956 {
957   struct GraphicInfo *g = &graphic_info[graphic];
958   int mini_startx = 0;
959   int mini_starty = g->bitmap->height * 2 / 3;
960
961   *bitmap = g->bitmap;
962   *x = mini_startx + g->src_x / 2;
963   *y = mini_starty + g->src_y / 2;
964 }
965
966 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
967 {
968   Bitmap *src_bitmap;
969   int src_x, src_y;
970
971   getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
972   BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
973 }
974
975 void DrawGraphicShifted(int x,int y, int dx,int dy, int graphic, int frame,
976                         int cut_mode, int mask_mode)
977 {
978   Bitmap *src_bitmap;
979   GC drawing_gc;
980   int src_x, src_y;
981   int width = TILEX, height = TILEY;
982   int cx = 0, cy = 0;
983   int dest_x, dest_y;
984
985   if (graphic < 0)
986   {
987     DrawGraphic(x, y, graphic, frame);
988     return;
989   }
990
991   if (dx || dy)                 /* shifted graphic */
992   {
993     if (x < BX1)                /* object enters playfield from the left */
994     {
995       x = BX1;
996       width = dx;
997       cx = TILEX - dx;
998       dx = 0;
999     }
1000     else if (x > BX2)           /* object enters playfield from the right */
1001     {
1002       x = BX2;
1003       width = -dx;
1004       dx = TILEX + dx;
1005     }
1006     else if (x==BX1 && dx < 0)  /* object leaves playfield to the left */
1007     {
1008       width += dx;
1009       cx = -dx;
1010       dx = 0;
1011     }
1012     else if (x==BX2 && dx > 0)  /* object leaves playfield to the right */
1013       width -= dx;
1014     else if (dx)                /* general horizontal movement */
1015       MarkTileDirty(x + SIGN(dx), y);
1016
1017     if (y < BY1)                /* object enters playfield from the top */
1018     {
1019       if (cut_mode==CUT_BELOW)  /* object completely above top border */
1020         return;
1021
1022       y = BY1;
1023       height = dy;
1024       cy = TILEY - dy;
1025       dy = 0;
1026     }
1027     else if (y > BY2)           /* object enters playfield from the bottom */
1028     {
1029       y = BY2;
1030       height = -dy;
1031       dy = TILEY + dy;
1032     }
1033     else if (y==BY1 && dy < 0)  /* object leaves playfield to the top */
1034     {
1035       height += dy;
1036       cy = -dy;
1037       dy = 0;
1038     }
1039     else if (dy > 0 && cut_mode == CUT_ABOVE)
1040     {
1041       if (y == BY2)             /* object completely above bottom border */
1042         return;
1043
1044       height = dy;
1045       cy = TILEY - dy;
1046       dy = TILEY;
1047       MarkTileDirty(x, y + 1);
1048     }                           /* object leaves playfield to the bottom */
1049     else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1050       height -= dy;
1051     else if (dy)                /* general vertical movement */
1052       MarkTileDirty(x, y + SIGN(dy));
1053   }
1054
1055 #if 1
1056   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1057 #else
1058   src_bitmap = graphic_info[graphic].bitmap;
1059   src_x = graphic_info[graphic].src_x;
1060   src_y = graphic_info[graphic].src_y;
1061   offset_x = graphic_info[graphic].offset_x;
1062   offset_y = graphic_info[graphic].offset_y;
1063
1064   src_x += frame * offset_x;
1065   src_y += frame * offset_y;
1066 #endif
1067
1068   drawing_gc = src_bitmap->stored_clip_gc;
1069
1070   src_x += cx;
1071   src_y += cy;
1072
1073   dest_x = FX + x * TILEX + dx;
1074   dest_y = FY + y * TILEY + dy;
1075
1076 #if DEBUG
1077   if (!IN_SCR_FIELD(x,y))
1078   {
1079     printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1080     printf("DrawGraphicShifted(): This should never happen!\n");
1081     return;
1082   }
1083 #endif
1084
1085   if (mask_mode == USE_MASKING)
1086   {
1087     SetClipOrigin(src_bitmap, drawing_gc, dest_x - src_x, dest_y - src_y);
1088     BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1089                      dest_x, dest_y);
1090   }
1091   else
1092     BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1093                dest_x, dest_y);
1094
1095   MarkTileDirty(x,y);
1096 }
1097
1098 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1099                                 int frame, int cut_mode)
1100 {
1101   DrawGraphicShifted(x,y, dx,dy, graphic, frame, cut_mode, USE_MASKING);
1102 }
1103
1104 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1105                           int cut_mode, int mask_mode)
1106 {
1107   int lx = LEVELX(x), ly = LEVELY(y);
1108   int graphic;
1109   int frame;
1110
1111   if (IN_LEV_FIELD(lx, ly))
1112   {
1113     SetRandomAnimationValue(lx, ly);
1114
1115     graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1116     frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1117   }
1118   else  /* border element */
1119   {
1120     graphic = el2img(element);
1121     frame = getGraphicAnimationFrame(graphic, -1);
1122   }
1123
1124   if (element == EL_EXPANDABLE_WALL)
1125   {
1126     boolean left_stopped = FALSE, right_stopped = FALSE;
1127
1128     if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1129       left_stopped = TRUE;
1130     if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1131       right_stopped = TRUE;
1132
1133     if (left_stopped && right_stopped)
1134       graphic = IMG_WALL;
1135     else if (left_stopped)
1136     {
1137       graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1138       frame = graphic_info[graphic].anim_frames - 1;
1139     }
1140     else if (right_stopped)
1141     {
1142       graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1143       frame = graphic_info[graphic].anim_frames - 1;
1144     }
1145   }
1146
1147   if (dx || dy)
1148     DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1149   else if (mask_mode == USE_MASKING)
1150     DrawGraphicThruMask(x, y, graphic, frame);
1151   else
1152     DrawGraphic(x, y, graphic, frame);
1153 }
1154
1155 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1156                          int cut_mode, int mask_mode)
1157 {
1158   if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1159     DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1160                          cut_mode, mask_mode);
1161 }
1162
1163 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1164                               int cut_mode)
1165 {
1166   DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1167 }
1168
1169 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1170                              int cut_mode)
1171 {
1172   DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1173 }
1174
1175 void DrawLevelElementThruMask(int x, int y, int element)
1176 {
1177   DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1178 }
1179
1180 void DrawLevelFieldThruMask(int x, int y)
1181 {
1182   DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1183 }
1184
1185 static void DrawLevelFieldCrumbledSandExt(int x, int y, int graphic, int frame)
1186 {
1187   Bitmap *src_bitmap;
1188   int src_x, src_y;
1189   int sx = SCREENX(x), sy = SCREENY(y);
1190   int element;
1191   int width, height, cx, cy, i;
1192 #if 1
1193   int crumbled_border_size = graphic_info[graphic].border_size;
1194 #else
1195   int snip = TILEX / 8; /* number of border pixels from "crumbled graphic" */
1196 #endif
1197   static int xy[4][2] =
1198   {
1199     { 0, -1 },
1200     { -1, 0 },
1201     { +1, 0 },
1202     { 0, +1 }
1203   };
1204
1205 #if 0
1206   if (x == 0 && y == 7)
1207     printf("::: %d, %d [%d]\n", GfxElement[x][y], Feld[x][y],
1208            crumbled_border_size);
1209 #endif
1210
1211   if (!IN_LEV_FIELD(x, y))
1212     return;
1213
1214   element = (GfxElement[x][y] != EL_UNDEFINED && Feld[x][y] != EL_EXPLOSION ?
1215              GfxElement[x][y] : Feld[x][y]);
1216
1217   /* crumble field itself */
1218   if (GFX_CRUMBLED(element) && !IS_MOVING(x, y))
1219   {
1220     if (!IN_SCR_FIELD(sx, sy))
1221       return;
1222
1223     getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1224
1225     for(i=0; i<4; i++)
1226     {
1227       int xx = x + xy[i][0];
1228       int yy = y + xy[i][1];
1229
1230       element = (IN_LEV_FIELD(xx, yy) ? Feld[xx][yy] : BorderElement);
1231
1232       /* check if neighbour field is of same type */
1233       if (GFX_CRUMBLED(element) && !IS_MOVING(xx, yy))
1234         continue;
1235
1236 #if 0
1237       if (Feld[x][y] == EL_CUSTOM_START + 123)
1238         printf("::: crumble [%d] THE CHAOS ENGINE (%d, %d): %d, %d\n",
1239                i, Feld[x][y], element,
1240                GFX_CRUMBLED(element), IS_MOVING(x, y));
1241 #endif
1242
1243       if (i == 1 || i == 2)
1244       {
1245         width = crumbled_border_size;
1246         height = TILEY;
1247         cx = (i == 2 ? TILEX - crumbled_border_size : 0);
1248         cy = 0;
1249       }
1250       else
1251       {
1252         width = TILEX;
1253         height = crumbled_border_size;
1254         cx = 0;
1255         cy = (i == 3 ? TILEY - crumbled_border_size : 0);
1256       }
1257
1258       BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1259                  width, height, FX + sx * TILEX + cx, FY + sy * TILEY + cy);
1260     }
1261
1262     MarkTileDirty(sx, sy);
1263   }
1264   else          /* crumble neighbour fields */
1265   {
1266 #if 0
1267     getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1268 #endif
1269
1270     for(i=0; i<4; i++)
1271     {
1272       int xx = x + xy[i][0];
1273       int yy = y + xy[i][1];
1274       int sxx = sx + xy[i][0];
1275       int syy = sy + xy[i][1];
1276
1277       if (!IN_LEV_FIELD(xx, yy) ||
1278           !IN_SCR_FIELD(sxx, syy) ||
1279           !GFX_CRUMBLED(Feld[xx][yy]) ||
1280           IS_MOVING(xx, yy))
1281         continue;
1282
1283 #if 1
1284       graphic = el_act2crm(Feld[xx][yy], ACTION_DEFAULT);
1285       crumbled_border_size = graphic_info[graphic].border_size;
1286
1287       getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1288 #endif
1289
1290       if (i == 1 || i == 2)
1291       {
1292         width = crumbled_border_size;
1293         height = TILEY;
1294         cx = (i == 1 ? TILEX - crumbled_border_size : 0);
1295         cy = 0;
1296       }
1297       else
1298       {
1299         width = TILEX;
1300         height = crumbled_border_size;
1301         cx = 0;
1302         cy = (i == 0 ? TILEY - crumbled_border_size : 0);
1303       }
1304
1305       BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1306                  width, height, FX + sxx * TILEX + cx, FY + syy * TILEY + cy);
1307
1308       MarkTileDirty(sxx, syy);
1309     }
1310   }
1311 }
1312
1313 void DrawLevelFieldCrumbledSand(int x, int y)
1314 {
1315 #if 1
1316   int graphic;
1317
1318   if (!IN_LEV_FIELD(x, y))
1319     return;
1320
1321   graphic = el_act2crm(Feld[x][y], ACTION_DEFAULT);
1322
1323   DrawLevelFieldCrumbledSandExt(x, y, graphic, 0);
1324 #else
1325   DrawLevelFieldCrumbledSandExt(x, y, IMG_SAND_CRUMBLED, 0);
1326 #endif
1327 }
1328
1329 void DrawLevelFieldCrumbledSandDigging(int x, int y, int direction,
1330                                        int step_frame)
1331 {
1332 #if 1
1333   int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
1334   int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
1335 #else
1336   int graphic1 = el_act_dir2img(EL_SAND,          ACTION_DIGGING, direction);
1337   int graphic2 = el_act_dir2img(EL_SAND_CRUMBLED, ACTION_DIGGING, direction);
1338 #endif
1339   int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
1340   int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
1341   int sx = SCREENX(x), sy = SCREENY(y);
1342
1343   DrawGraphic(sx, sy, graphic1, frame1);
1344   DrawLevelFieldCrumbledSandExt(x, y, graphic2, frame2);
1345 }
1346
1347 void DrawLevelFieldCrumbledSandNeighbours(int x, int y)
1348 {
1349   int sx = SCREENX(x), sy = SCREENY(y);
1350   static int xy[4][2] =
1351   {
1352     { 0, -1 },
1353     { -1, 0 },
1354     { +1, 0 },
1355     { 0, +1 }
1356   };
1357   int i;
1358
1359   for(i=0; i<4; i++)
1360   {
1361     int xx = x + xy[i][0];
1362     int yy = y + xy[i][1];
1363     int sxx = sx + xy[i][0];
1364     int syy = sy + xy[i][1];
1365
1366     if (!IN_LEV_FIELD(xx, yy) ||
1367         !IN_SCR_FIELD(sxx, syy) ||
1368         !GFX_CRUMBLED(Feld[xx][yy]) ||
1369         IS_MOVING(xx, yy))
1370       continue;
1371
1372     DrawLevelField(xx, yy);
1373   }
1374 }
1375
1376 static int getBorderElement(int x, int y)
1377 {
1378   int border[7][2] =
1379   {
1380     { EL_STEELWALL_TOPLEFT,             EL_INVISIBLE_STEELWALL_TOPLEFT     },
1381     { EL_STEELWALL_TOPRIGHT,            EL_INVISIBLE_STEELWALL_TOPRIGHT    },
1382     { EL_STEELWALL_BOTTOMLEFT,          EL_INVISIBLE_STEELWALL_BOTTOMLEFT  },
1383     { EL_STEELWALL_BOTTOMRIGHT,         EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
1384     { EL_STEELWALL_VERTICAL,            EL_INVISIBLE_STEELWALL_VERTICAL    },
1385     { EL_STEELWALL_HORIZONTAL,          EL_INVISIBLE_STEELWALL_HORIZONTAL  },
1386     { EL_STEELWALL,                     EL_INVISIBLE_STEELWALL             }
1387   };
1388   int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
1389   int steel_position = (x == -1         && y == -1              ? 0 :
1390                         x == lev_fieldx && y == -1              ? 1 :
1391                         x == -1         && y == lev_fieldy      ? 2 :
1392                         x == lev_fieldx && y == lev_fieldy      ? 3 :
1393                         x == -1         || x == lev_fieldx      ? 4 :
1394                         y == -1         || y == lev_fieldy      ? 5 : 6);
1395
1396   return border[steel_position][steel_type];
1397 }
1398
1399 void DrawScreenElement(int x, int y, int element)
1400 {
1401   DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
1402   DrawLevelFieldCrumbledSand(LEVELX(x), LEVELY(y));
1403 }
1404
1405 void DrawLevelElement(int x, int y, int element)
1406 {
1407   if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1408     DrawScreenElement(SCREENX(x), SCREENY(y), element);
1409 }
1410
1411 void DrawScreenField(int x, int y)
1412 {
1413   int lx = LEVELX(x), ly = LEVELY(y);
1414   int element, content;
1415
1416   if (!IN_LEV_FIELD(lx, ly))
1417   {
1418     if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
1419       element = EL_EMPTY;
1420     else
1421       element = getBorderElement(lx, ly);
1422
1423     DrawScreenElement(x, y, element);
1424     return;
1425   }
1426
1427   element = Feld[lx][ly];
1428   content = Store[lx][ly];
1429
1430   if (IS_MOVING(lx, ly))
1431   {
1432     int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
1433     boolean cut_mode = NO_CUTTING;
1434
1435     if (element == EL_QUICKSAND_EMPTYING ||
1436         element == EL_MAGIC_WALL_EMPTYING ||
1437         element == EL_BD_MAGIC_WALL_EMPTYING ||
1438         element == EL_AMOEBA_DROPPING)
1439       cut_mode = CUT_ABOVE;
1440     else if (element == EL_QUICKSAND_FILLING ||
1441              element == EL_MAGIC_WALL_FILLING ||
1442              element == EL_BD_MAGIC_WALL_FILLING)
1443       cut_mode = CUT_BELOW;
1444
1445     if (cut_mode == CUT_ABOVE)
1446       DrawScreenElementShifted(x, y, 0, 0, element, NO_CUTTING);
1447     else
1448       DrawScreenElement(x, y, EL_EMPTY);
1449
1450     if (horiz_move)
1451       DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
1452     else if (cut_mode == NO_CUTTING)
1453       DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
1454     else
1455       DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
1456
1457     if (content == EL_ACID)
1458       DrawLevelElementThruMask(lx, ly + 1, EL_ACID);
1459   }
1460   else if (IS_BLOCKED(lx, ly))
1461   {
1462     int oldx, oldy;
1463     int sx, sy;
1464     int horiz_move;
1465     boolean cut_mode = NO_CUTTING;
1466     int element_old, content_old;
1467
1468     Blocked2Moving(lx, ly, &oldx, &oldy);
1469     sx = SCREENX(oldx);
1470     sy = SCREENY(oldy);
1471     horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
1472                   MovDir[oldx][oldy] == MV_RIGHT);
1473
1474     element_old = Feld[oldx][oldy];
1475     content_old = Store[oldx][oldy];
1476
1477     if (element_old == EL_QUICKSAND_EMPTYING ||
1478         element_old == EL_MAGIC_WALL_EMPTYING ||
1479         element_old == EL_BD_MAGIC_WALL_EMPTYING ||
1480         element_old == EL_AMOEBA_DROPPING)
1481       cut_mode = CUT_ABOVE;
1482
1483     DrawScreenElement(x, y, EL_EMPTY);
1484
1485     if (horiz_move)
1486       DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
1487                                NO_CUTTING);
1488     else if (cut_mode == NO_CUTTING)
1489       DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
1490                                cut_mode);
1491     else
1492       DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
1493                                cut_mode);
1494   }
1495   else if (IS_DRAWABLE(element))
1496     DrawScreenElement(x, y, element);
1497   else
1498     DrawScreenElement(x, y, EL_EMPTY);
1499 }
1500
1501 void DrawLevelField(int x, int y)
1502 {
1503   if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1504     DrawScreenField(SCREENX(x), SCREENY(y));
1505   else if (IS_MOVING(x, y))
1506   {
1507     int newx,newy;
1508
1509     Moving2Blocked(x, y, &newx, &newy);
1510     if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
1511       DrawScreenField(SCREENX(newx), SCREENY(newy));
1512   }
1513   else if (IS_BLOCKED(x, y))
1514   {
1515     int oldx, oldy;
1516
1517     Blocked2Moving(x, y, &oldx, &oldy);
1518     if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
1519       DrawScreenField(SCREENX(oldx), SCREENY(oldy));
1520   }
1521 }
1522
1523 void DrawMiniElement(int x, int y, int element)
1524 {
1525   int graphic;
1526
1527   graphic = el2edimg(element);
1528   DrawMiniGraphic(x, y, graphic);
1529 }
1530
1531 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
1532 {
1533   int x = sx + scroll_x, y = sy + scroll_y;
1534
1535   if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
1536     DrawMiniElement(sx, sy, EL_EMPTY);
1537   else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
1538     DrawMiniElement(sx, sy, Feld[x][y]);
1539   else
1540     DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
1541 }
1542
1543 void DrawEnvelopeBackground(int envelope_nr, int startx, int starty,
1544                             int x, int y, int xsize, int ysize, int font_nr)
1545 {
1546   int font_width  = getFontWidth(font_nr);
1547   int font_height = getFontHeight(font_nr);
1548   int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
1549   Bitmap *src_bitmap;
1550   int src_x, src_y;
1551   int dst_x = SX + startx + x * font_width;
1552   int dst_y = SY + starty + y * font_height;
1553   int width  = graphic_info[graphic].width;
1554   int height = graphic_info[graphic].height;
1555   int inner_width  = MAX(width  - 2 * font_width,  font_width);
1556   int inner_height = MAX(height - 2 * font_height, font_height);
1557   int inner_sx = (width >= 3 * font_width ? font_width : 0);
1558   int inner_sy = (height >= 3 * font_height ? font_height : 0);
1559   boolean draw_masked = graphic_info[graphic].draw_masked;
1560
1561   getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1562
1563   if (src_bitmap == NULL || width < font_width || height < font_height)
1564   {
1565     ClearRectangle(drawto, dst_x, dst_y, font_width, font_height);
1566     return;
1567   }
1568
1569   src_x += (x == 0 ? 0 : x == xsize - 1 ? width  - font_width  :
1570             inner_sx + (x - 1) * font_width  % inner_width);
1571   src_y += (y == 0 ? 0 : y == ysize - 1 ? height - font_height :
1572             inner_sy + (y - 1) * font_height % inner_height);
1573
1574   if (draw_masked)
1575   {
1576     SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
1577                   dst_x - src_x, dst_y - src_y);
1578     BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, font_width, font_height,
1579                      dst_x, dst_y);
1580   }
1581   else
1582     BlitBitmap(src_bitmap, drawto, src_x, src_y, font_width, font_height,
1583                dst_x, dst_y);
1584 }
1585
1586 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
1587 {
1588   int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
1589   boolean draw_masked = graphic_info[graphic].draw_masked;
1590   int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_ON_BACKGROUND);
1591   boolean ffwd_delay = (tape.playing && tape.fast_forward);
1592   unsigned long anim_delay = 0;
1593   int anim_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
1594   int font_nr = FONT_ENVELOPE_1 + envelope_nr;
1595   int font_width = getFontWidth(font_nr);
1596   int font_height = getFontHeight(font_nr);
1597   int max_xsize = level.envelope_xsize[envelope_nr];
1598   int max_ysize = level.envelope_ysize[envelope_nr];
1599   int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
1600   int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
1601   int xend = max_xsize;
1602   int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
1603   int xstep = (xstart < xend ? 1 : 0);
1604   int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
1605   int x, y;
1606
1607   for (x=xstart, y=ystart; x <= xend && y <= yend; x += xstep, y += ystep)
1608   {
1609     int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
1610     int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
1611     int sx = (SXSIZE - xsize * font_width)  / 2;
1612     int sy = (SYSIZE - ysize * font_height) / 2;
1613     int xx, yy;
1614
1615     SetDrawtoField(DRAW_BUFFERED);
1616
1617     BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1618
1619     SetDrawtoField(DRAW_BACKBUFFER);
1620
1621     for (yy=0; yy < ysize; yy++) for (xx=0; xx < xsize; xx++)
1622       DrawEnvelopeBackground(envelope_nr, sx,sy, xx,yy, xsize, ysize, font_nr);
1623
1624     DrawTextToTextArea(SX + sx + font_width, SY + sy + font_height,
1625                        level.envelope_text[envelope_nr], font_nr, max_xsize,
1626                        xsize - 2, ysize - 2, mask_mode);
1627
1628     redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
1629     BackToFront();
1630
1631     WaitUntilDelayReached(&anim_delay, anim_delay_value / 2);
1632   }
1633 }
1634
1635 void ShowEnvelope(int envelope_nr)
1636 {
1637   int element = EL_ENVELOPE_1 + envelope_nr;
1638   int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
1639   int sound_opening = element_info[element].sound[ACTION_OPENING];
1640   int sound_closing = element_info[element].sound[ACTION_CLOSING];
1641   boolean ffwd_delay = (tape.playing && tape.fast_forward);
1642   int wait_delay_value = (ffwd_delay ? 500 : 1000);
1643   int anim_mode = graphic_info[graphic].anim_mode;
1644   int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
1645                         anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
1646
1647   game.envelope_active = TRUE;  /* needed for RedrawPlayfield() events */
1648
1649   PlaySoundStereo(sound_opening, SOUND_MIDDLE);
1650
1651   if (anim_mode == ANIM_DEFAULT)
1652     AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
1653
1654   AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
1655
1656   if (tape.playing)
1657     Delay(wait_delay_value);
1658   else
1659     WaitForEventToContinue();
1660
1661   PlaySoundStereo(sound_closing, SOUND_MIDDLE);
1662
1663   if (anim_mode != ANIM_NONE)
1664     AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
1665
1666   if (anim_mode == ANIM_DEFAULT)
1667     AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
1668
1669   game.envelope_active = FALSE;
1670
1671   SetDrawtoField(DRAW_BUFFERED);
1672
1673   redraw_mask |= REDRAW_FIELD;
1674   BackToFront();
1675 }
1676
1677 void getMicroGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1678 {
1679   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
1680   int mini_startx = src_bitmap->width * 3 / 4;
1681   int mini_starty = src_bitmap->height * 2 / 3;
1682   int src_x = mini_startx + graphic_info[graphic].src_x / 8;
1683   int src_y = mini_starty + graphic_info[graphic].src_y / 8;
1684
1685   *bitmap = src_bitmap;
1686   *x = src_x;
1687   *y = src_y;
1688 }
1689
1690 void DrawMicroElement(int xpos, int ypos, int element)
1691 {
1692   Bitmap *src_bitmap;
1693   int src_x, src_y;
1694   int graphic = el2preimg(element);
1695
1696   getMicroGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1697   BlitBitmap(src_bitmap, drawto, src_x, src_y, MICRO_TILEX, MICRO_TILEY,
1698              xpos, ypos);
1699 }
1700
1701 void DrawLevel()
1702 {
1703   int x,y;
1704
1705   SetDrawBackgroundMask(REDRAW_NONE);
1706   ClearWindow();
1707
1708   for(x=BX1; x<=BX2; x++)
1709     for(y=BY1; y<=BY2; y++)
1710       DrawScreenField(x, y);
1711
1712   redraw_mask |= REDRAW_FIELD;
1713 }
1714
1715 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
1716 {
1717   int x,y;
1718
1719   for(x=0; x<size_x; x++)
1720     for(y=0; y<size_y; y++)
1721       DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
1722
1723   redraw_mask |= REDRAW_FIELD;
1724 }
1725
1726 static void DrawMicroLevelExt(int xpos, int ypos, int from_x, int from_y)
1727 {
1728   int x, y;
1729
1730   DrawBackground(xpos, ypos, MICROLEV_XSIZE, MICROLEV_YSIZE);
1731
1732   if (lev_fieldx < STD_LEV_FIELDX)
1733     xpos += (STD_LEV_FIELDX - lev_fieldx) / 2 * MICRO_TILEX;
1734   if (lev_fieldy < STD_LEV_FIELDY)
1735     ypos += (STD_LEV_FIELDY - lev_fieldy) / 2 * MICRO_TILEY;
1736
1737   xpos += MICRO_TILEX;
1738   ypos += MICRO_TILEY;
1739
1740   for(x=-1; x<=STD_LEV_FIELDX; x++)
1741   {
1742     for(y=-1; y<=STD_LEV_FIELDY; y++)
1743     {
1744       int lx = from_x + x, ly = from_y + y;
1745
1746       if (lx >= 0 && lx < lev_fieldx && ly >= 0 && ly < lev_fieldy)
1747         DrawMicroElement(xpos + x * MICRO_TILEX, ypos + y * MICRO_TILEY,
1748                          level.field[lx][ly]);
1749       else if (lx >= -1 && lx < lev_fieldx+1 && ly >= -1 && ly < lev_fieldy+1
1750                && BorderElement != EL_EMPTY)
1751         DrawMicroElement(xpos + x * MICRO_TILEX, ypos + y * MICRO_TILEY,
1752                          getBorderElement(lx, ly));
1753     }
1754   }
1755
1756   redraw_mask |= REDRAW_MICROLEVEL;
1757 }
1758
1759 #define MICROLABEL_EMPTY                0
1760 #define MICROLABEL_LEVEL_NAME           1
1761 #define MICROLABEL_CREATED_BY           2
1762 #define MICROLABEL_LEVEL_AUTHOR         3
1763 #define MICROLABEL_IMPORTED_FROM        4
1764 #define MICROLABEL_LEVEL_IMPORT_INFO    5
1765
1766 static void DrawMicroLevelLabelExt(int mode)
1767 {
1768   char label_text[MAX_OUTPUT_LINESIZE + 1];
1769   int max_len_label_text;
1770   int font_nr = FONT_TEXT_2;
1771
1772   if (mode == MICROLABEL_CREATED_BY || mode == MICROLABEL_IMPORTED_FROM)
1773     font_nr = FONT_TEXT_3;
1774
1775   max_len_label_text = SXSIZE / getFontWidth(font_nr);
1776
1777   DrawBackground(SX, MICROLABEL_YPOS, SXSIZE, getFontHeight(font_nr));
1778
1779   strncpy(label_text, (mode == MICROLABEL_LEVEL_NAME ? level.name :
1780                        mode == MICROLABEL_CREATED_BY ? "created by" :
1781                        mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
1782                        mode == MICROLABEL_IMPORTED_FROM ? "imported from" :
1783                        mode == MICROLABEL_LEVEL_IMPORT_INFO ?
1784                        leveldir_current->imported_from : ""),
1785           max_len_label_text);
1786   label_text[max_len_label_text] = '\0';
1787
1788   if (strlen(label_text) > 0)
1789   {
1790     int lxpos = SX + (SXSIZE - getTextWidth(label_text, font_nr)) / 2;
1791     int lypos = MICROLABEL_YPOS;
1792
1793     DrawText(lxpos, lypos, label_text, font_nr);
1794   }
1795
1796   redraw_mask |= REDRAW_MICROLEVEL;
1797 }
1798
1799 void DrawMicroLevel(int xpos, int ypos, boolean restart)
1800 {
1801   static unsigned long scroll_delay = 0;
1802   static unsigned long label_delay = 0;
1803   static int from_x, from_y, scroll_direction;
1804   static int label_state, label_counter;
1805   int last_game_status = game_status;   /* save current game status */
1806
1807   /* force PREVIEW font on preview level */
1808   game_status = GAME_MODE_PSEUDO_PREVIEW;
1809
1810   if (restart)
1811   {
1812     from_x = from_y = 0;
1813     scroll_direction = MV_RIGHT;
1814     label_state = 1;
1815     label_counter = 0;
1816
1817     DrawMicroLevelExt(xpos, ypos, from_x, from_y);
1818     DrawMicroLevelLabelExt(label_state);
1819
1820     /* initialize delay counters */
1821     DelayReached(&scroll_delay, 0);
1822     DelayReached(&label_delay, 0);
1823
1824     if (leveldir_current->name)
1825     {
1826       int text_width = getTextWidth(leveldir_current->name, FONT_TEXT_1);
1827       int lxpos = SX + (SXSIZE - text_width) / 2;
1828       int lypos = SY + 352;
1829
1830       DrawText(lxpos, lypos, leveldir_current->name, FONT_TEXT_1);
1831     }
1832
1833     game_status = last_game_status;     /* restore current game status */
1834
1835     return;
1836   }
1837
1838   /* scroll micro level, if needed */
1839   if ((lev_fieldx > STD_LEV_FIELDX || lev_fieldy > STD_LEV_FIELDY) &&
1840       DelayReached(&scroll_delay, MICROLEVEL_SCROLL_DELAY))
1841   {
1842     switch (scroll_direction)
1843     {
1844       case MV_LEFT:
1845         if (from_x > 0)
1846           from_x--;
1847         else
1848           scroll_direction = MV_UP;
1849         break;
1850
1851       case MV_RIGHT:
1852         if (from_x < lev_fieldx - STD_LEV_FIELDX)
1853           from_x++;
1854         else
1855           scroll_direction = MV_DOWN;
1856         break;
1857
1858       case MV_UP:
1859         if (from_y > 0)
1860           from_y--;
1861         else
1862           scroll_direction = MV_RIGHT;
1863         break;
1864
1865       case MV_DOWN:
1866         if (from_y < lev_fieldy - STD_LEV_FIELDY)
1867           from_y++;
1868         else
1869           scroll_direction = MV_LEFT;
1870         break;
1871
1872       default:
1873         break;
1874     }
1875
1876     DrawMicroLevelExt(xpos, ypos, from_x, from_y);
1877   }
1878
1879   /* redraw micro level label, if needed */
1880   if (strcmp(level.name, NAMELESS_LEVEL_NAME) != 0 &&
1881       strcmp(level.author, ANONYMOUS_NAME) != 0 &&
1882       strcmp(level.author, leveldir_current->name) != 0 &&
1883       DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
1884   {
1885     int max_label_counter = 23;
1886
1887     if (leveldir_current->imported_from != NULL)
1888       max_label_counter += 14;
1889
1890     label_counter = (label_counter + 1) % max_label_counter;
1891     label_state = (label_counter >= 0 && label_counter <= 7 ?
1892                    MICROLABEL_LEVEL_NAME :
1893                    label_counter >= 9 && label_counter <= 12 ?
1894                    MICROLABEL_CREATED_BY :
1895                    label_counter >= 14 && label_counter <= 21 ?
1896                    MICROLABEL_LEVEL_AUTHOR :
1897                    label_counter >= 23 && label_counter <= 26 ?
1898                    MICROLABEL_IMPORTED_FROM :
1899                    label_counter >= 28 && label_counter <= 35 ?
1900                    MICROLABEL_LEVEL_IMPORT_INFO : MICROLABEL_EMPTY);
1901     DrawMicroLevelLabelExt(label_state);
1902   }
1903
1904   game_status = last_game_status;       /* restore current game status */
1905 }
1906
1907 void WaitForEventToContinue()
1908 {
1909   boolean still_wait = TRUE;
1910
1911   /* simulate releasing mouse button over last gadget, if still pressed */
1912   if (button_status)
1913     HandleGadgets(-1, -1, 0);
1914
1915   button_status = MB_RELEASED;
1916
1917   while (still_wait)
1918   {
1919     if (PendingEvent())
1920     {
1921       Event event;
1922
1923       NextEvent(&event);
1924
1925       switch (event.type)
1926       {
1927         case EVENT_BUTTONPRESS:
1928         case EVENT_KEYPRESS:
1929           still_wait = FALSE;
1930           break;
1931
1932         case EVENT_KEYRELEASE:
1933           ClearPlayerAction();
1934           break;
1935
1936         default:
1937           HandleOtherEvents(&event);
1938           break;
1939       }
1940     }
1941     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
1942     {
1943       still_wait = FALSE;
1944     }
1945
1946     DoAnimation();
1947
1948     /* don't eat all CPU time */
1949     Delay(10);
1950   }
1951 }
1952
1953 #define MAX_REQUEST_LINES               13
1954 #define MAX_REQUEST_LINE_LEN            7
1955
1956 boolean Request(char *text, unsigned int req_state)
1957 {
1958   int mx, my, ty, result = -1;
1959   unsigned int old_door_state;
1960   int last_game_status = game_status;   /* save current game status */
1961
1962 #if 1
1963   SetMouseCursor(CURSOR_DEFAULT);
1964 #endif
1965
1966 #if defined(PLATFORM_UNIX)
1967   /* pause network game while waiting for request to answer */
1968   if (options.network &&
1969       game_status == GAME_MODE_PLAYING &&
1970       req_state & REQUEST_WAIT_FOR)
1971     SendToServer_PausePlaying();
1972 #endif
1973
1974   old_door_state = GetDoorState();
1975
1976   /* simulate releasing mouse button over last gadget, if still pressed */
1977   if (button_status)
1978     HandleGadgets(-1, -1, 0);
1979
1980   UnmapAllGadgets();
1981
1982   CloseDoor(DOOR_CLOSE_1);
1983
1984   /* save old door content */
1985   BlitBitmap(bitmap_db_door, bitmap_db_door,
1986              DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE,
1987              DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY1);
1988
1989   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
1990
1991   /* clear door drawing field */
1992   DrawBackground(DX, DY, DXSIZE, DYSIZE);
1993
1994   /* force DOOR font on preview level */
1995   game_status = GAME_MODE_PSEUDO_DOOR;
1996
1997   /* write text for request */
1998   for(ty=0; ty < MAX_REQUEST_LINES; ty++)
1999   {
2000     char text_line[MAX_REQUEST_LINE_LEN + 1];
2001     int tx, tl, tc;
2002
2003     if (!*text)
2004       break;
2005
2006     for(tl=0,tx=0; tx < MAX_REQUEST_LINE_LEN; tl++,tx++)
2007     {
2008       tc = *(text + tx);
2009       if (!tc || tc == ' ')
2010         break;
2011     }
2012
2013     if (!tl)
2014     { 
2015       text++; 
2016       ty--; 
2017       continue; 
2018     }
2019
2020     strncpy(text_line, text, tl);
2021     text_line[tl] = 0;
2022
2023     DrawText(DX + (DXSIZE - tl * getFontWidth(FONT_TEXT_2)) / 2,
2024              DY + 8 + ty * (getFontHeight(FONT_TEXT_2) + 2),
2025              text_line, FONT_TEXT_2);
2026
2027     text += tl + (tc == ' ' ? 1 : 0);
2028   }
2029
2030   game_status = last_game_status;       /* restore current game status */
2031
2032   if (req_state & REQ_ASK)
2033   {
2034     MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2035     MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2036   }
2037   else if (req_state & REQ_CONFIRM)
2038   {
2039     MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2040   }
2041   else if (req_state & REQ_PLAYER)
2042   {
2043     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2044     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2045     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2046     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2047   }
2048
2049   /* copy request gadgets to door backbuffer */
2050   BlitBitmap(drawto, bitmap_db_door,
2051              DX, DY, DXSIZE, DYSIZE,
2052              DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2053
2054   OpenDoor(DOOR_OPEN_1);
2055
2056 #if 0
2057   ClearEventQueue();
2058 #endif
2059
2060   if (!(req_state & REQUEST_WAIT_FOR))
2061   {
2062     SetDrawBackgroundMask(REDRAW_FIELD);
2063
2064     return FALSE;
2065   }
2066
2067   if (game_status != GAME_MODE_MAIN)
2068     InitAnimation();
2069
2070   button_status = MB_RELEASED;
2071
2072   request_gadget_id = -1;
2073
2074   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
2075
2076 #if 0
2077   SetMouseCursor(CURSOR_DEFAULT);
2078 #endif
2079
2080   while(result < 0)
2081   {
2082     if (PendingEvent())
2083     {
2084       Event event;
2085
2086       NextEvent(&event);
2087
2088       switch(event.type)
2089       {
2090         case EVENT_BUTTONPRESS:
2091         case EVENT_BUTTONRELEASE:
2092         case EVENT_MOTIONNOTIFY:
2093         {
2094           if (event.type == EVENT_MOTIONNOTIFY)
2095           {
2096             if (!PointerInWindow(window))
2097               continue; /* window and pointer are on different screens */
2098
2099             if (!button_status)
2100               continue;
2101
2102             motion_status = TRUE;
2103             mx = ((MotionEvent *) &event)->x;
2104             my = ((MotionEvent *) &event)->y;
2105           }
2106           else
2107           {
2108             motion_status = FALSE;
2109             mx = ((ButtonEvent *) &event)->x;
2110             my = ((ButtonEvent *) &event)->y;
2111             if (event.type == EVENT_BUTTONPRESS)
2112               button_status = ((ButtonEvent *) &event)->button;
2113             else
2114               button_status = MB_RELEASED;
2115           }
2116
2117           /* this sets 'request_gadget_id' */
2118           HandleGadgets(mx, my, button_status);
2119
2120           switch(request_gadget_id)
2121           {
2122             case TOOL_CTRL_ID_YES:
2123               result = TRUE;
2124               break;
2125             case TOOL_CTRL_ID_NO:
2126               result = FALSE;
2127               break;
2128             case TOOL_CTRL_ID_CONFIRM:
2129               result = TRUE | FALSE;
2130               break;
2131
2132             case TOOL_CTRL_ID_PLAYER_1:
2133               result = 1;
2134               break;
2135             case TOOL_CTRL_ID_PLAYER_2:
2136               result = 2;
2137               break;
2138             case TOOL_CTRL_ID_PLAYER_3:
2139               result = 3;
2140               break;
2141             case TOOL_CTRL_ID_PLAYER_4:
2142               result = 4;
2143               break;
2144
2145             default:
2146               break;
2147           }
2148
2149           break;
2150         }
2151
2152         case EVENT_KEYPRESS:
2153           switch(GetEventKey((KeyEvent *)&event, TRUE))
2154           {
2155             case KSYM_Return:
2156               result = 1;
2157               break;
2158
2159             case KSYM_Escape:
2160               result = 0;
2161               break;
2162
2163             default:
2164               break;
2165           }
2166           if (req_state & REQ_PLAYER)
2167             result = 0;
2168           break;
2169
2170         case EVENT_KEYRELEASE:
2171           ClearPlayerAction();
2172           break;
2173
2174         default:
2175           HandleOtherEvents(&event);
2176           break;
2177       }
2178     }
2179     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
2180     {
2181       int joy = AnyJoystick();
2182
2183       if (joy & JOY_BUTTON_1)
2184         result = 1;
2185       else if (joy & JOY_BUTTON_2)
2186         result = 0;
2187     }
2188
2189     DoAnimation();
2190
2191     /* don't eat all CPU time */
2192     Delay(10);
2193   }
2194
2195   if (game_status != GAME_MODE_MAIN)
2196     StopAnimation();
2197
2198   UnmapToolButtons();
2199
2200   if (!(req_state & REQ_STAY_OPEN))
2201   {
2202     CloseDoor(DOOR_CLOSE_1);
2203
2204     if (!(req_state & REQ_STAY_CLOSED) && (old_door_state & DOOR_OPEN_1))
2205     {
2206       BlitBitmap(bitmap_db_door, bitmap_db_door,
2207                  DOOR_GFX_PAGEX2,DOOR_GFX_PAGEY1, DXSIZE,DYSIZE,
2208                  DOOR_GFX_PAGEX1,DOOR_GFX_PAGEY1);
2209       OpenDoor(DOOR_OPEN_1);
2210     }
2211   }
2212
2213   RemapAllGadgets();
2214
2215   SetDrawBackgroundMask(REDRAW_FIELD);
2216
2217 #if defined(PLATFORM_UNIX)
2218   /* continue network game after request */
2219   if (options.network &&
2220       game_status == GAME_MODE_PLAYING &&
2221       req_state & REQUEST_WAIT_FOR)
2222     SendToServer_ContinuePlaying();
2223 #endif
2224
2225   return result;
2226 }
2227
2228 unsigned int OpenDoor(unsigned int door_state)
2229 {
2230   unsigned int new_door_state;
2231
2232   if (door_state & DOOR_COPY_BACK)
2233   {
2234     BlitBitmap(bitmap_db_door, bitmap_db_door,
2235                DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE + VYSIZE,
2236                DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2237     door_state &= ~DOOR_COPY_BACK;
2238   }
2239
2240   new_door_state = MoveDoor(door_state);
2241
2242   return(new_door_state);
2243 }
2244
2245 unsigned int CloseDoor(unsigned int door_state)
2246 {
2247   unsigned int new_door_state;
2248
2249   BlitBitmap(backbuffer, bitmap_db_door,
2250              DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2251   BlitBitmap(backbuffer, bitmap_db_door,
2252              VX, VY, VXSIZE, VYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY2);
2253
2254   new_door_state = MoveDoor(door_state);
2255
2256   return(new_door_state);
2257 }
2258
2259 unsigned int GetDoorState()
2260 {
2261   return MoveDoor(DOOR_GET_STATE);
2262 }
2263
2264 unsigned int SetDoorState(unsigned int door_state)
2265 {
2266   return MoveDoor(door_state | DOOR_SET_STATE);
2267 }
2268
2269 unsigned int MoveDoor(unsigned int door_state)
2270 {
2271   static int door1 = DOOR_OPEN_1;
2272   static int door2 = DOOR_CLOSE_2;
2273   unsigned long door_delay = 0;
2274   unsigned long door_delay_value;
2275   int stepsize = 1;
2276
2277   if (door_state == DOOR_GET_STATE)
2278     return(door1 | door2);
2279
2280   if (door_state & DOOR_SET_STATE)
2281   {
2282     if (door_state & DOOR_ACTION_1)
2283       door1 = door_state & DOOR_ACTION_1;
2284     if (door_state & DOOR_ACTION_2)
2285       door2 = door_state & DOOR_ACTION_2;
2286
2287     return(door1 | door2);
2288   }
2289
2290   if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
2291     door_state &= ~DOOR_OPEN_1;
2292   else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
2293     door_state &= ~DOOR_CLOSE_1;
2294   if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
2295     door_state &= ~DOOR_OPEN_2;
2296   else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
2297     door_state &= ~DOOR_CLOSE_2;
2298
2299   door_delay_value = (door_state & DOOR_ACTION_1 ? door_1.step_delay :
2300                       door_2.step_delay);
2301
2302   if (setup.quick_doors)
2303   {
2304     stepsize = 20;              /* must be choosen to always draw last frame */
2305     door_delay_value = 0;
2306
2307     StopSound(SND_DOOR_OPENING);
2308     StopSound(SND_DOOR_CLOSING);
2309   }
2310
2311   if (global.autoplay_leveldir)
2312   {
2313     door_state |= DOOR_NO_DELAY;
2314     door_state &= ~DOOR_CLOSE_ALL;
2315   }
2316
2317   if (door_state & DOOR_ACTION)
2318   {
2319     boolean door_1_done = !(door_state & DOOR_ACTION_1);
2320     boolean door_2_done = !(door_state & DOOR_ACTION_2);
2321     int start = ((door_state & DOOR_NO_DELAY) ? DXSIZE : 0);
2322     int end = (door_state & DOOR_ACTION_1 &&
2323                door_1.anim_mode == ANIM_VERTICAL ? DYSIZE : DXSIZE);
2324     int x;
2325
2326     if (!(door_state & DOOR_NO_DELAY))
2327     {
2328       /* opening door sound has priority over simultaneously closing door */
2329       if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
2330         PlaySoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
2331       else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
2332         PlaySoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
2333     }
2334
2335     for(x = start; x <= end && !(door_1_done && door_2_done); x += stepsize)
2336     {
2337       Bitmap *bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2338       GC gc = bitmap->stored_clip_gc;
2339
2340       if (door_state & DOOR_ACTION_1)
2341       {
2342         int a = MIN(x * door_1.step_offset, end);
2343         int i = (door_state & DOOR_OPEN_1 ? end - a : a);
2344
2345         if (x <= a)
2346         {
2347           BlitBitmap(bitmap_db_door, drawto,
2348                      DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1 + i / 2,
2349                      DXSIZE,DYSIZE - i / 2, DX, DY);
2350
2351           ClearRectangle(drawto, DX, DY + DYSIZE - i / 2, DXSIZE, i / 2);
2352         }
2353
2354         if (door_1.anim_mode == ANIM_HORIZONTAL && x <= DXSIZE)
2355         {
2356           int src1_x = DXSIZE,          src1_y = DOOR_GFX_PAGEY1;
2357           int dst1_x = DX + DXSIZE - i, dst1_y = DY;
2358           int src2_x = DXSIZE - i,      src2_y = DOOR_GFX_PAGEY1;
2359           int dst2_x = DX,              dst2_y = DY;
2360           int width = i, height = DYSIZE;
2361
2362           SetClipOrigin(bitmap, gc, dst1_x - src1_x, dst1_y - src1_y);
2363           BlitBitmapMasked(bitmap, drawto, src1_x, src1_y, width, height,
2364                            dst1_x, dst1_y);
2365
2366           SetClipOrigin(bitmap, gc, dst2_x - src2_x, dst2_y - src2_y);
2367           BlitBitmapMasked(bitmap, drawto, src2_x, src2_y, width, height,
2368                            dst2_x, dst2_y);
2369         }
2370         else if (door_1.anim_mode == ANIM_VERTICAL && x <= DYSIZE)
2371         {
2372           int src1_x = DXSIZE,          src1_y = DOOR_GFX_PAGEY1;
2373           int dst1_x = DX,              dst1_y = DY + DYSIZE - i;
2374           int src2_x = 0,               src2_y = DOOR_GFX_PAGEY1 + DYSIZE - i;
2375           int dst2_x = DX,              dst2_y = DY;
2376           int width = DXSIZE, height = i;
2377
2378           SetClipOrigin(bitmap, gc, dst1_x - src1_x, dst1_y - src1_y);
2379           BlitBitmapMasked(bitmap, drawto, src1_x, src1_y, width, height,
2380                            dst1_x, dst1_y);
2381
2382           SetClipOrigin(bitmap, gc, dst2_x - src2_x, dst2_y - src2_y);
2383           BlitBitmapMasked(bitmap, drawto, src2_x, src2_y, width, height,
2384                            dst2_x, dst2_y);
2385         }
2386         else if (x <= DXSIZE)   /* ANIM_DEFAULT */
2387         {
2388           int j = (door_1.anim_mode == ANIM_DEFAULT ? (DXSIZE - i) / 3 : 0);
2389
2390           SetClipOrigin(bitmap, gc, DX - i, (DY + j) - DOOR_GFX_PAGEY1);
2391           BlitBitmapMasked(bitmap, drawto,
2392                            DXSIZE, DOOR_GFX_PAGEY1, i, 77,
2393                            DX + DXSIZE - i, DY + j);
2394           BlitBitmapMasked(bitmap, drawto,
2395                            DXSIZE, DOOR_GFX_PAGEY1 + 140, i, 63,
2396                            DX + DXSIZE - i, DY + 140 + j);
2397           SetClipOrigin(bitmap, gc, DX - DXSIZE + i,
2398                         DY - (DOOR_GFX_PAGEY1 + j));
2399           BlitBitmapMasked(bitmap, drawto,
2400                            DXSIZE - i, DOOR_GFX_PAGEY1 + j, i, 77 - j,
2401                            DX, DY);
2402           BlitBitmapMasked(bitmap, drawto,
2403                            DXSIZE-i, DOOR_GFX_PAGEY1 + 140, i, 63,
2404                            DX, DY + 140 - j);
2405
2406           BlitBitmapMasked(bitmap, drawto,
2407                            DXSIZE - i, DOOR_GFX_PAGEY1 + 77, i, 63,
2408                            DX, DY + 77 - j);
2409           BlitBitmapMasked(bitmap, drawto,
2410                            DXSIZE - i, DOOR_GFX_PAGEY1 + 203, i, 77,
2411                            DX, DY + 203 - j);
2412           SetClipOrigin(bitmap, gc, DX - i, (DY + j) - DOOR_GFX_PAGEY1);
2413           BlitBitmapMasked(bitmap, drawto,
2414                            DXSIZE, DOOR_GFX_PAGEY1 + 77, i, 63,
2415                            DX + DXSIZE - i, DY + 77 + j);
2416           BlitBitmapMasked(bitmap, drawto,
2417                            DXSIZE, DOOR_GFX_PAGEY1 + 203, i, 77 - j,
2418                            DX + DXSIZE - i, DY + 203 + j);
2419         }
2420
2421         redraw_mask |= REDRAW_DOOR_1;
2422         door_1_done = (a == end);
2423       }
2424
2425       if (door_state & DOOR_ACTION_2)
2426       {
2427         int a = MIN(x * door_2.step_offset, VXSIZE);
2428         int i = (door_state & DOOR_OPEN_2 ? VXSIZE - a : a);
2429
2430         if (x <= VYSIZE)
2431         {
2432           BlitBitmap(bitmap_db_door, drawto,
2433                      DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY2 + i / 2,
2434                      VXSIZE, VYSIZE - i / 2, VX, VY);
2435
2436           ClearRectangle(drawto, VX, VY + VYSIZE - i / 2, VXSIZE, i / 2);
2437         }
2438
2439         if (door_2.anim_mode == ANIM_HORIZONTAL && x <= VXSIZE)
2440         {
2441           int src1_x = VXSIZE,          src1_y = DOOR_GFX_PAGEY2;
2442           int dst1_x = VX + VXSIZE - i, dst1_y = VY;
2443           int src2_x = VXSIZE - i,      src2_y = DOOR_GFX_PAGEY2;
2444           int dst2_x = VX,              dst2_y = VY;
2445           int width = i, height = VYSIZE;
2446
2447           SetClipOrigin(bitmap, gc, dst1_x - src1_x, dst1_y - src1_y);
2448           BlitBitmapMasked(bitmap, drawto, src1_x, src1_y, width, height,
2449                            dst1_x, dst1_y);
2450
2451           SetClipOrigin(bitmap, gc, dst2_x - src2_x, dst2_y - src2_y);
2452           BlitBitmapMasked(bitmap, drawto, src2_x, src2_y, width, height,
2453                            dst2_x, dst2_y);
2454         }
2455         else if (door_2.anim_mode == ANIM_VERTICAL && x <= VYSIZE)
2456         {
2457           int src1_x = VXSIZE,          src1_y = DOOR_GFX_PAGEY2;
2458           int dst1_x = VX,              dst1_y = VY + VYSIZE - i;
2459           int src2_x = 0,               src2_y = DOOR_GFX_PAGEY2 + VYSIZE - i;
2460           int dst2_x = VX,              dst2_y = VY;
2461           int width = VXSIZE, height = i;
2462
2463           SetClipOrigin(bitmap, gc, dst1_x - src1_x, dst1_y - src1_y);
2464           BlitBitmapMasked(bitmap, drawto, src1_x, src1_y, width, height,
2465                            dst1_x, dst1_y);
2466
2467           SetClipOrigin(bitmap, gc, dst2_x - src2_x, dst2_y - src2_y);
2468           BlitBitmapMasked(bitmap, drawto, src2_x, src2_y, width, height,
2469                            dst2_x, dst2_y);
2470         }
2471         else if (x <= VXSIZE)   /* ANIM_DEFAULT */
2472         {
2473           int j = (door_2.anim_mode == ANIM_DEFAULT ? (VXSIZE - i) / 3 : 0);
2474
2475           SetClipOrigin(bitmap, gc, VX - i, (VY + j) - DOOR_GFX_PAGEY2);
2476           BlitBitmapMasked(bitmap, drawto,
2477                            VXSIZE, DOOR_GFX_PAGEY2, i, VYSIZE / 2,
2478                            VX + VXSIZE - i, VY + j);
2479           SetClipOrigin(bitmap, gc,
2480                         VX - VXSIZE + i, VY - (DOOR_GFX_PAGEY2 + j));
2481           BlitBitmapMasked(bitmap, drawto,
2482                            VXSIZE - i, DOOR_GFX_PAGEY2 + j, i, VYSIZE / 2 - j,
2483                            VX, VY);
2484
2485           BlitBitmapMasked(bitmap, drawto,
2486                            VXSIZE - i, DOOR_GFX_PAGEY2 + VYSIZE / 2,
2487                            i, VYSIZE / 2, VX, VY + VYSIZE / 2 - j);
2488           SetClipOrigin(bitmap, gc, VX - i, (VY + j) - DOOR_GFX_PAGEY2);
2489           BlitBitmapMasked(bitmap, drawto,
2490                            VXSIZE, DOOR_GFX_PAGEY2 + VYSIZE / 2,
2491                            i, VYSIZE / 2 - j,
2492                            VX + VXSIZE - i, VY + VYSIZE / 2 + j);
2493         }
2494
2495         redraw_mask |= REDRAW_DOOR_2;
2496         door_2_done = (a == VXSIZE);
2497       }
2498
2499       BackToFront();
2500
2501       if (game_status == GAME_MODE_MAIN)
2502         DoAnimation();
2503
2504       if (!(door_state & DOOR_NO_DELAY))
2505         WaitUntilDelayReached(&door_delay, door_delay_value);
2506     }
2507   }
2508
2509   if (setup.quick_doors)
2510   {
2511     StopSound(SND_DOOR_OPENING);
2512     StopSound(SND_DOOR_CLOSING);
2513   }
2514
2515   if (door_state & DOOR_ACTION_1)
2516     door1 = door_state & DOOR_ACTION_1;
2517   if (door_state & DOOR_ACTION_2)
2518     door2 = door_state & DOOR_ACTION_2;
2519
2520   return (door1 | door2);
2521 }
2522
2523 void DrawSpecialEditorDoor()
2524 {
2525   /* draw bigger toolbox window */
2526   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2527              DOOR_GFX_PAGEX7, 0, EXSIZE + 8, 8,
2528              EX - 4, EY - 12);
2529   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
2530              EX - 4, VY - 4, EXSIZE + 8, EYSIZE - VYSIZE + 4,
2531              EX - 4, EY - 4);
2532
2533   redraw_mask |= REDRAW_ALL;
2534 }
2535
2536 void UndrawSpecialEditorDoor()
2537 {
2538   /* draw normal tape recorder window */
2539   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
2540              EX - 4, EY - 12, EXSIZE + 8, EYSIZE - VYSIZE + 12,
2541              EX - 4, EY - 12);
2542
2543   redraw_mask |= REDRAW_ALL;
2544 }
2545
2546
2547 /* ---------- new tool button stuff ---------------------------------------- */
2548
2549 /* graphic position values for tool buttons */
2550 #define TOOL_BUTTON_YES_XPOS            2
2551 #define TOOL_BUTTON_YES_YPOS            250
2552 #define TOOL_BUTTON_YES_GFX_YPOS        0
2553 #define TOOL_BUTTON_YES_XSIZE           46
2554 #define TOOL_BUTTON_YES_YSIZE           28
2555 #define TOOL_BUTTON_NO_XPOS             52
2556 #define TOOL_BUTTON_NO_YPOS             TOOL_BUTTON_YES_YPOS
2557 #define TOOL_BUTTON_NO_GFX_YPOS         TOOL_BUTTON_YES_GFX_YPOS
2558 #define TOOL_BUTTON_NO_XSIZE            TOOL_BUTTON_YES_XSIZE
2559 #define TOOL_BUTTON_NO_YSIZE            TOOL_BUTTON_YES_YSIZE
2560 #define TOOL_BUTTON_CONFIRM_XPOS        TOOL_BUTTON_YES_XPOS
2561 #define TOOL_BUTTON_CONFIRM_YPOS        TOOL_BUTTON_YES_YPOS
2562 #define TOOL_BUTTON_CONFIRM_GFX_YPOS    30
2563 #define TOOL_BUTTON_CONFIRM_XSIZE       96
2564 #define TOOL_BUTTON_CONFIRM_YSIZE       TOOL_BUTTON_YES_YSIZE
2565 #define TOOL_BUTTON_PLAYER_XSIZE        30
2566 #define TOOL_BUTTON_PLAYER_YSIZE        30
2567 #define TOOL_BUTTON_PLAYER_GFX_XPOS     5
2568 #define TOOL_BUTTON_PLAYER_GFX_YPOS     185
2569 #define TOOL_BUTTON_PLAYER_XPOS         (5 + TOOL_BUTTON_PLAYER_XSIZE / 2)
2570 #define TOOL_BUTTON_PLAYER_YPOS         (215 - TOOL_BUTTON_PLAYER_YSIZE / 2)
2571 #define TOOL_BUTTON_PLAYER1_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
2572                                          + 0 * TOOL_BUTTON_PLAYER_XSIZE)
2573 #define TOOL_BUTTON_PLAYER2_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
2574                                          + 1 * TOOL_BUTTON_PLAYER_XSIZE)
2575 #define TOOL_BUTTON_PLAYER3_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
2576                                          + 0 * TOOL_BUTTON_PLAYER_XSIZE)
2577 #define TOOL_BUTTON_PLAYER4_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
2578                                          + 1 * TOOL_BUTTON_PLAYER_XSIZE)
2579 #define TOOL_BUTTON_PLAYER1_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
2580                                          + 0 * TOOL_BUTTON_PLAYER_YSIZE)
2581 #define TOOL_BUTTON_PLAYER2_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
2582                                          + 0 * TOOL_BUTTON_PLAYER_YSIZE)
2583 #define TOOL_BUTTON_PLAYER3_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
2584                                          + 1 * TOOL_BUTTON_PLAYER_YSIZE)
2585 #define TOOL_BUTTON_PLAYER4_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
2586                                          + 1 * TOOL_BUTTON_PLAYER_YSIZE)
2587
2588 static struct
2589 {
2590   int xpos, ypos;
2591   int x, y;
2592   int width, height;
2593   int gadget_id;
2594   char *infotext;
2595 } toolbutton_info[NUM_TOOL_BUTTONS] =
2596 {
2597   {
2598     TOOL_BUTTON_YES_XPOS,       TOOL_BUTTON_YES_GFX_YPOS,
2599     TOOL_BUTTON_YES_XPOS,       TOOL_BUTTON_YES_YPOS,
2600     TOOL_BUTTON_YES_XSIZE,      TOOL_BUTTON_YES_YSIZE,
2601     TOOL_CTRL_ID_YES,
2602     "yes"
2603   },
2604   {
2605     TOOL_BUTTON_NO_XPOS,        TOOL_BUTTON_NO_GFX_YPOS,
2606     TOOL_BUTTON_NO_XPOS,        TOOL_BUTTON_NO_YPOS,
2607     TOOL_BUTTON_NO_XSIZE,       TOOL_BUTTON_NO_YSIZE,
2608     TOOL_CTRL_ID_NO,
2609     "no"
2610   },
2611   {
2612     TOOL_BUTTON_CONFIRM_XPOS,   TOOL_BUTTON_CONFIRM_GFX_YPOS,
2613     TOOL_BUTTON_CONFIRM_XPOS,   TOOL_BUTTON_CONFIRM_YPOS,
2614     TOOL_BUTTON_CONFIRM_XSIZE,  TOOL_BUTTON_CONFIRM_YSIZE,
2615     TOOL_CTRL_ID_CONFIRM,
2616     "confirm"
2617   },
2618   {
2619     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
2620     TOOL_BUTTON_PLAYER1_XPOS,   TOOL_BUTTON_PLAYER1_YPOS,
2621     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
2622     TOOL_CTRL_ID_PLAYER_1,
2623     "player 1"
2624   },
2625   {
2626     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
2627     TOOL_BUTTON_PLAYER2_XPOS,   TOOL_BUTTON_PLAYER2_YPOS,
2628     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
2629     TOOL_CTRL_ID_PLAYER_2,
2630     "player 2"
2631   },
2632   {
2633     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
2634     TOOL_BUTTON_PLAYER3_XPOS,   TOOL_BUTTON_PLAYER3_YPOS,
2635     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
2636     TOOL_CTRL_ID_PLAYER_3,
2637     "player 3"
2638   },
2639   {
2640     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
2641     TOOL_BUTTON_PLAYER4_XPOS,   TOOL_BUTTON_PLAYER4_YPOS,
2642     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
2643     TOOL_CTRL_ID_PLAYER_4,
2644     "player 4"
2645   }
2646 };
2647
2648 void CreateToolButtons()
2649 {
2650   int i;
2651
2652   for (i=0; i<NUM_TOOL_BUTTONS; i++)
2653   {
2654     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2655     Bitmap *deco_bitmap = None;
2656     int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
2657     struct GadgetInfo *gi;
2658     unsigned long event_mask;
2659     int gd_xoffset, gd_yoffset;
2660     int gd_x1, gd_x2, gd_y;
2661     int id = i;
2662
2663     event_mask = GD_EVENT_RELEASED;
2664
2665     gd_xoffset = toolbutton_info[i].xpos;
2666     gd_yoffset = toolbutton_info[i].ypos;
2667     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
2668     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
2669     gd_y = DOOR_GFX_PAGEY1 + gd_yoffset;
2670
2671     if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
2672     {
2673       int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
2674
2675       getMiniGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr),
2676                            &deco_bitmap, &deco_x, &deco_y);
2677       deco_xpos = (toolbutton_info[i].width - MINI_TILEX) / 2;
2678       deco_ypos = (toolbutton_info[i].height - MINI_TILEY) / 2;
2679     }
2680
2681     gi = CreateGadget(GDI_CUSTOM_ID, id,
2682                       GDI_INFO_TEXT, toolbutton_info[i].infotext,
2683                       GDI_X, DX + toolbutton_info[i].x,
2684                       GDI_Y, DY + toolbutton_info[i].y,
2685                       GDI_WIDTH, toolbutton_info[i].width,
2686                       GDI_HEIGHT, toolbutton_info[i].height,
2687                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
2688                       GDI_STATE, GD_BUTTON_UNPRESSED,
2689                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y,
2690                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y,
2691                       GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
2692                       GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
2693                       GDI_DECORATION_SIZE, MINI_TILEX, MINI_TILEY,
2694                       GDI_DECORATION_SHIFTING, 1, 1,
2695                       GDI_EVENT_MASK, event_mask,
2696                       GDI_CALLBACK_ACTION, HandleToolButtons,
2697                       GDI_END);
2698
2699     if (gi == NULL)
2700       Error(ERR_EXIT, "cannot create gadget");
2701
2702     tool_gadget[id] = gi;
2703   }
2704 }
2705
2706 void FreeToolButtons()
2707 {
2708   int i;
2709
2710   for (i=0; i<NUM_TOOL_BUTTONS; i++)
2711     FreeGadget(tool_gadget[i]);
2712 }
2713
2714 static void UnmapToolButtons()
2715 {
2716   int i;
2717
2718   for (i=0; i<NUM_TOOL_BUTTONS; i++)
2719     UnmapGadget(tool_gadget[i]);
2720 }
2721
2722 static void HandleToolButtons(struct GadgetInfo *gi)
2723 {
2724   request_gadget_id = gi->custom_id;
2725 }
2726
2727 int get_next_element(int element)
2728 {
2729   switch(element)
2730   {
2731     case EL_QUICKSAND_FILLING:          return EL_QUICKSAND_FULL;
2732     case EL_QUICKSAND_EMPTYING:         return EL_QUICKSAND_EMPTY;
2733     case EL_MAGIC_WALL_FILLING:         return EL_MAGIC_WALL_FULL;
2734     case EL_MAGIC_WALL_EMPTYING:        return EL_MAGIC_WALL_ACTIVE;
2735     case EL_BD_MAGIC_WALL_FILLING:      return EL_BD_MAGIC_WALL_FULL;
2736     case EL_BD_MAGIC_WALL_EMPTYING:     return EL_BD_MAGIC_WALL_ACTIVE;
2737     case EL_AMOEBA_DROPPING:            return EL_AMOEBA_WET;
2738
2739     default:                            return element;
2740   }
2741 }
2742
2743 int el_act_dir2img(int element, int action, int direction)
2744 {
2745   element = GFX_ELEMENT(element);
2746   direction = MV_DIR_BIT(direction);
2747
2748   return element_info[element].direction_graphic[action][direction];
2749 }
2750
2751 static int el_act_dir2crm(int element, int action, int direction)
2752 {
2753   element = GFX_ELEMENT(element);
2754   direction = MV_DIR_BIT(direction);
2755
2756   return element_info[element].direction_crumbled[action][direction];
2757 }
2758
2759 int el_act2img(int element, int action)
2760 {
2761   element = GFX_ELEMENT(element);
2762
2763   return element_info[element].graphic[action];
2764 }
2765
2766 int el_act2crm(int element, int action)
2767 {
2768   element = GFX_ELEMENT(element);
2769
2770   return element_info[element].crumbled[action];
2771 }
2772
2773 int el_dir2img(int element, int direction)
2774 {
2775   element = GFX_ELEMENT(element);
2776
2777   return el_act_dir2img(element, ACTION_DEFAULT, direction);
2778 }
2779
2780 int el2img(int element)
2781 {
2782   element = GFX_ELEMENT(element);
2783
2784   return element_info[element].graphic[ACTION_DEFAULT];
2785 }
2786
2787 int el2edimg(int element)
2788 {
2789   element = GFX_ELEMENT(element);
2790
2791   return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
2792 }
2793
2794 int el2preimg(int element)
2795 {
2796   element = GFX_ELEMENT(element);
2797
2798   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
2799 }