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