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