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