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