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