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