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