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