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