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