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