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