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