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