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