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