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